
-- ============================================================
-- FILE: PlayerFrame.lua
-- PURPOSE: Replaces Blizzard's default player frame with a fully
--   custom SecureUnitButtonTemplate frame. Handles health/power
--   display, absorbs, heal prediction, buff/debuff aura frames,
--   the condition border tint system, and class-specific resource
--   overlays (Soul Shards, Combo Points, DK Runes).
-- HOW IT WORKS:
--   CreatePlayerFrame() builds a SecureUnitButtonTemplate at load.
--   Events drive UpdateHealth/UpdatePower/UpdateAbsorbs. Condition
--   tinting is coordinated via _G.MidnightUI_SetPlayerFrameConditionTint
--   (called by ConditionBorder.lua) — a state machine with weak/strong
--   source priority and delayed application for flicker prevention.
--   During combat, a 50ms C_Timer ticker drives debuff refreshes since
--   UNIT_AURA events are unreliable in restricted state.
--   Aura/debuff limits are applied via WoW CVars. hooksecurefunc on
--   BuffFrame/DebuffFrame Layout prevents Blizzard from overriding.
-- DEPENDS ON:
--   MidnightUI_ConditionBorder — Update(frame) for condition border
--   MidnightUI_MoverRegistry   — frame position management
--   MidnightUI_Core.GetClassColor — class colour
--   MidnightUI_ResolveFontPath — font path resolver
--   MidnightUISettings.PlayerFrame, .General, .Combat
-- EXPORTS:
--   _G.MidnightUI_PlayerFrame               — the frame itself
--   _G.MidnightUI_ApplySharedUnitBarStyle   — shared bar style fn
--   _G.MidnightUI_ApplyPlayerFrameBarStyle  — re-apply bar style
--   _G.MidnightUI_SetPlayerFrameConditionTint(isActive, r, g, b, source)
--   _G.MidnightUI_ApplyPlayerAuraSettings()
--   _G.MidnightUI_ApplyPlayerDebuffSettings()
--   _G.MidnightUI_RefreshPlayerFrame()
--   Class overlays: InitSoulShards, InitComboPoints, InitDKRunes (+Apply/Lock)
-- OWNED SAVEDVARIABLES:
--   MidnightUISettings.PlayerFrame  — frame + aura + debuff settings
--   MidnightUISettings.SoulShards   — warlock soul shard overlay
--   MidnightUISettings.ComboPoints  — rogue/druid combo point overlay
--   MidnightUISettings.DKRunes      — death knight rune overlay
-- EVENTS REGISTERED: ADDON_LOADED, PLAYER_ENTERING_WORLD,
--   UNIT_AURA/HEALTH/MAXHEALTH/POWER_UPDATE/MAXPOWER/DISPLAYPOWER
--   UNIT_ABSORB_AMOUNT_CHANGED, UNIT_HEAL_PREDICTION,
--   PLAYER_SPECIALIZATION_CHANGED, INCOMING_RESURRECT_CHANGED,
--   PLAYER_DEAD, PLAYER_ALIVE, PLAYER_UNGHOST,
--   PLAYER_REGEN_DISABLED, PLAYER_REGEN_ENABLED
-- FRAMES CREATED: MidnightUI_PlayerFrame (SecureUnitButtonTemplate)
-- GOTCHAS:
--   Secret health values during instanced combat: UpdateHealth
--   detects frame._muiCondTintSecret and applies tint anyway via
--   SetVertexColor (which accepts secret ColorMixin values).
--   Condition tint state machine: SetPlayerFrameConditionTintState
--   has "weak" vs "strong" source priority with C_Timer delays to
--   prevent flicker when multiple sources fire simultaneously.
--   Debuff ticker runs ONLY during combat (PLAYER_REGEN_DISABLED/
--   ENABLED) — never call UpdateConditionBorder from the ticker,
--   only call UpdatePlayerDispellableDebuffsOverlay.
-- ============================================================

local ADDON_NAME = "MidnightUI"
local PlayerFrameManager = CreateFrame("Frame")



local POWER_COLORS = {
    ["MANA"] = {0.00, 0.50, 1.00}, ["RAGE"] = {0.90, 0.10, 0.10}, ["FOCUS"] = {1.00, 0.50, 0.25},
    ["ENERGY"] = {1.00, 1.00, 0.35}, ["COMBO_POINTS"] = {1.00, 0.96, 0.41}, ["RUNES"] = {0.50, 0.50, 0.50},
    ["RUNIC_POWER"] = {0.00, 0.82, 1.00}, ["SOUL_SHARDS"] = {0.50, 0.32, 0.55}, ["LUNAR_POWER"] = {0.30, 0.52, 0.90},
    ["HOLY_POWER"] = {0.95, 0.90, 0.60}, ["MAELSTROM"] = {0.00, 0.50, 1.00}, ["CHI"] = {0.71, 1.00, 0.92},
    ["INSANITY"] = {0.40, 0.00, 0.80}, ["ARCANE_CHARGES"] = {0.10, 0.10, 0.98}, ["FURY"] = {0.79, 0.26, 0.99},
    ["PAIN"] = {1.00, 0.61, 0.00}, ["ESSENCE"] = {0.20, 0.88, 0.66},
}
local DEFAULT_UNIT_COLOR_R, DEFAULT_UNIT_COLOR_G, DEFAULT_UNIT_COLOR_B = 0.5, 0.5, 0.5
local DEFAULT_POWER_COLOR = {0.0, 0.5, 1.0}
local DEAD_STATUS_ATLAS = "icons_64x64_deadly"

local function EnsureDeadIconTexture(icon)
    if not icon then return end
    if C_Texture and C_Texture.GetAtlasInfo and C_Texture.GetAtlasInfo(DEAD_STATUS_ATLAS) then
        icon:SetAtlas(DEAD_STATUS_ATLAS, false)
    else
        icon:SetTexture("Interface\\TargetingFrame\\UI-TargetingFrame-Skull")
    end
end

local function ApplyDeadIconVisualStyle(icon)
    if not icon then return end
    EnsureDeadIconTexture(icon)
    if icon.SetDesaturated then icon:SetDesaturated(false) end
    icon:SetVertexColor(1, 0.2, 0.2, 1)
    icon:SetAlpha(1)
end

local config = {
    width = 380, height = 66, healthHeight = 42, powerHeight = 16, spacing = 1,
    position = {"BOTTOM", "UIParent", "BOTTOM", -280.00, 240.67},
    auras = {
        enabled = true,
        scale = 100,
        alpha = 1.0,
        position = {"CENTER", "CENTER", -369.95, -255.06},
        alignment = "Right",
        maxShown = 32,
        perRow = 16,
    },
    debuffs = {
        enabled = true,
        scale = 80,
        alpha = 1.0,
        position = {"BOTTOM", "BOTTOM", -449.51, 193.03},
        alignment = "Right",
        maxShown = 16,
        perRow = 8,
    },
}

-- ============================================================
-- SECTION: Module Init + Power Colors + Dead Icon + Config
-- POWER_COLORS: RGB table keyed by power type string (MANA, RAGE,
--   etc.). Used by UpdatePower to color the power bar.
-- DEFAULT_UNIT_COLOR_*: fallback health bar tint when class color
--   is unavailable.
-- DEAD_STATUS_ATLAS: skull icon atlas for the death indicator.
-- EnsureDeadIconTexture/ApplyDeadIconVisualStyle: set the death
--   icon texture (atlas or fallback) and apply red vertex tint.
-- config: compile-time default dimensions and aura settings,
--   overridden at runtime by MidnightUISettings.PlayerFrame.
-- READS:  C_Texture.GetAtlasInfo (atlas availability check)
-- WRITES: (pure declarations + config table)
-- FUNCTIONS:
--   EnsureDeadIconTexture(icon), ApplyDeadIconVisualStyle(icon)
-- ============================================================
local function GetPlayerAuraOverlaySize(auraKey)
    local s = MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame[auraKey]
    local maxLimit = auraKey == "auras" and 32 or 16
    local maxShown = (s and s.maxShown) or config[auraKey].maxShown or maxLimit
    local perRow = (s and s.perRow) or config[auraKey].perRow or 16
    if maxShown > maxLimit then maxShown = maxLimit end
    if perRow < 1 then perRow = 1 end
    local cols = math.min(perRow, maxShown)
    local rows = math.ceil(maxShown / perRow)
    local w = (cols * 35) - 5 + 4
    local h = (rows * 45) - 5 + 4
    return math.max(w, 40), math.max(h, 20)
end

local function InitializeAuraSettings()
    if not MidnightUISettings then
        MidnightUISettings = {}
    end
    if not MidnightUISettings.PlayerFrame then
        MidnightUISettings.PlayerFrame = {}
    end
    
    if not MidnightUISettings.PlayerFrame.auras then
        MidnightUISettings.PlayerFrame.auras = {
            enabled = true,
            scale = 100,
            alpha = 1.0,
            position = {"CENTER", "CENTER", -369.95, -255.06},
            alignment = "Right",
            maxShown = 32,
        }
    end
    if MidnightUISettings.PlayerFrame.auras.enabled == nil then
        MidnightUISettings.PlayerFrame.auras.enabled = true
    end
    if not MidnightUISettings.PlayerFrame.auras.scale then
        MidnightUISettings.PlayerFrame.auras.scale = 100
    end
    if not MidnightUISettings.PlayerFrame.auras.alpha then
        MidnightUISettings.PlayerFrame.auras.alpha = 1.0
    end
    if not MidnightUISettings.PlayerFrame.auras.alignment then
        MidnightUISettings.PlayerFrame.auras.alignment = "Right"
    end
    if MidnightUISettings.PlayerFrame.auras.maxShown == nil then
        MidnightUISettings.PlayerFrame.auras.maxShown = 32
    end
    
    if not MidnightUISettings.PlayerFrame.debuffs then
        MidnightUISettings.PlayerFrame.debuffs = {
            enabled = true,
            scale = 80,
            alpha = 1.0,
            position = {"BOTTOM", "BOTTOM", -449.51, 193.03},
            alignment = "Right",
            maxShown = 16,
            perRow = 8,
        }
    end
    if MidnightUISettings.PlayerFrame.debuffs.enabled == nil then
        MidnightUISettings.PlayerFrame.debuffs.enabled = true
    end
    if not MidnightUISettings.PlayerFrame.debuffs.scale then
        MidnightUISettings.PlayerFrame.debuffs.scale = 100
    end
    if not MidnightUISettings.PlayerFrame.debuffs.alpha then
        MidnightUISettings.PlayerFrame.debuffs.alpha = 1.0
    end
    if not MidnightUISettings.PlayerFrame.debuffs.alignment then
        MidnightUISettings.PlayerFrame.debuffs.alignment = "Right"
    end
    if MidnightUISettings.PlayerFrame.debuffs.maxShown == nil then
        MidnightUISettings.PlayerFrame.debuffs.maxShown = 16
    end
end



local function GetUnitColor(unit)
    if UnitIsPlayer(unit) then
        if _G.MidnightUI_Core and _G.MidnightUI_Core.GetClassColor then
            local r, g, b = _G.MidnightUI_Core.GetClassColor(unit)
            if type(r) == "number" and type(g) == "number" and type(b) == "number" then
                return r, g, b
            end
        end
        local _, class = UnitClass(unit)
        if class and RAID_CLASS_COLORS and RAID_CLASS_COLORS[class] then
            local c = RAID_CLASS_COLORS[class]
            if type(c.r) == "number" and type(c.g) == "number" and type(c.b) == "number" then
                return c.r, c.g, c.b
            end
        end
        return DEFAULT_UNIT_COLOR_R, DEFAULT_UNIT_COLOR_G, DEFAULT_UNIT_COLOR_B
    end
    return DEFAULT_UNIT_COLOR_R, DEFAULT_UNIT_COLOR_G, DEFAULT_UNIT_COLOR_B
end

local function CreateDropShadow(frame, intensity)
    intensity = intensity or 6
    local shadows = {}
    for i = 1, intensity do
        local shadowLayer = CreateFrame("Frame", nil, frame)
        shadowLayer:SetFrameLevel(math.max(0, frame:GetFrameLevel() - 1))
        local offset = i * 0.8
        local alpha = (0.18 - (i * 0.025)) * (intensity / 6)
        shadowLayer:SetPoint("TOPLEFT", frame, "TOPLEFT", -offset, offset)
        shadowLayer:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", offset, -offset)
        local shadowTex = shadowLayer:CreateTexture(nil, "BACKGROUND")
        shadowTex:SetAllPoints()
        shadowTex:SetColorTexture(0, 0, 0, alpha)
        table.insert(shadows, shadowLayer)
    end
    return shadows
end

local function CreateBlackBorder(parent, thickness, alpha)
    thickness = thickness or 1
    alpha = alpha or 1
    local border = CreateFrame("Frame", nil, parent)
    border:SetAllPoints()
    border.top = border:CreateTexture(nil, "OVERLAY"); border.top:SetHeight(thickness); border.top:SetPoint("TOPLEFT"); border.top:SetPoint("TOPRIGHT"); border.top:SetColorTexture(0,0,0,alpha)
    border.bottom = border:CreateTexture(nil, "OVERLAY"); border.bottom:SetHeight(thickness); border.bottom:SetPoint("BOTTOMLEFT"); border.bottom:SetPoint("BOTTOMRIGHT"); border.bottom:SetColorTexture(0,0,0,alpha)
    border.left = border:CreateTexture(nil, "OVERLAY"); border.left:SetWidth(thickness); border.left:SetPoint("TOPLEFT"); border.left:SetPoint("BOTTOMLEFT"); border.left:SetColorTexture(0,0,0,alpha)
    border.right = border:CreateTexture(nil, "OVERLAY"); border.right:SetWidth(thickness); border.right:SetPoint("TOPRIGHT"); border.right:SetPoint("BOTTOMRIGHT"); border.right:SetColorTexture(0,0,0,alpha)
    border.innerHighlight = border:CreateTexture(nil, "OVERLAY", nil, 2)
    border.innerHighlight:SetHeight(3)
    border.innerHighlight:SetPoint("TOPLEFT", border.top, "BOTTOMLEFT", thickness, 0)
    border.innerHighlight:SetPoint("TOPRIGHT", border.top, "BOTTOMRIGHT", -thickness, 0)
    border.innerHighlight:SetTexture("Interface\\Buttons\\WHITE8X8")
    border.innerHighlight:SetGradient("VERTICAL",
        CreateColor(1, 1, 1, 0),
        CreateColor(1, 1, 1, 0.07))
    border.innerShadow = border:CreateTexture(nil, "OVERLAY", nil, 2)
    border.innerShadow:SetHeight(3)
    border.innerShadow:SetPoint("BOTTOMLEFT", border.bottom, "TOPLEFT", thickness, 0)
    border.innerShadow:SetPoint("BOTTOMRIGHT", border.bottom, "TOPRIGHT", -thickness, 0)
    border.innerShadow:SetTexture("Interface\\Buttons\\WHITE8X8")
    border.innerShadow:SetGradient("VERTICAL",
        CreateColor(0, 0, 0, 0.25),
        CreateColor(0, 0, 0, 0))
    return border
end

local function SetBorderColor(border, r, g, b, a)
    if not border then return end
    local alpha = a or 1
    if border.top then border.top:SetColorTexture(r, g, b, alpha) end
    if border.bottom then border.bottom:SetColorTexture(r, g, b, alpha) end
    if border.left then border.left:SetColorTexture(r, g, b, alpha) end
    if border.right then border.right:SetColorTexture(r, g, b, alpha) end
end

local function SetFontSafe(fs, fontPath, size, flags)
    local resolved = (_G.MidnightUI_ResolveFontPath and _G.MidnightUI_ResolveFontPath(fontPath)) or fontPath
    local ok = fs:SetFont(resolved, size, flags)
    if not ok then
        local fallback = (_G.MidnightUI_ResolveFontPath and _G.MidnightUI_ResolveFontPath()) or (GameFontNormal and GameFontNormal:GetFont())
        if fallback then
            fs:SetFont(fallback, size or 12, flags)
        end
    end
    return ok
end

-- ============================================================
-- SECTION: Settings Init + Unit Color + Frame Construction Helpers
-- GetPlayerAuraOverlaySize: returns (maxShown, perRow) for "auras"
--   or "debuffs" key, clamped to safe limits.
-- InitializeAuraSettings: lazily creates MidnightUISettings.PlayerFrame
--   and all sub-tables with defaults. Called at the top of event
--   handlers that need settings to exist.
-- GetUnitColor: delegates to MidnightUI_Core.GetClassColor(unit)
--   with a grey fallback. Returns r, g, b.
-- CreateDropShadow/CreateBlackBorder/SetBorderColor/SetFontSafe:
--   low-level frame construction helpers used by CreatePlayerFrame.
-- READS:  MidnightUISettings.PlayerFrame, MidnightUI_Core,
--         MidnightUI_ResolveFontPath
-- WRITES: MidnightUISettings.PlayerFrame (lazy init)
-- FUNCTIONS:
--   GetPlayerAuraOverlaySize(auraKey) → maxShown, perRow
--   InitializeAuraSettings()          — lazy settings init
--   GetUnitColor(unit)                → r, g, b
--   CreateDropShadow(parent)          → texture
--   CreateBlackBorder(parent)         → border frame
--   SetBorderColor(border, r, g, b, a)
--   SetFontSafe(fs, path, size, flags)
-- ============================================================
local function ApplyHealthBarStyle(healthBar)
    if not healthBar then return end
    local function ApplyPolishedGradient(tex, topDarkA, centerLightA, bottomDarkA)
        local anchor = tex or healthBar
        local baseR, baseG, baseB = healthBar:GetStatusBarColor()
        local baseIsSecret = false
        if type(issecretvalue) == "function" then
            local okR, secR = pcall(issecretvalue, baseR)
            local okG, secG = pcall(issecretvalue, baseG)
            local okB, secB = pcall(issecretvalue, baseB)
            baseIsSecret = (okR and secR == true) or (okG and secG == true) or (okB and secB == true)
        end
        if type(baseR) ~= "number" or baseIsSecret then
            local fr = tonumber(healthBar._muiStyleFallbackR)
            local fg = tonumber(healthBar._muiStyleFallbackG)
            local fb = tonumber(healthBar._muiStyleFallbackB)
            if fr and fg and fb then
                if fr < 0 then fr = 0 elseif fr > 1 then fr = 1 end
                if fg < 0 then fg = 0 elseif fg > 1 then fg = 1 end
                if fb < 0 then fb = 0 elseif fb > 1 then fb = 1 end
                baseR, baseG, baseB = fr, fg, fb
            else
                baseR, baseG, baseB = 0.5, 0.5, 0.5
            end
        end
        local function Darken(v, f)
            local n = (v or 0) * f
            if n < 0 then return 0 end
            if n > 1 then return 1 end
            return n
        end
        local function Lift(v, a)
            local b = v or 0
            local n = b + (1 - b) * (a or 0.035)
            if n < 0 then return 0 end
            if n > 1 then return 1 end
            return n
        end
        local edgeR, edgeG, edgeB = Darken(baseR, 0.74), Darken(baseG, 0.74), Darken(baseB, 0.74)
        local midR, midG, midB = Lift(baseR, 0.10), Lift(baseG, 0.10), Lift(baseB, 0.10)
        if not healthBar._muiTopHighlight then
            healthBar._muiTopHighlight = healthBar:CreateTexture(nil, "ARTWORK", nil, 2)
        end
        healthBar._muiTopHighlight:SetTexture("Interface\\Buttons\\WHITE8X8")
        healthBar._muiTopHighlight:SetBlendMode("DISABLE")
        if not healthBar._muiBottomShade then
            healthBar._muiBottomShade = healthBar:CreateTexture(nil, "ARTWORK", nil, 1)
        end
        healthBar._muiBottomShade:SetTexture("Interface\\Buttons\\WHITE8X8")
        healthBar._muiBottomShade:SetBlendMode("DISABLE")
        if not healthBar._muiSpecular then
            healthBar._muiSpecular = healthBar:CreateTexture(nil, "ARTWORK", nil, 3)
        end
        healthBar._muiSpecular:SetTexture("Interface\\Buttons\\WHITE8X8")
        healthBar._muiSpecular:SetBlendMode("ADD")
        local rawH = (healthBar.GetHeight and healthBar:GetHeight()) or 2
        local h = tonumber(tostring(rawH)) or 2
        local topH = math.max(1, h * 0.45)
        local botH = math.max(1, h * 0.58)
        local specH = math.max(1, h * 0.35)
        healthBar._muiTopHighlight:ClearAllPoints()
        healthBar._muiTopHighlight:SetPoint("TOPLEFT", anchor, "TOPLEFT", 0, 0)
        healthBar._muiTopHighlight:SetPoint("TOPRIGHT", anchor, "TOPRIGHT", 0, 0)
        healthBar._muiTopHighlight:SetHeight(topH)
        healthBar._muiTopHighlight:SetGradient("VERTICAL",
            CreateColor(midR, midG, midB, 1),
            CreateColor(edgeR, edgeG, edgeB, 1))

        healthBar._muiBottomShade:ClearAllPoints()
        healthBar._muiBottomShade:SetPoint("BOTTOMLEFT", anchor, "BOTTOMLEFT", 0, 0)
        healthBar._muiBottomShade:SetPoint("BOTTOMRIGHT", anchor, "BOTTOMRIGHT", 0, 0)
        healthBar._muiBottomShade:SetHeight(botH)
        healthBar._muiBottomShade:SetGradient("VERTICAL",
            CreateColor(edgeR, edgeG, edgeB, 1),
            CreateColor(midR, midG, midB, 1))

        healthBar._muiSpecular:ClearAllPoints()
        healthBar._muiSpecular:SetPoint("TOPLEFT", anchor, "TOPLEFT", 0, 0)
        healthBar._muiSpecular:SetPoint("TOPRIGHT", anchor, "TOPRIGHT", 0, 0)
        healthBar._muiSpecular:SetHeight(specH)
        healthBar._muiSpecular:SetGradient("VERTICAL",
            CreateColor(1, 1, 1, 0),
            CreateColor(1, 1, 1, 0.06))
        healthBar._muiTopHighlight:Show()
        healthBar._muiBottomShade:Show()
        healthBar._muiSpecular:Show()
    end

    local style = (MidnightUISettings and MidnightUISettings.General and MidnightUISettings.General.unitFrameBarStyle) or "Gradient"
    if style == "Balanced" then style = "Gradient" end
    if style == "Gradient" then
        healthBar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8")
        local tex = healthBar:GetStatusBarTexture()
        if tex then
            tex:SetHorizTile(false)
            tex:SetVertTile(false)
        end
        ApplyPolishedGradient(tex, 0.28, 0.035, 0.32)
        if healthBar._muiFlatGradient then healthBar._muiFlatGradient:Hide() end
        return
    end
    healthBar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8")
    local tex = healthBar:GetStatusBarTexture()
    if tex then
        tex:SetHorizTile(false)
        tex:SetVertTile(false)
    end
    if not healthBar._muiFlatGradient then
        healthBar._muiFlatGradient = healthBar:CreateTexture(nil, "ARTWORK")
        healthBar._muiFlatGradient:SetAllPoints(tex or healthBar)
        healthBar._muiFlatGradient:SetBlendMode("DISABLE")
    end
    healthBar._muiFlatGradient:SetGradient("VERTICAL", CreateColor(1, 1, 1, 0.06), CreateColor(0, 0, 0, 0.12))
    healthBar._muiFlatGradient:Show()
    if healthBar._muiTopHighlight then healthBar._muiTopHighlight:Hide() end
    if healthBar._muiBottomShade then healthBar._muiBottomShade:Hide() end
    if healthBar._muiSpecular then healthBar._muiSpecular:Hide() end
end

_G.MidnightUI_ApplySharedUnitBarStyle = ApplyHealthBarStyle

local function ApplyFrameTextStyle(frame)
    if not frame or not frame.nameText then return end

    local sharedText = _G.MidnightUI_ApplySharedUnitTextStyle
    if type(sharedText) == "function" then
        sharedText(frame, {
            nameFont = "Fonts\\FRIZQT__.TTF",
            nameSize = 14,
            healthFont = "Fonts\\ARIALN.TTF",
            healthSize = 14,
            levelFont = "Fonts\\FRIZQT__.TTF",
            levelSize = 11,
            powerFont = "Fonts\\FRIZQT__.TTF",
            powerSize = 11,
            nameShadowAlpha = 1,
            healthShadowAlpha = 1,
            levelShadowAlpha = 1,
            powerShadowAlpha = 1,
        })
        return
    end

    SetFontSafe(frame.nameText, "Fonts\\FRIZQT__.TTF", 14, "OUTLINE")
    SetFontSafe(frame.healthText, "Fonts\\ARIALN.TTF", 14, "OUTLINE")
    SetFontSafe(frame.levelText, "Fonts\\FRIZQT__.TTF", 11, "OUTLINE")
    SetFontSafe(frame.powerText, "Fonts\\FRIZQT__.TTF", 11, "OUTLINE")
    frame.nameText:SetShadowOffset(1, -1); frame.nameText:SetShadowColor(0, 0, 0, 1)
    frame.healthText:SetShadowOffset(2, -2); frame.healthText:SetShadowColor(0, 0, 0, 1)
    frame.levelText:SetShadowOffset(1, -1); frame.levelText:SetShadowColor(0, 0, 0, 1)
    frame.powerText:SetShadowOffset(1, -1); frame.powerText:SetShadowColor(0, 0, 0, 0.9)
end

local function ApplyFrameLayout(frame)
    if not frame or not frame.healthContainer or not frame.powerContainer then return end
    frame.healthContainer:SetHeight(config.healthHeight)
    frame.powerContainer:ClearAllPoints()
    frame.powerContainer:SetPoint("TOPLEFT", frame.healthContainer, "BOTTOMLEFT", 0, -config.spacing)
    frame.powerContainer:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -1, 1)
    if frame.powerSep then frame.powerSep:Show() end
    if frame.healthBg then frame.healthBg:SetColorTexture(0.1, 0.1, 0.1, 1) end
    if frame.powerBg then frame.powerBg:SetColorTexture(0, 0, 0, 0.5) end
end


-- ============================================================
-- SECTION: Health Bar Style + Text Style + Layout
-- ApplyHealthBarStyle: applies the "Polished Gradient" or "Flat"
--   visual treatment to a StatusBar. Creates/updates three texture
--   layers (_muiTopHighlight, _muiBottomShade, _muiSpecular) using
--   the blend mode system. Reads MidnightUISettings.General.unitFrameBarStyle.
--   Exported as _G.MidnightUI_ApplySharedUnitBarStyle (also used by
--   TargetFrame, FocusFrame, etc.).
-- ApplyPlayerFrameBarStyle: wrapper that calls ApplyHealthBarStyle
--   on frame.healthBar and frame.powerBar, then refreshes text/layout.
--   Exported as _G.MidnightUI_ApplyPlayerFrameBarStyle.
-- ApplyFrameTextStyle/ApplyFrameLayout: delegates to shared module
--   functions for consistent styling across all unit frames.
-- READS:  MidnightUISettings.General.unitFrameBarStyle, frame bars
-- WRITES: StatusBar texture layers
-- FUNCTIONS:
--   ApplyHealthBarStyle(healthBar)
--   _G.MidnightUI_ApplySharedUnitBarStyle — alias
--   ApplyPlayerFrameBarStyle()
--   _G.MidnightUI_ApplyPlayerFrameBarStyle — alias
--   ApplyFrameTextStyle(frame), ApplyFrameLayout(frame)
-- ============================================================
-- ============================================================
-- SECTION: CreatePlayerFrame — Frame Constructor
-- Builds the entire player frame hierarchy:
--   1. SecureUnitButtonTemplate "MidnightUI_PlayerFrame" — unit="player",
--      type1=target, type2=togglemenu. RegisterUnitWatch called.
--   2. healthBar (StatusBar, ARIALN font health text, percentage text)
--   3. powerBar (StatusBar below health)
--   4. Level text, name text
--   5. Dead icon (skull texture overlay)
--   6. Absorb bar and heal prediction bar
--   7. Drop shadow + black border
--   8. Debuff border (condition glow overlay, frame level +30)
--   9. Debuff outline (+31)
--  10. Condition border: calls MidnightUI_ConditionBorder.BuildCondBorder
--      and M.Init(frame).
--  11. MoverRegistry registration for "PlayerFrame" key.
-- Returns the frame; stored as _G.MidnightUI_PlayerFrame.
-- READS:  config, MidnightUISettings.PlayerFrame
-- WRITES: _G.MidnightUI_PlayerFrame
-- FUNCTIONS:
--   CreatePlayerFrame() → frame
-- ============================================================
local function CreatePlayerFrame()
    local frame = CreateFrame("Button", "MidnightUI_PlayerFrame", UIParent, "SecureUnitButtonTemplate, BackdropTemplate")
    frame:SetSize(config.width, config.height)
    frame:SetAttribute("unit", "player")
    frame:SetAttribute("type1", "target")
    frame:SetAttribute("*type2", "togglemenu")
    frame:RegisterForClicks("AnyUp")
    RegisterUnitWatch(frame)

    local savedScale = 1.0
    if MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.scale then
        savedScale = MidnightUISettings.PlayerFrame.scale / 100
    end
    frame:SetScale(savedScale)

    frame:SetPoint(unpack(config.position))

    frame:SetFrameStrata("MEDIUM")
    frame:SetFrameLevel(10)
    frame:SetMovable(true)
    frame:SetClampedToScreen(true)
    frame:EnableMouse(true)
    frame:SetAlpha(0.95)

    frame:SetScript("OnEnter", function(self)
        if frame.dragOverlay and frame.dragOverlay:IsShown() then return end
        GameTooltip:SetOwner(self, "ANCHOR_BOTTOMLEFT")
        GameTooltip:SetUnit("player")
        GameTooltip:Show()
    end)
    frame:SetScript("OnLeave", function(self) GameTooltip:Hide() end)

    frame:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
        insets = { left = 0, right = 0, top = 0, bottom = 0 }
    })
    frame:SetBackdropColor(0.1, 0.1, 0.1, 0.9)
    frame:SetBackdropBorderColor(0, 0, 0, 1)

    frame.shadows = CreateDropShadow(frame, 3)

    local debuffBorder = CreateBlackBorder(frame, 1, 1)
    debuffBorder:SetFrameLevel(frame:GetFrameLevel() + 30)
    debuffBorder:Hide()
    frame.debuffBorder = debuffBorder

    local debuffOutline = CreateFrame("Frame", nil, frame, "BackdropTemplate")
    debuffOutline:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0)
    debuffOutline:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0)
    debuffOutline:SetFrameLevel(frame:GetFrameLevel() + 31)
    debuffOutline:SetBackdrop({
        edgeFile = "Interface\\Buttons\\WHITE8x8",
        edgeSize = 2,
        insets = { left = 0, right = 0, top = 0, bottom = 0 },
    })
    debuffOutline:SetBackdropBorderColor(0, 0, 0, 1)
    debuffOutline:Hide()
    frame.debuffOutline = debuffOutline

    local CB = _G.MidnightUI_ConditionBorder
    if CB and CB.BuildCondBorder then
        frame.conditionBorder = CB.BuildCondBorder(frame)
        CB.Init(frame)
    end

    local healthContainer = CreateFrame("Frame", nil, frame)
    healthContainer:SetPoint("TOPLEFT", frame, "TOPLEFT", 1, -1)
    healthContainer:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -1, -1)
    healthContainer:SetHeight(config.healthHeight)
    frame.healthContainer = healthContainer

    local healthBg = healthContainer:CreateTexture(nil, "BACKGROUND")
    healthBg:SetAllPoints()
    healthBg:SetColorTexture(0.1, 0.1, 0.1, 1)
    frame.healthBg = healthBg
    frame.healthBg = healthBg

    local healthBar = CreateFrame("StatusBar", nil, healthContainer)
    healthBar:SetPoint("TOPLEFT", 0, 0)
    healthBar:SetPoint("BOTTOMRIGHT", 0, 0)
    ApplyHealthBarStyle(healthBar)
    healthBar:SetStatusBarColor(0.5, 0.5, 0.5, 1.0)
    healthBar:SetMinMaxValues(0, 1); healthBar:SetValue(1)
    frame.healthBar = healthBar

    local incomingHeal = CreateFrame("StatusBar", nil, healthContainer)
    incomingHeal:SetPoint("TOPLEFT", healthBar:GetStatusBarTexture(), "TOPRIGHT", 0, 0)
    incomingHeal:SetPoint("BOTTOMRIGHT", healthContainer, "BOTTOMRIGHT", 0, 0)
    incomingHeal:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8")
    incomingHeal:GetStatusBarTexture():SetHorizTile(false)
    incomingHeal:SetStatusBarColor(0.4, 0.9, 0.4, 0.7)
    incomingHeal:SetFrameLevel(healthBar:GetFrameLevel() + 2)
    incomingHeal:SetMinMaxValues(0, 1); incomingHeal:SetValue(0)
    incomingHeal:SetAlpha(0)
    frame.incomingHeal = incomingHeal

    local absorbBar = CreateFrame("StatusBar", nil, healthContainer)
    absorbBar:SetPoint("TOPLEFT", healthBar:GetStatusBarTexture(), "TOPRIGHT", 0, 0)
    absorbBar:SetPoint("BOTTOMRIGHT", healthContainer, "BOTTOMRIGHT", 0, 0)
    absorbBar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8")
    absorbBar:GetStatusBarTexture():SetHorizTile(false)
    absorbBar:SetStatusBarColor(0.3, 0.5, 0.9, 0.7)
    absorbBar:SetFrameLevel(healthBar:GetFrameLevel() + 2)
    absorbBar:SetMinMaxValues(0, 1); absorbBar:SetValue(0)
    absorbBar:SetAlpha(0)
    frame.absorbBar = absorbBar

    if CreateUnitHealPredictionCalculator then
        frame._healCalc = CreateUnitHealPredictionCalculator()
        frame._healCalc:SetIncomingHealClampMode(Enum.UnitIncomingHealClampMode.MissingHealth)
        frame._healCalc:SetIncomingHealOverflowPercent(1)
        frame._healCalc:SetDamageAbsorbClampMode(Enum.UnitDamageAbsorbClampMode.MaximumHealth)
        frame._healCalc:SetHealAbsorbClampMode(Enum.UnitHealAbsorbClampMode.MaximumHealth)
        frame._healCalc:SetHealAbsorbMode(Enum.UnitHealAbsorbMode.Total)
    end

    local textOverlay = CreateFrame("Frame", nil, healthContainer)
    textOverlay:SetAllPoints()
    textOverlay:SetFrameStrata("MEDIUM")
    textOverlay:SetFrameLevel(healthContainer:GetFrameLevel() + 5)

    local nameText = textOverlay:CreateFontString(nil, "OVERLAY")
    SetFontSafe(nameText, "Fonts\\FRIZQT__.TTF", 14, "OUTLINE")
    nameText:SetPoint("CENTER", textOverlay, "CENTER", 0, 2)
    nameText:SetTextColor(1, 1, 1, 1); nameText:SetShadowOffset(1, -1); nameText:SetShadowColor(0, 0, 0, 1)
    frame.nameText = nameText

    local healthText = textOverlay:CreateFontString(nil, "OVERLAY")
    SetFontSafe(healthText, "Fonts\\ARIALN.TTF", 14, "OUTLINE")
    healthText:SetPoint("RIGHT", textOverlay, "RIGHT", -10, 0)
    healthText:SetTextColor(1, 1, 1, 1); healthText:SetShadowOffset(2, -2); healthText:SetShadowColor(0, 0, 0, 1)
    frame.healthText = healthText

    local levelText = textOverlay:CreateFontString(nil, "OVERLAY")
    SetFontSafe(levelText, "Fonts\\FRIZQT__.TTF", 11, "OUTLINE")
    levelText:SetPoint("LEFT", textOverlay, "LEFT", 10, 0)
    levelText:SetTextColor(0.9, 0.9, 0.9, 1)
    frame.levelText = levelText

    local resIcon = textOverlay:CreateTexture(nil, "OVERLAY")
    resIcon:SetSize(28, 28)
    resIcon:SetPoint("CENTER", healthContainer, "CENTER", 0, 0)
    resIcon:SetTexture("Interface\\RaidFrame\\Raid-Icon-Rez")
    resIcon:SetVertexColor(1, 1, 1, 1)
    resIcon:Hide()
    frame.resIcon = resIcon

    local deadIcon = textOverlay:CreateTexture(nil, "OVERLAY")
    deadIcon:SetSize(32, 32)
    deadIcon:SetPoint("RIGHT", healthContainer, "RIGHT", -28, 0)
    EnsureDeadIconTexture(deadIcon)
    deadIcon:SetVertexColor(1, 1, 1, 1)
    deadIcon:SetDrawLayer("OVERLAY", 7)
    deadIcon:SetAlpha(1)
    deadIcon:Hide()
    frame.deadIcon = deadIcon
    
    local resGlow = textOverlay:CreateTexture(nil, "ARTWORK")
    resGlow:SetSize(50, 50)
    resGlow:SetPoint("CENTER", resIcon, "CENTER")
    resGlow:SetTexture("Interface\\Buttons\\WHITE8X8")
    resGlow:SetVertexColor(1, 0.85, 0.3, 0)
    resGlow:SetBlendMode("ADD")
    resGlow:Hide()
    frame.resGlow = resGlow

    local powerContainer = CreateFrame("Frame", nil, frame)
    powerContainer:SetPoint("TOPLEFT", healthContainer, "BOTTOMLEFT", 0, -config.spacing)
    powerContainer:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -1, 1)
    frame.powerContainer = powerContainer
    
    local pSep = powerContainer:CreateTexture(nil, "OVERLAY", nil, 7)
    pSep:SetHeight(1); pSep:SetPoint("TOPLEFT"); pSep:SetPoint("TOPRIGHT")
    pSep:SetColorTexture(0, 0, 0, 1)
    frame.powerSep = pSep

    local powerBg = powerContainer:CreateTexture(nil, "BACKGROUND")
    powerBg:SetAllPoints(); powerBg:SetColorTexture(0, 0, 0, 0.5)
    frame.powerBg = powerBg

    local powerBar = CreateFrame("StatusBar", nil, powerContainer)
    powerBar:SetPoint("TOPLEFT", 0, 0); powerBar:SetPoint("BOTTOMRIGHT", 0, 0)
    powerBar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8")
    powerBar:GetStatusBarTexture():SetHorizTile(false); powerBar:GetStatusBarTexture():SetVertTile(false)
    powerBar:SetStatusBarColor(0.0, 0.5, 1.0, 1.0)
    powerBar:SetMinMaxValues(0, 100); powerBar:SetValue(0)
    powerBar:SetFrameLevel(powerContainer:GetFrameLevel() + 2)
    frame.powerBar = powerBar

    local powerOverlay = CreateFrame("Frame", nil, powerContainer)
    powerOverlay:SetAllPoints()
    powerOverlay:SetFrameLevel(powerBar:GetFrameLevel() + 10)

    local powerText = powerOverlay:CreateFontString(nil, "OVERLAY")
    SetFontSafe(powerText, "Fonts\\FRIZQT__.TTF", 11, "OUTLINE")
    powerText:SetPoint("CENTER", powerOverlay, "CENTER", 0, 0)
    powerText:SetTextColor(1, 1, 1, 1); powerText:SetShadowOffset(1, -1); powerText:SetShadowColor(0, 0, 0, 0.9)
    frame.powerText = powerText

    local debuffAlert = CreateFrame("Frame", nil, frame, "BackdropTemplate")
    debuffAlert:SetSize(18, 18)
    debuffAlert:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -2, 2)
    debuffAlert:SetFrameLevel(frame:GetFrameLevel() + 12)
    debuffAlert:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
        insets = { left = 0, right = 0, top = 0, bottom = 0 },
    })
    debuffAlert:SetBackdropColor(0, 0, 0, 0.15)
    debuffAlert:SetBackdropBorderColor(0, 0, 0, 1)
    debuffAlert:Hide()
    frame.debuffAlert = debuffAlert

    local debuffAlertIcon = debuffAlert:CreateTexture(nil, "ARTWORK")
    debuffAlertIcon:SetPoint("TOPLEFT", debuffAlert, "TOPLEFT", 1, -1)
    debuffAlertIcon:SetPoint("BOTTOMRIGHT", debuffAlert, "BOTTOMRIGHT", -1, 1)
    debuffAlertIcon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
    frame.debuffAlertIcon = debuffAlertIcon

    frame:Hide()
    return frame
end

-- ============================================================
-- SECTION: Health + Power Update Engine
-- AllowSecretHealthPercent: checks MidnightUI_ForceHideHealthPct
--   and settings to determine whether to show health % when secret.
-- Diagnostic helpers (IsPlayerDebugEnabled, CondTintLog, etc.):
--   gate verbose log output behind settings flags.
-- IsSecretValue: wraps issecretvalue() WoW API safely.
-- CoerceNumber/Clamp01Number: taint-safe number coercion.
-- GetConditionTintBarColor: reads frame._muiCondTint* fields and
--   returns the bar's effective tint RGB. Handles secret colour path.
-- EnsureCondTintShade: lazily creates a DISABLE-blend overlay
--   texture that darkens the health bar during condition tinting.
-- ApplyConditionTintToStatusBar: core tint applier — sets StatusBar
--   color via SetVertexColor (secret-safe), shows/hides shade.
-- ForceRestoreBaseBarColors: clears tint, restores class color.
-- ResolveTintColorPayload: processes RGB tint arguments, detects
--   secret values, populates frame._muiCondTintSecret flag.
-- IsUnitActuallyDead: combat-lockdown-aware dead status check.
-- GetDisplayHealthPercent/GetSafeFallbackPercent: safe hp% with
--   fallback when UnitHealth returns secret.
-- UpdateHealth: main health bar update. Applies dead state, color,
--   text, condition tint, bar style. Called on UNIT_HEALTH events.
-- UpdatePower: updates power bar with POWER_COLORS class-specific
--   colors and power type label.
-- UpdateAbsorbs/ReanchorPredictionBars: absorb bar visualization
--   and heal prediction bar repositioning.
-- UpdateAll: batches UpdateHealth + UpdatePower + UpdateAbsorbs.
-- READS:  frame health/power bars, MidnightUISettings.General,
--         UnitHealth, UnitHealthMax, UnitPower, UnitMaxPower
-- WRITES: bar vertex colors, text strings, frame._muiCondTint* fields
-- FUNCTIONS: ~20 helpers + UpdateHealth, UpdatePower, UpdateAll
-- ============================================================
local function ApplyPlayerFrameBarStyle()
    local frame = _G.MidnightUI_PlayerFrame
    if frame and frame.healthBar then
        ApplyHealthBarStyle(frame.healthBar)
    end
    if frame and frame.powerBar then
        ApplyHealthBarStyle(frame.powerBar)
    end
    ApplyFrameTextStyle(frame)
    ApplyFrameLayout(frame)
end
_G.MidnightUI_ApplyPlayerFrameBarStyle = ApplyPlayerFrameBarStyle


local function AllowSecretHealthPercent()
    if _G.MidnightUI_ForceHideHealthPct then return false end
    return MidnightUISettings and MidnightUISettings.General and MidnightUISettings.General.allowSecretHealthPercent == true
end

local lastAllowSecretState = nil
local lastPlayerDebugTime = 0

local function IsPlayerDebugEnabled()
    return _G.MidnightUI_Debug
        and MidnightUISettings
        and MidnightUISettings.PlayerFrame
        and MidnightUISettings.PlayerFrame.debug == true
end

local function IsCondTintDebugEnabled()
    if not _G.MidnightUI_Debug then return false end
    local s = MidnightUISettings
    if not s or not s.PlayerFrame then return false end
    return s.PlayerFrame.condBorderDebug == true or s.PlayerFrame.debug == true
end

local function IsPlayerCondTintDiagConsoleEnabled()
    local diag = _G.MidnightUI_Diagnostics
    if not diag or type(diag.LogDebugSource) ~= "function" then
        return false
    end
    if type(diag.IsEnabled) == "function" then
        local okEnabled, enabled = pcall(diag.IsEnabled)
        return okEnabled and enabled == true
    end
    return true
end

local function EmitPlayerCondTintDiag(msg)
    local text = tostring(msg or "")
    local src = "PlayerCondTint"
    if IsPlayerCondTintDiagConsoleEnabled() then
        pcall(_G.MidnightUI_Diagnostics.LogDebugSource, src, text)
        return
    end
    _G.MidnightUI_DiagnosticsQueue = _G.MidnightUI_DiagnosticsQueue or {}
    table.insert(_G.MidnightUI_DiagnosticsQueue, "[" .. src .. "] " .. text)
end

local function CondTintLog(msg)
    if not IsCondTintDebugEnabled() then return end
    EmitPlayerCondTintDiag(msg)
end

local function IsCondTintVerboseEnabled()
    local s = MidnightUISettings
    if not s or not s.PlayerFrame then return false end
    return s.PlayerFrame.condBorderDebugVerbose == true
end

local function CondTintVerboseLog(msg)
    if not IsCondTintVerboseEnabled() then return end
    CondTintLog(msg)
end

local function PlayerSafeToString(value)
    if value == nil then return "nil" end
    if value == false then return "false" end
    local ok, s = pcall(tostring, value)
    if not ok then return "[Restricted]" end
    local ok2 = pcall(function() return table.concat({ s }, "") end)
    if not ok2 then return "[Restricted]" end
    return s
end

local function PlayerLogDebug(message)
    return
end

local function IsSecretValue(val)
    if type(issecretvalue) ~= "function" then return false end
    local ok, res = pcall(issecretvalue, val)
    return ok and res == true
end

local function CoerceNumber(val)
    if val == nil then return nil end
    return tonumber(tostring(val))
end

local function Clamp01Number(v)
    if type(v) ~= "number" then return nil end
    if v < 0 then return 0 end
    if v > 1 then return 1 end
    return v
end

local COND_TINT_DARKEN_MUL = 0.42
local COND_TINT_DARKEN_SHADE_ALPHA = 1 - COND_TINT_DARKEN_MUL

local function GetConditionTintBarColor(frame, fallbackR, fallbackG, fallbackB)
    if not frame or frame._muiCondTintActive ~= true then
        return fallbackR, fallbackG, fallbackB, false, false
    end
    if frame._muiCondTintHasColor ~= true then
        return fallbackR, fallbackG, fallbackB, true, false
    end

    if frame._muiCondTintSecret == true then
        return frame._muiCondTintR, frame._muiCondTintG, frame._muiCondTintB, true, true
    end

    local r = Clamp01Number(CoerceNumber(frame._muiCondTintR))
    local g = Clamp01Number(CoerceNumber(frame._muiCondTintG))
    local b = Clamp01Number(CoerceNumber(frame._muiCondTintB))
    if not r or not g or not b then
        return fallbackR, fallbackG, fallbackB, true, false
    end

    return r * COND_TINT_DARKEN_MUL, g * COND_TINT_DARKEN_MUL, b * COND_TINT_DARKEN_MUL, true, false
end

local function EnsureCondTintShade(bar)
    if not bar then return nil end
    if not bar._muiCondTintShade then
        local shade = bar:CreateTexture(nil, "OVERLAY", nil, 6)
        shade:SetTexture("Interface\\Buttons\\WHITE8X8")
        shade:SetBlendMode("BLEND")
        shade:Hide()
        bar._muiCondTintShade = shade
    end
    local anchor = (bar.GetStatusBarTexture and bar:GetStatusBarTexture()) or bar
    bar._muiCondTintShade:ClearAllPoints()
    bar._muiCondTintShade:SetAllPoints(anchor)
    return bar._muiCondTintShade
end

local function SetCondTintShadeVisible(bar, visible, frame)
    local shade = EnsureCondTintShade(bar)
    if not shade then return end

    if not visible or not frame then
        shade:Hide()
        return
    end

    if frame._muiCondTintHasColor == true then
        shade:Hide()
        return
    end

    shade:SetVertexColor(0.35, 0.35, 0.40, COND_TINT_DARKEN_SHADE_ALPHA * 0.55)
    shade:Show()
end

local function SetBarVisualPolishSuppressed(bar, suppressed)
    if not bar then return end
    if suppressed == true then
        if bar._muiTopHighlight then bar._muiTopHighlight:Hide() end
        if bar._muiBottomShade then bar._muiBottomShade:Hide() end
        if bar._muiSpecular then bar._muiSpecular:Hide() end
        if bar._muiFlatGradient then bar._muiFlatGradient:Hide() end
        bar._muiCondTintSecretPolishOff = true
        bar._muiCondTintGradientShapeActive = false
        return
    end
    if bar._muiCondTintSecretPolishOff == true then
        bar._muiCondTintSecretPolishOff = false
        ApplyHealthBarStyle(bar)
    end
    if bar._muiCondTintGradientShapeActive == true then
        bar._muiCondTintGradientShapeActive = false
        ApplyHealthBarStyle(bar)
    end
end

local function ApplyNumericColorToStatusBar(bar, r, g, b)
    if not bar then return end
    local nr = Clamp01Number(CoerceNumber(r)) or 0
    local ng = Clamp01Number(CoerceNumber(g)) or 0
    local nb = Clamp01Number(CoerceNumber(b)) or 0

    bar._muiStyleFallbackR = nr
    bar._muiStyleFallbackG = ng
    bar._muiStyleFallbackB = nb

    bar:SetStatusBarColor(nr, ng, nb, 1.0)
    ApplyHealthBarStyle(bar)

    local tex = bar.GetStatusBarTexture and bar:GetStatusBarTexture()
    if tex then
        tex:SetVertexColor(nr, ng, nb, 1.0)
    end
end

local function ApplyConditionTintToStatusBar(bar, tintR, tintG, tintB, fallbackR, fallbackG, fallbackB, isTinted, useSecretTint, logTarget)
    if not bar then return end

    if isTinted and useSecretTint then
        ApplyNumericColorToStatusBar(bar, fallbackR, fallbackG, fallbackB)
        SetBarVisualPolishSuppressed(bar, true)

        local tex = bar.GetStatusBarTexture and bar:GetStatusBarTexture()
        if tex then
            tex:SetVertexColor(tintR, tintG, tintB, 1.0)
            if bar._muiCondTintSecretApplied ~= true then
                bar._muiCondTintSecretApplied = true
                CondTintVerboseLog("apply=SECRET_TEX target=" .. tostring(logTarget or "bar"))
            end
        else
            if bar._muiCondTintSecretApplied ~= false then
                bar._muiCondTintSecretApplied = false
                CondTintVerboseLog("apply=SECRET_TEX_MISSING target=" .. tostring(logTarget or "bar"))
            end
        end
        return
    end

    ApplyNumericColorToStatusBar(bar, tintR, tintG, tintB)
    SetBarVisualPolishSuppressed(bar, false)

    if bar._muiCondTintSecretApplied == true then
        bar._muiCondTintSecretApplied = false
        CondTintVerboseLog("apply=NUMERIC target=" .. tostring(logTarget or "bar"))
    end
end

local function DescribeTintRGBForLog(r, g, b)
    if IsSecretValue(r) or IsSecretValue(g) or IsSecretValue(b) then
        return "secret"
    end
    local rn, gn, bn = tonumber(r), tonumber(g), tonumber(b)
    if rn and gn and bn then
        return string.format("%.3f,%.3f,%.3f", rn, gn, bn)
    end
    return tostring(r) .. "," .. tostring(g) .. "," .. tostring(b)
end

local function ForceRestoreBaseBarColors(frame)
    if not frame then return end

    if frame.healthBar then
        local classR, classG, classB = GetUnitColor("player")
        ApplyNumericColorToStatusBar(frame.healthBar, classR, classG, classB)
        SetBarVisualPolishSuppressed(frame.healthBar, false)
        frame.healthBar._muiCondTintSecretApplied = false
        if frame.healthBar._muiCondTintShade then
            frame.healthBar._muiCondTintShade:Hide()
        end
        CondTintVerboseLog("restore=HEALTH rgb=" .. string.format("%.3f,%.3f,%.3f", classR or 0, classG or 0, classB or 0))
    end

    if frame.powerBar then
        local powerType, powerToken = UnitPowerType("player")
        local token = powerToken or "MANA"
        local color = POWER_COLORS[token] or DEFAULT_POWER_COLOR
        ApplyNumericColorToStatusBar(frame.powerBar, color[1], color[2], color[3])
        SetBarVisualPolishSuppressed(frame.powerBar, false)
        frame.powerBar._muiCondTintSecretApplied = false
        if frame.powerBar._muiCondTintShade then
            frame.powerBar._muiCondTintShade:Hide()
        end
        CondTintVerboseLog("restore=POWER token=" .. tostring(token) .. " rgb=" .. string.format("%.3f,%.3f,%.3f", color[1] or 0, color[2] or 0, color[3] or 0))
    end
end

local function ResolveTintColorPayload(r, g, b)
    if r == nil or g == nil or b == nil then
        return nil, nil, nil, false, false
    end
    if IsSecretValue(r) or IsSecretValue(g) or IsSecretValue(b) then
        return r, g, b, true, true
    end
    local rn = Clamp01Number(CoerceNumber(r))
    local gn = Clamp01Number(CoerceNumber(g))
    local bn = Clamp01Number(CoerceNumber(b))
    if not rn or not gn or not bn then
        return nil, nil, nil, false, false
    end
    return rn, gn, bn, true, false
end

local function IsUnitActuallyDead(unit)
    if not unit or not UnitExists(unit) then return false end

    local okDeadOrGhost, deadOrGhost = pcall(UnitIsDeadOrGhost, unit)
    if okDeadOrGhost then
        local okState, isDeadOrGhost = pcall(function()
            return deadOrGhost == true
        end)
        if okState and isDeadOrGhost then
            return true
        end
    end

    local okDead, dead = pcall(UnitIsDead, unit)
    if okDead then
        local okIsDead, isDead = pcall(function()
            return dead == true
        end)
        if okIsDead and isDead then
            return true
        end
    end

    local okGhost, ghost = pcall(UnitIsGhost, unit)
    if okGhost then
        local okIsGhost, isGhost = pcall(function()
            return ghost == true
        end)
        if okIsGhost and isGhost then
            return true
        end
    end

    local okCorpse, corpse = pcall(UnitIsCorpse, unit)
    if okCorpse then
        local okIsCorpse, isCorpse = pcall(function()
            return corpse == true
        end)
        if okIsCorpse and isCorpse then
            return true
        end
    end

    local okHp, hp = pcall(UnitHealth, unit)
    if okHp and hp ~= nil then
        local okCmp, isZeroOrLess = pcall(function()
            return hp <= 0
        end)
        if okCmp and isZeroOrLess then
            return true
        end
    end

    return false
end

local function GetDisplayHealthPercent(unit)
    if not unit then return nil end
    local allowSecret = AllowSecretHealthPercent()
    if not allowSecret then
        return nil
    end

    if UnitHealthPercent then
        local scaleTo100 = CurveConstants and CurveConstants.ScaleTo100 or nil
        local ok, pct = pcall(UnitHealthPercent, unit, true, scaleTo100)
        if ok and pct ~= nil then
            if allowSecret or not IsSecretValue(pct) then
                return pct
            end
        end
    end
    return nil
end

local function GetSafeFallbackPercent(cur, max)
    local okPct, pct = pcall(function()
        return (cur / max) * 100
    end)
    if okPct and type(pct) == "number" then
        return pct
    end
    local curNum = CoerceNumber(cur)
    local maxNum = CoerceNumber(max)
    if type(curNum) == "number" and type(maxNum) == "number" and maxNum > 0 then
        return (curNum / maxNum) * 100
    end
    return nil
end

local function UpdateHealth(frame)
    if not UnitExists("player") then return end
    local allowSecret = AllowSecretHealthPercent()
    if lastAllowSecretState == nil or lastAllowSecretState ~= allowSecret then
        lastAllowSecretState = allowSecret
        PlayerLogDebug("AllowProtectedHealth%=" .. tostring(allowSecret))
    end

    local classR, classG, classB = GetUnitColor("player")
    local healthR, healthG, healthB, healthTinted, healthSecret = GetConditionTintBarColor(frame, classR, classG, classB)
    local useSecretHealthTint = healthSecret == true
    ApplyConditionTintToStatusBar(
        frame.healthBar,
        healthR, healthG, healthB,
        classR, classG, classB,
        healthTinted,
        useSecretHealthTint,
        "health"
    )
    SetCondTintShadeVisible(frame.healthBar, healthTinted, frame)
    frame.nameText:SetTextColor(classR, classG, classB, 1)
    frame.nameText:SetText(UnitName("player"))

    local hasIncomingRes = UnitHasIncomingResurrection("player")
    local isDead = IsUnitActuallyDead("player")

    local level = UnitLevel("player")
    if level == -1 then level = "??" end
    frame.levelText:SetText(level)

    local current = UnitHealth("player")
    local max = UnitHealthMax("player")
    if not isDead and not hasIncomingRes then
        local okZero, isZero = pcall(function() return current <= 0 end)
        local okMaxPos, maxPos = pcall(function() return max > 0 end)
        if okZero and isZero and okMaxPos and maxPos then
            isDead = true
        end
    end

    if frame.healthBg then
        local bgR, bgG, bgB, bgAlpha = classR * 0.15, classG * 0.15, classB * 0.15, 0.6
        frame.healthBg:SetColorTexture(bgR, bgG, bgB, bgAlpha)
    end

    pcall(function()
        frame.healthBar:SetMinMaxValues(0, max)
        frame.healthBar:SetValue(current)
    end)

    local pct = GetDisplayHealthPercent("player")
    if not pct then
        pct = GetSafeFallbackPercent(current, max)
    end
    local fmt = _G.MidnightUI_FormatHealthText
    local renderedText = fmt and fmt(current, max, pct) or nil
    if not renderedText and pct then
        local ok, text = pcall(string.format, "%.0f%%", pct)
        if ok then renderedText = text end
    end
    frame.healthText:SetText(renderedText or "")

    if frame.deadIcon then
        if isDead and not hasIncomingRes then
            frame.deadIcon:SetSize(32, 32)
            frame.deadIcon:ClearAllPoints()
            frame.deadIcon:SetPoint("RIGHT", frame.healthContainer, "RIGHT", -28, 0)
            ApplyDeadIconVisualStyle(frame.deadIcon)
            frame.deadIcon:Show()
            frame.healthText:SetText("")
        else
            frame.deadIcon:Hide()
        end
    end

    if frame.resIcon then
        if hasIncomingRes then
            frame.resIcon:Show()
            if frame.resGlow then
                frame.resGlow:Show()
                frame.resGlow:SetAlpha(0.6)
            end
            local alpha = (MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.alpha) or 0.95
            frame:SetAlpha(alpha)
        else
            frame.resIcon:Hide()
            if frame.resGlow then frame.resGlow:Hide() end
        end
    end

    PlayerLogDebug("pctType=" .. PlayerSafeToString(type(pct))
        .. " curType=" .. PlayerSafeToString(type(current))
        .. " maxType=" .. PlayerSafeToString(type(max))
        .. " isDead=" .. PlayerSafeToString(isDead)
        .. " incomingRes=" .. PlayerSafeToString(hasIncomingRes)
        .. " iconShown=" .. PlayerSafeToString(frame.deadIcon and frame.deadIcon:IsShown())
        .. " atlasReady=" .. PlayerSafeToString(C_Texture and C_Texture.GetAtlasInfo and C_Texture.GetAtlasInfo(DEAD_STATUS_ATLAS) ~= nil)
        .. " rendered=" .. PlayerSafeToString(renderedText)
        .. " shown=" .. PlayerSafeToString(frame.healthText and frame.healthText:GetText()))
end


local function UpdatePower(frame)
    if not UnitExists("player") then return end

    local powerType, powerToken = UnitPowerType("player")
    local token = powerToken or "MANA"
    local color = POWER_COLORS[token] or DEFAULT_POWER_COLOR

    local powerR, powerG, powerB, powerTinted, powerSecret = GetConditionTintBarColor(frame, color[1], color[2], color[3])
    local useSecretPowerTint = powerSecret == true
    ApplyConditionTintToStatusBar(
        frame.powerBar,
        powerR, powerG, powerB,
        color[1], color[2], color[3],
        powerTinted,
        useSecretPowerTint,
        "power"
    )
    SetCondTintShadeVisible(frame.powerBar, powerTinted, frame)

    local current = UnitPower("player", powerType)
    local max = UnitPowerMax("player", powerType)

    pcall(function()
        frame.powerBar:SetMinMaxValues(0, max)
        frame.powerBar:SetValue(current)
    end)

    local ok, txt = pcall(function()
        return string.format("%s / %s", current, max)
    end)

    if ok and txt then
        frame.powerText:SetText(txt)
    else
        frame.powerText:SetText("")
    end
end

-- ============================================================
-- SECTION: Condition Tint State Machine
-- Manages the colored health-bar tint that signals an active
-- debuff condition (poison=green, magic=blue, etc.).  Uses a
-- two-tier source priority model so that high-confidence
-- classifications (ConditionBorder strong-path) cannot be
-- overwritten by low-confidence ones (ticker weak-path).
--
-- READS:  frame._muiCondTint* fields, _G.MidnightUI_ConditionBorder
-- WRITES: frame._muiCondTintActive, frame._muiCondTintSource,
--         frame._muiCondTintColor (r,g,b), frame._muiCondTintSecret,
--         frame._muiCondTintHasColor
--
-- FUNCTIONS:
--   IsWeakConditionTintSource(source) — returns true for ticker/
--     weak sources that may not override a strong classification
--   GetConditionTintBarColor(frame,cr,cg,cb) — returns final
--     r,g,b,tinted; blends condition color over class color
--   ApplyConditionTintToStatusBar(bar,...) — sets vertex color
--     on bar accounting for secret values
--   SetCondTintShadeVisible(bar,tinted,frame) — shows/hides the
--     shade overlay that darkens the non-tinted portion
--   SetPrimaryFillFromSecretColor(frame,r,g,b) — stores a secret-
--     valued condition color safely for later use
--   SetConditionTint(frame,r,g,b,secret,source) — central setter;
--     respects source priority; debounces redundant calls
--   ClearConditionTint(frame,source) — removes tint if source
--     has sufficient priority; schedules 100ms grace timer
--   SyncPlayerFrameBarTint(tinted,r,g,b,logTag) — propagates the
--     active tint to the power bar and Blizzard overlay
-- ============================================================
local function IsWeakConditionTintSource(source)
    if type(source) ~= "string" then
        return false
    end
    return source == "PRIMARY_OVERLAY" or source == "SECRET_UNKNOWN"
end

local function IsStrongConditionTintSource(source)
    if type(source) ~= "string" then
        return false
    end
    if IsWeakConditionTintSource(source) then
        return false
    end
    if source:match("^PRIMARY_") then return true end
    if source:match("^SECRET_") then return true end
    if source:match("^RGB_") then return true end
    return false
end

local function GetCondTintNow()
    if GetTimePreciseSec then
        return GetTimePreciseSec()
    end
    if GetTime then
        return GetTime()
    end
    return 0
end

local WEAK_TINT_APPLY_DELAY_SEC = 0.12
local SECRET_UNKNOWN_APPLY_DELAY_SEC = 0.22
local SECRET_TINT_RESYNC_SEC = 0.10

local function SetPlayerFrameConditionTintState(isActive, r, g, b, source)
    local frame = _G["MidnightUI_PlayerFrame"]
    if not frame then
        return false
    end

    local wasActive = frame._muiCondTintActive == true
    local nextActive = isActive == true
    local nr, ng, nb, hasColor, isSecret = nil, nil, nil, false, false

    if nextActive then
        nr, ng, nb, hasColor, isSecret = ResolveTintColorPayload(r, g, b)
        if r == nil or g == nil or b == nil then
            nextActive = false
        end

        if IsWeakConditionTintSource(source) then
            if frame._muiCondTintActive == true and IsStrongConditionTintSource(frame._muiCondTintSource) then
                return false
            end
            local delay = WEAK_TINT_APPLY_DELAY_SEC
            if source == "SECRET_UNKNOWN" then
                delay = SECRET_UNKNOWN_APPLY_DELAY_SEC
            end
            local now = GetCondTintNow()
            local pendingAt = frame._muiCondTintWeakPendingAt
            local pendingSrc = frame._muiCondTintWeakPendingSource
            if not pendingAt or pendingSrc ~= source then
                frame._muiCondTintWeakPendingAt = now
                frame._muiCondTintWeakPendingSource = source
                return false
            end
            if now > 0 and (now - pendingAt) < delay then
                return false
            end
        else
            frame._muiCondTintWeakPendingAt = nil
            frame._muiCondTintWeakPendingSource = nil
        end

        if frame._muiCondTintActive == true
            and IsStrongConditionTintSource(frame._muiCondTintSource)
            and IsWeakConditionTintSource(source) then
            return false
        end
    else
        frame._muiCondTintWeakPendingAt = nil
        frame._muiCondTintWeakPendingSource = nil
    end

    local changed = (frame._muiCondTintActive == true) ~= nextActive
    if nextActive and not changed then
        changed = (frame._muiCondTintSource or "") ~= (source or "")
            or ((frame._muiCondTintHasColor == true) ~= (hasColor == true))
            or ((frame._muiCondTintSecret == true) ~= (isSecret == true))
            or (hasColor == true and isSecret ~= true and (
                math.abs((frame._muiCondTintR or 0) - (nr or 0)) > 0.001
                or math.abs((frame._muiCondTintG or 0) - (ng or 0)) > 0.001
                or math.abs((frame._muiCondTintB or 0) - (nb or 0)) > 0.001
            ))
    end

    local forceSecretResync = false

    if nextActive then
        frame._muiCondTintActive = true
        frame._muiCondTintHasColor = hasColor == true
        frame._muiCondTintSecret = isSecret == true
        frame._muiCondTintWeakPendingAt = nil
        frame._muiCondTintWeakPendingSource = nil
        if hasColor == true then
            frame._muiCondTintR, frame._muiCondTintG, frame._muiCondTintB = nr, ng, nb
        else
            frame._muiCondTintR, frame._muiCondTintG, frame._muiCondTintB = nil, nil, nil
        end
        frame._muiCondTintSource = source
        if hasColor == true and isSecret == true and not changed then
            local now = GetCondTintNow()
            local lastSyncAt = frame._muiCondTintSecretSyncAt or 0
            if now <= 0 or (now - lastSyncAt) >= SECRET_TINT_RESYNC_SEC then
                forceSecretResync = true
                frame._muiCondTintSecretSyncAt = now
                CondTintVerboseLog("refresh=SECRET_SYNC src=" .. tostring(source or "unknown"))
            end
        else
            frame._muiCondTintSecretSyncAt = nil
        end
    else
        frame._muiCondTintActive = false
        frame._muiCondTintHasColor = false
        frame._muiCondTintSecret = false
        frame._muiCondTintR, frame._muiCondTintG, frame._muiCondTintB = nil, nil, nil
        frame._muiCondTintSource = nil
        frame._muiCondTintWeakPendingAt = nil
        frame._muiCondTintWeakPendingSource = nil
        frame._muiCondTintSecretSyncAt = nil
        if wasActive then
            ForceRestoreBaseBarColors(frame)
        end
    end

    if not changed and not forceSecretResync then
        return false
    end

    if frame:IsShown() then
        UpdateHealth(frame)
        UpdatePower(frame)
    end
    return changed
end

_G.MidnightUI_SetPlayerFrameConditionTint = function(isActive, r, g, b, source)
    local ok, changedOrErr = pcall(SetPlayerFrameConditionTintState, isActive, r, g, b, source)
    if not ok then
        return false
    end
    return changedOrErr == true
end

local function ReanchorPredictionBars(frame)
    local tex = frame.healthBar:GetStatusBarTexture()
    if not tex then return end
    local container = frame.healthBar:GetParent()

    frame.incomingHeal:ClearAllPoints()
    frame.incomingHeal:SetPoint("TOPLEFT", tex, "TOPRIGHT", 0, 0)
    frame.incomingHeal:SetPoint("BOTTOMRIGHT", container, "BOTTOMRIGHT", 0, 0)

    frame.absorbBar:ClearAllPoints()
    frame.absorbBar:SetPoint("TOPLEFT", tex, "TOPRIGHT", 0, 0)
    frame.absorbBar:SetPoint("BOTTOMRIGHT", container, "BOTTOMRIGHT", 0, 0)
end

local function UpdateAbsorbs(frame)
    if not UnitExists("player") then return end

    local calc = frame._healCalc
    if not calc then
        frame.incomingHeal:SetAlpha(0)
        frame.absorbBar:SetAlpha(0)
        return
    end

    ReanchorPredictionBars(frame)

    pcall(UnitGetDetailedHealPrediction, "player", nil, calc)

    local _, missing = pcall(function() return calc:GetMissingHealth() end)

    pcall(function()
        local allHeal = calc:GetIncomingHeals()
        frame.incomingHeal:SetMinMaxValues(0, missing)
        frame.incomingHeal:SetValue(allHeal)
        frame.incomingHeal:SetAlpha(allHeal)
    end)

    pcall(function()
        local absorbAmount = calc:GetDamageAbsorbs()
        frame.absorbBar:SetMinMaxValues(0, missing)
        frame.absorbBar:SetValue(absorbAmount)
        frame.absorbBar:SetAlpha(absorbAmount)
    end)
end

local function UpdateAll(frame)
    UpdateHealth(frame)
    UpdatePower(frame)
    UpdateAbsorbs(frame)
end

-- ============================================================
-- SECTION: Aura + Debuff Display System
-- Positions the Blizzard AuraContainer relative to the custom
-- player frame, adjusts aura icon size/spacing, suppresses
-- Blizzard's default aura rows, and drives the per-combat
-- debuff ticker that feeds condition tint.
--
-- READS:  Settings.playerFrame.auraAlignment, auraSize,
--         auraSpacing, showDebuffs, showBuffs
-- WRITES: frame.AuraContainer position/scale, frame._muiAuraXxx
--         fields, frame._muiDebuffTickerRunning
--
-- FUNCTIONS:
--   ApplyAuraFrameAlignment(frame, alignment) — repositions
--     AuraContainer (BELOW/ABOVE/RIGHT/LEFT) relative to the
--     player frame after each settings change
--   ApplyAuraFrameSize(frame) — sets icon size + spacing on the
--     container; called after alignment or size setting changes
--   SuppressBlizzardAuras(frame) — hides Blizzard buff/debuff
--     rows and prevents them from re-showing during combat
--   UpdatePlayerDispellableDebuffsOverlay(frame) — scans harmful
--     aura slots, classifies each, builds the condition tint
--     signal; called by the 50ms combat ticker
--   StartDebuffTicker(frame) — creates C_Timer.NewTicker at 0.05s
--     while in combat (PLAYER_REGEN_DISABLED)
--   StopDebuffTicker() — cancels ticker on PLAYER_REGEN_ENABLED
-- ============================================================
local function ApplyAuraFrameAlignment(frame, alignment)
    if not frame then return end

    local container = frame.AuraContainer
    if not container then return end

    local mapped = alignment
    if alignment == "TOPRIGHT" or alignment == "BOTTOMRIGHT" then mapped = "Right" end
    if alignment == "TOPLEFT" or alignment == "BOTTOMLEFT" then mapped = "Left" end
    if alignment == "CENTER" then mapped = "Center" end

    frame.midnightAlignment = mapped
    container.midnightAlignment = mapped

    container:ClearAllPoints()

    if mapped == "Left" then
        container:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0)
    elseif mapped == "Center" then
        container:SetPoint("TOP", frame, "TOP", 0, 0)
    else
        container:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, 0)
    end

    if not container.midnightAlignmentHooked then
        if container.LayoutChildren then
            hooksecurefunc(container, "LayoutChildren", function(self)
                if self.midnightAlignment and self:GetParent().midnightAlignment == self.midnightAlignment then
                    C_Timer.After(0, function()
                        if self:GetParent() then
                            ApplyAuraFrameAlignment(self:GetParent(), self.midnightAlignment)
                        end
                    end)
                end
            end)
        end
        container.midnightAlignmentHooked = true
    end

    if container.LayoutChildren then
        container:LayoutChildren()
    elseif container.Layout then
        container:Layout()
    end
end

local function ClampPlayerAuraMaxShown(value, fallback)
    local n = tonumber(value)
    if not n then
        n = tonumber(fallback) or 16
    end
    n = math.floor(n + 0.5)
    if n < 1 then n = 1 end
    if n > 32 then n = 32 end
    return n
end

local function SetPlayerAuraLimitCVar(cvarName, value)
    local text = tostring(value)
    if C_CVar and C_CVar.SetCVar then
        pcall(C_CVar.SetCVar, cvarName, text)
        return
    end
    if SetCVar then
        pcall(SetCVar, cvarName, text)
    end
end

local function RefreshDefaultAuraLayouts()
    local function RefreshFrameLayout(frame)
        if not frame then return end
        local container = frame.AuraContainer
        if container and container.LayoutChildren then
            pcall(container.LayoutChildren, container)
        elseif container and container.Layout then
            pcall(container.Layout, container)
        end
    end
    RefreshFrameLayout(BuffFrame)
    RefreshFrameLayout(DebuffFrame)
end

local function InstallAuraButtonTooltipFallback(button, isDebuff)
    if not button or button._muiTooltipFallbackHooked then return end
    button._muiTooltipFallbackHooked = true
    if type(button.HookScript) ~= "function" then return end
    button:HookScript("OnEnter", function(self)
        if not GameTooltip or not GameTooltip:IsOwned(self) then return end
        local n = 0
        if type(GameTooltip.NumLines) == "function" then
            local okN, count = pcall(GameTooltip.NumLines, GameTooltip)
            if okN and type(count) == "number" then n = count end
        end
        if n > 0 then return end
        GameTooltip:AddLine(isDebuff and "Debuff" or "Buff", 1, 1, 1)
        GameTooltip:AddLine("|cff888888(details hidden in combat)|r", 0.6, 0.6, 0.6)
        GameTooltip:Show()
    end)
end

local function InstallAuraButtonTooltipFallbacks(frame, isDebuff)
    if not frame or not frame.AuraContainer or not frame.AuraContainer.GetChildren then return end
    for _, child in ipairs({frame.AuraContainer:GetChildren()}) do
        InstallAuraButtonTooltipFallback(child, isDebuff)
    end
end

local function HookAuraContainerLayout(frame, settingsKey)
    if not frame or not frame.AuraContainer then return end
    local container = frame.AuraContainer
    if container._muiMaxShownHooked then return end
    container._muiMaxShownHooked = true
    local isDebuff = (settingsKey == "debuffs")
    local function ReEnforce()
        local s = MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame[settingsKey]
        local maxLimit = settingsKey == "auras" and 32 or 16
        local maxShown = (s and s.maxShown) or config[settingsKey].maxShown or maxLimit
        maxShown = ClampPlayerAuraMaxShown(maxShown, maxLimit)
        EnforceAuraMaxShown(frame, maxShown)
        InstallAuraButtonTooltipFallbacks(frame, isDebuff)
    end
    if container.LayoutChildren then
        hooksecurefunc(container, "LayoutChildren", function()
            C_Timer.After(0, ReEnforce)
        end)
    end
    if container.Layout then
        hooksecurefunc(container, "Layout", function()
            C_Timer.After(0, ReEnforce)
        end)
    end
    InstallAuraButtonTooltipFallbacks(frame, isDebuff)
end

local function EnforceAuraMaxShown(frame, maxShown)
    if not frame or not frame.AuraContainer or not frame.AuraContainer.GetChildren then return end
    local container = frame.AuraContainer
    local visibleCount = 0
    for _, child in ipairs({container:GetChildren()}) do
        if child and child:IsShown() then
            visibleCount = visibleCount + 1
            if visibleCount > maxShown then
                child:Hide()
            end
        end
    end
end

local function ApplyPlayerAuraDisplayLimits()
    local auraSettings = (MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.auras) or config.auras
    local debuffSettings = (MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.debuffs) or config.debuffs
    local buffMax = ClampPlayerAuraMaxShown(auraSettings and auraSettings.maxShown, config.auras and config.auras.maxShown or 32)
    local debuffMax = ClampPlayerAuraMaxShown(debuffSettings and debuffSettings.maxShown, config.debuffs and config.debuffs.maxShown or 16)
    SetPlayerAuraLimitCVar("buffFrameMaxBuffs", buffMax)
    SetPlayerAuraLimitCVar("buffFrameMaxDebuffs", debuffMax)
    RefreshDefaultAuraLayouts()
    EnforceAuraMaxShown(BuffFrame, buffMax)
    EnforceAuraMaxShown(DebuffFrame, debuffMax)
    HookAuraContainerLayout(BuffFrame, "auras")
    HookAuraContainerLayout(DebuffFrame, "debuffs")
end

function MidnightUI_ApplyPlayerAuraSettings()
    if not BuffFrame then return end
    
    local auraSettings = (MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.auras) or config.auras
    BuffFrame:SetSize(GetPlayerAuraOverlaySize("auras"))
    
    if auraSettings.enabled == false then
        BuffFrame:Hide()
    else
        BuffFrame:Show()
    end
    
    BuffFrame:SetScale((auraSettings.scale or 100) / 100)
    BuffFrame:SetAlpha(auraSettings.alpha or 1.0)
    ApplyPlayerAuraDisplayLimits()
    
    ApplyAuraFrameAlignment(BuffFrame, auraSettings.alignment or "Right")
    
    local MR = _G.MidnightUI_MoverRegistry
    if MR and MR.ApplyPosition then MR.ApplyPosition("PlayerAuras") end
end

local AURA_BTN_W = 30
local AURA_BTN_H = 40
local AURA_ICON_SIZE = 30
local AURA_H_STRIDE = 35
local AURA_V_STRIDE = 45

local function GetAuraPerRow(auraKey)
    local s = MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame[auraKey]
    return (s and s.perRow) or config[auraKey].perRow or 16
end

local function EnsureAuraEditPreview(overlay)
    if overlay._muiPreviewIcons then return overlay._muiPreviewIcons end
    overlay._muiPreviewIcons = {}
    for i = 1, 32 do
        local holder = CreateFrame("Frame", nil, overlay, "BackdropTemplate")
        holder:SetSize(AURA_ICON_SIZE, AURA_ICON_SIZE)
        holder:SetBackdrop({ edgeFile = "Interface\\Buttons\\WHITE8x8", edgeSize = 1 })
        holder:SetBackdropBorderColor(0.2, 0.2, 0.2, 0.9)
        local icon = holder:CreateTexture(nil, "ARTWORK")
        icon:SetAllPoints()
        icon:SetTexture(134400)
        icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
        holder.icon = icon
        holder:Hide()
        overlay._muiPreviewIcons[i] = holder
    end
    return overlay._muiPreviewIcons
end

local function UpdateAuraEditPreview(overlay, maxShown)
    if not overlay then return end
    local icons = EnsureAuraEditPreview(overlay)
    local parent = overlay:GetParent()
    local auraKey = parent == BuffFrame and "auras" or "debuffs"
    local maxLimit = auraKey == "auras" and 32 or 16
    local count = tonumber(maxShown) or maxLimit
    if count < 1 then count = 1 end
    if count > maxLimit then count = maxLimit end

    local size = AURA_ICON_SIZE
    local spacing = 5
    local stride = size + spacing

    local s = MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame[auraKey]
    local perRow = (s and s.perRow) or config[auraKey].perRow or 16
    local alignment = (s and s.alignment) or config[auraKey].alignment or "Right"

    local cols = math.min(perRow, count)
    local rows = math.ceil(count / perRow)
    local gridW = (cols * stride) - spacing + 4
    local gridH = (rows * stride) - spacing + 4
    if parent and parent.SetSize then
        parent:SetSize(gridW, gridH)
    end

    overlay:ClearAllPoints()
    overlay:SetAllPoints(parent)

    local useRight = (alignment == "Right")
    local useLeft = (alignment == "Left")
    for i, holder in ipairs(icons) do
        holder:SetSize(size, size)
        if i <= count then
            holder:ClearAllPoints()
            local row = math.floor((i - 1) / perRow)
            local col = (i - 1) % perRow
            if useRight then
                holder:SetPoint("TOPRIGHT", overlay, "TOPRIGHT", -2 - (col * stride), -2 - (row * stride))
            elseif useLeft then
                holder:SetPoint("TOPLEFT", overlay, "TOPLEFT", 2 + (col * stride), -2 - (row * stride))
            else
                local totalW = cols * stride - (stride - size)
                local startX = -totalW / 2 + (col * stride)
                holder:SetPoint("TOP", overlay, "TOP", startX, -2 - (row * stride))
            end
            holder:Show()
        else
            holder:Hide()
        end
    end
end


local function InitBuffFrame(playerFrame)
    if not BuffFrame then return end
    
    local auraSettings = (MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.auras) or config.auras
    
    BuffFrame:SetMovable(true)
    BuffFrame:SetClampedToScreen(true)
    BuffFrame:SetSize(GetPlayerAuraOverlaySize("auras"))
    
    if BuffFrame.CollapseAndExpandButton then
        BuffFrame.CollapseAndExpandButton:SetAlpha(0)
        BuffFrame.CollapseAndExpandButton:EnableMouse(false)
    end
    
    BuffFrame:SetScale((auraSettings.scale or 100) / 100)
    BuffFrame:SetAlpha(auraSettings.alpha or 1.0)
    ApplyPlayerAuraDisplayLimits()
    
    ApplyAuraFrameAlignment(BuffFrame, auraSettings.alignment or "Right")
    
    local function ApplyAuraPos()
        local MR = _G.MidnightUI_MoverRegistry
        if MR and MR.ApplyPosition then MR.ApplyPosition("PlayerAuras") end
    end
    ApplyAuraPos()
    C_Timer.After(0.1, ApplyAuraPos)
    C_Timer.After(0.5, ApplyAuraPos)
    
    if auraSettings.enabled == false then
        BuffFrame:Hide()
    else
        BuffFrame:Show()
    end
end



function MidnightUI_ApplyPlayerDebuffSettings()
    if not DebuffFrame then return end
    
    local debuffSettings = (MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.debuffs) or config.debuffs
    DebuffFrame:SetSize(GetPlayerAuraOverlaySize("debuffs"))
    
    if debuffSettings.enabled == false then
        DebuffFrame:Hide()
    else
        DebuffFrame:Show()
    end
    
    DebuffFrame:SetScale((debuffSettings.scale or 100) / 100)
    DebuffFrame:SetAlpha(debuffSettings.alpha or 1.0)
    ApplyPlayerAuraDisplayLimits()
    
    ApplyAuraFrameAlignment(DebuffFrame, debuffSettings.alignment or "Right")
    
    local MR = _G.MidnightUI_MoverRegistry
    if MR and MR.ApplyPosition then MR.ApplyPosition("PlayerDebuffs") end
end

-- ============================================================
-- SECTION: Overlay Settings + Combat Debuff Integration
-- Registers diagnostic panels, applies overlay settings from
-- Settings.lua, and wires PLAYER_REGEN_DISABLED/ENABLED events
-- to start/stop the debuff ticker.  Also initialises the
-- ConditionBorder integration (SetConditionTint callbacks) so
-- that external modules can push tint updates into this frame.
--
-- READS:  Settings.playerFrame.*, _G.MidnightUI_RegisterDiagnostic,
--         _G.MidnightUI_ConditionBorder, _G.MidnightUI_MoverRegistry
-- WRITES: _G.MidnightUI_ApplyPlayerAuraSettings,
--         _G.MidnightUI_ApplyPlayerFrameBarStyle (re-exported),
--         frame event handlers
--
-- FUNCTIONS:
--   MidnightUI_ApplyPlayerAuraSettings() — reads aura settings
--     and calls ApplyAuraFrameAlignment + ApplyAuraFrameSize
--   Diagnostic "Player Aura" panel — callback triggers
--     MidnightUI_ApplyPlayerAuraSettings for live reload
--   Diagnostic "Player Frame" panel — triggers full
--     ApplyPlayerFrameBarStyle re-apply
--   ConditionBorder.RegisterPlayerFrameCallback(fn) — external
--     hook called whenever ConditionBorder classifies a new
--     tint; pushes SetConditionTint into this frame
--   PLAYER_REGEN_DISABLED handler — StartDebuffTicker
--   PLAYER_REGEN_ENABLED handler — StopDebuffTicker +
--     ClearConditionTint("regen")
-- ============================================================
if _G.MidnightUI_RegisterDiagnostic then
    _G.MidnightUI_RegisterDiagnostic("Player Aura", function()
        if _G.MidnightUI_ApplyPlayerAuraSettings then _G.MidnightUI_ApplyPlayerAuraSettings() end
    end)
    _G.MidnightUI_RegisterDiagnostic("Player Debuff", function()
        if _G.MidnightUI_ApplyPlayerDebuffSettings then _G.MidnightUI_ApplyPlayerDebuffSettings() end
    end)
    _G.MidnightUI_RegisterDiagnostic("Cond Border", function()
        local CB = _G.MidnightUI_ConditionBorder
        local frame = _G.MidnightUI_PlayerFrame
        local lines = CB and CB.GetDiagLines and CB.GetDiagLines(frame)
            or { "[MUI CondBorder] module not loaded" }
        for _, l in ipairs(lines) do
            if _G.MidnightUI_Debug then _G.MidnightUI_Debug(l) end
        end
    end)
elseif _G.MidnightUI_DiagnosticsPending then
    table.insert(_G.MidnightUI_DiagnosticsPending, {
        name = "Player Aura",
        fn = function()
            if _G.MidnightUI_ApplyPlayerAuraSettings then _G.MidnightUI_ApplyPlayerAuraSettings() end
        end
    })
    table.insert(_G.MidnightUI_DiagnosticsPending, {
        name = "Player Debuff",
        fn = function()
            if _G.MidnightUI_ApplyPlayerDebuffSettings then _G.MidnightUI_ApplyPlayerDebuffSettings() end
        end
    })
end



local function EnsurePlayerCombatDebuffSettings()
    if not MidnightUISettings then
        MidnightUISettings = {}
    end
    if not MidnightUISettings.Combat then
        MidnightUISettings.Combat = {}
    end
    local combat = MidnightUISettings.Combat
    if combat.debuffOverlayGlobalEnabled == nil then
        combat.debuffOverlayGlobalEnabled = true
    end
    if combat.debuffOverlayPlayerEnabled == nil then
        combat.debuffOverlayPlayerEnabled = true
    end
    return combat
end

local function IsPlayerDebuffOverlayEnabled()
    local combat = EnsurePlayerCombatDebuffSettings()
    if combat.debuffOverlayGlobalEnabled == false then
        return false
    end
    if combat.debuffOverlayPlayerEnabled == false then
        return false
    end
    return true
end

local function RefreshPlayerDebuffOverlayVisuals()
    local frame = _G.MidnightUI_PlayerFrame
    if _G.MidnightUI_ConditionBorder and _G.MidnightUI_ConditionBorder.Update and frame then
        _G.MidnightUI_ConditionBorder.Update(frame)
    end
end

local function RefreshOverlayByKey(key)
    if _G.MidnightUI_GetOverlayHandle then
        local o = _G.MidnightUI_GetOverlayHandle(key)
        if o and o.dragOverlay and o.dragOverlay.SetAllPoints then
            o.dragOverlay:SetAllPoints(o)
        elseif o and o.GetParent and o:GetParent() and o:GetParent() ~= UIParent then
            o:SetAllPoints()
        end
    end
end

local function BuildPlayerFrameOverlaySettings(content, key)
    if not _G.MidnightUI_CreateOverlayBuilder then return end
    local s = (MidnightUISettings and MidnightUISettings.PlayerFrame) or {}
    local b = _G.MidnightUI_CreateOverlayBuilder(content, { startY = -6 })
    b:Header("Frame")
    b:Checkbox("Enable Player Frame", s.enabled ~= false, function(v)
        MidnightUISettings.PlayerFrame.enabled = v
        if _G.MidnightUI_Settings and _G.MidnightUI_Settings.ApplyPlayerSettings then
            _G.MidnightUI_Settings.ApplyPlayerSettings()
        end
        RefreshOverlayByKey(key)
    end)
    b:Checkbox("Player Debuff Overlay", IsPlayerDebuffOverlayEnabled(), function(v)
        local combat = EnsurePlayerCombatDebuffSettings()
        combat.debuffOverlayPlayerEnabled = v and true or false
        RefreshPlayerDebuffOverlayVisuals()
    end)
    b:Slider("Scale %", 50, 200, 5, s.scale or 100, function(v)
        MidnightUISettings.PlayerFrame.scale = math.floor(v)
        if _G.MidnightUI_Settings and _G.MidnightUI_Settings.ApplyPlayerSettings then
            _G.MidnightUI_Settings.ApplyPlayerSettings()
        end
        RefreshOverlayByKey(key)
    end)
    b:Slider("Width", 200, 600, 5, s.width or 380, function(v)
        MidnightUISettings.PlayerFrame.width = math.floor(v)
        if _G.MidnightUI_Settings and _G.MidnightUI_Settings.ApplyPlayerSettings then
            _G.MidnightUI_Settings.ApplyPlayerSettings()
        end
        RefreshOverlayByKey(key)
    end)
    b:Slider("Height", 50, 150, 2, s.height or 66, function(v)
        MidnightUISettings.PlayerFrame.height = math.floor(v)
        if _G.MidnightUI_Settings and _G.MidnightUI_Settings.ApplyPlayerSettings then
            _G.MidnightUI_Settings.ApplyPlayerSettings()
        end
        RefreshOverlayByKey(key)
    end)
    b:Slider("Opacity", 0.1, 1.0, 0.05, s.alpha or 0.95, function(v)
        MidnightUISettings.PlayerFrame.alpha = v
        if _G.MidnightUI_Settings and _G.MidnightUI_Settings.ApplyPlayerSettings then
            _G.MidnightUI_Settings.ApplyPlayerSettings()
        end
        RefreshOverlayByKey(key)
    end)
    return b:Height()
end

local function BuildPlayerAurasOverlaySettings(content, key)
    if not _G.MidnightUI_CreateOverlayBuilder then return end
    local s = (MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.auras) or {}
    local b = _G.MidnightUI_CreateOverlayBuilder(content, { startY = -6 })
    b:Header("Auras")
    b:Checkbox("Show Buff Bar", s.enabled ~= false, function(v)
        MidnightUISettings.PlayerFrame.auras.enabled = v
        if _G.MidnightUI_ApplyPlayerAuraSettings then _G.MidnightUI_ApplyPlayerAuraSettings() end
        RefreshOverlayByKey(key)
    end)
    b:Slider("Scale %", 50, 200, 5, s.scale or 100, function(v)
        MidnightUISettings.PlayerFrame.auras.scale = math.floor(v)
        if _G.MidnightUI_ApplyPlayerAuraSettings then _G.MidnightUI_ApplyPlayerAuraSettings() end
        RefreshOverlayByKey(key)
    end)
    b:Slider("Opacity", 0.1, 1.0, 0.05, s.alpha or 1.0, function(v)
        MidnightUISettings.PlayerFrame.auras.alpha = v
        if _G.MidnightUI_ApplyPlayerAuraSettings then _G.MidnightUI_ApplyPlayerAuraSettings() end
        RefreshOverlayByKey(key)
    end)
    b:Slider("Max Shown", 1, 32, 1, s.maxShown or 32, function(v)
        MidnightUISettings.PlayerFrame.auras.maxShown = math.floor(v + 0.5)
        if _G.MidnightUI_ApplyPlayerAuraSettings then _G.MidnightUI_ApplyPlayerAuraSettings() end
        if BuffFrame and BuffFrame.dragOverlay then
            UpdateAuraEditPreview(BuffFrame.dragOverlay, math.floor(v + 0.5))
        end
        RefreshOverlayByKey(key)
    end)
    b:Slider("Icons Per Row", 1, 32, 1, s.perRow or 16, function(v)
        MidnightUISettings.PlayerFrame.auras.perRow = math.floor(v + 0.5)
        if _G.MidnightUI_ApplyPlayerAuraSettings then _G.MidnightUI_ApplyPlayerAuraSettings() end
        if BuffFrame and BuffFrame.dragOverlay then
            UpdateAuraEditPreview(BuffFrame.dragOverlay, MidnightUISettings.PlayerFrame.auras.maxShown)
        end
        RefreshOverlayByKey(key)
    end)
    b:Dropdown("Alignment", {"Left", "Center", "Right"}, s.alignment or "Right", function(v)
        MidnightUISettings.PlayerFrame.auras.alignment = v
        if _G.MidnightUI_ApplyPlayerAuraSettings then _G.MidnightUI_ApplyPlayerAuraSettings() end
        if BuffFrame and BuffFrame.dragOverlay then
            UpdateAuraEditPreview(BuffFrame.dragOverlay, MidnightUISettings.PlayerFrame.auras.maxShown)
        end
        RefreshOverlayByKey(key)
    end)
    return b:Height()
end

local function BuildPlayerDebuffsOverlaySettings(content, key)
    if not _G.MidnightUI_CreateOverlayBuilder then return end
    local s = (MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.debuffs) or {}
    local b = _G.MidnightUI_CreateOverlayBuilder(content, { startY = -6 })
    b:Header("Debuffs")
    b:Checkbox("Show Debuff Bar", s.enabled ~= false, function(v)
        MidnightUISettings.PlayerFrame.debuffs.enabled = v
        if _G.MidnightUI_ApplyPlayerDebuffSettings then _G.MidnightUI_ApplyPlayerDebuffSettings() end
        RefreshOverlayByKey(key)
    end)
    b:Slider("Scale %", 50, 200, 5, s.scale or 100, function(v)
        MidnightUISettings.PlayerFrame.debuffs.scale = math.floor(v)
        if _G.MidnightUI_ApplyPlayerDebuffSettings then _G.MidnightUI_ApplyPlayerDebuffSettings() end
        RefreshOverlayByKey(key)
    end)
    b:Slider("Opacity", 0.1, 1.0, 0.05, s.alpha or 1.0, function(v)
        MidnightUISettings.PlayerFrame.debuffs.alpha = v
        if _G.MidnightUI_ApplyPlayerDebuffSettings then _G.MidnightUI_ApplyPlayerDebuffSettings() end
        RefreshOverlayByKey(key)
    end)
    b:Slider("Max Shown", 1, 16, 1, s.maxShown or 16, function(v)
        MidnightUISettings.PlayerFrame.debuffs.maxShown = math.floor(v + 0.5)
        if _G.MidnightUI_ApplyPlayerDebuffSettings then _G.MidnightUI_ApplyPlayerDebuffSettings() end
        if DebuffFrame and DebuffFrame.dragOverlay then
            UpdateAuraEditPreview(DebuffFrame.dragOverlay, math.floor(v + 0.5))
        end
        RefreshOverlayByKey(key)
    end)
    b:Slider("Icons Per Row", 1, 16, 1, s.perRow or 16, function(v)
        MidnightUISettings.PlayerFrame.debuffs.perRow = math.floor(v + 0.5)
        if _G.MidnightUI_ApplyPlayerDebuffSettings then _G.MidnightUI_ApplyPlayerDebuffSettings() end
        if DebuffFrame and DebuffFrame.dragOverlay then
            UpdateAuraEditPreview(DebuffFrame.dragOverlay, MidnightUISettings.PlayerFrame.debuffs.maxShown)
        end
        RefreshOverlayByKey(key)
    end)
    b:Dropdown("Alignment", {"Left", "Center", "Right"}, s.alignment or "Right", function(v)
        MidnightUISettings.PlayerFrame.debuffs.alignment = v
        if _G.MidnightUI_ApplyPlayerDebuffSettings then _G.MidnightUI_ApplyPlayerDebuffSettings() end
        if DebuffFrame and DebuffFrame.dragOverlay then
            UpdateAuraEditPreview(DebuffFrame.dragOverlay, MidnightUISettings.PlayerFrame.debuffs.maxShown)
        end
        RefreshOverlayByKey(key)
    end)
    return b:Height()
end

if _G.MidnightUI_RegisterOverlaySettings then
    _G.MidnightUI_RegisterOverlaySettings("PlayerFrame", { title = "Player Frame", build = BuildPlayerFrameOverlaySettings })
    _G.MidnightUI_RegisterOverlaySettings("PlayerAuras", { title = "Player Auras", build = BuildPlayerAurasOverlaySettings })
    _G.MidnightUI_RegisterOverlaySettings("PlayerDebuffs", { title = "Player Debuffs", build = BuildPlayerDebuffsOverlaySettings })
end

local function InitDebuffFrame(playerFrame)
    if not DebuffFrame then return end

    local debuffSettings = (MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.debuffs) or config.debuffs
    
    DebuffFrame:SetMovable(true)
    DebuffFrame:SetClampedToScreen(true)
    DebuffFrame:SetSize(GetPlayerAuraOverlaySize("debuffs"))
    
    DebuffFrame:SetScale((debuffSettings.scale or 100) / 100)
    DebuffFrame:SetAlpha(debuffSettings.alpha or 1.0)
    ApplyPlayerAuraDisplayLimits()
    
    ApplyAuraFrameAlignment(DebuffFrame, debuffSettings.alignment or "Right")
    
    local function ApplyDebuffPos()
        local MR = _G.MidnightUI_MoverRegistry
        if MR and MR.ApplyPosition then MR.ApplyPosition("PlayerDebuffs") end
    end
    ApplyDebuffPos()
    C_Timer.After(0.1, ApplyDebuffPos)
    C_Timer.After(0.5, ApplyDebuffPos)
    
    if debuffSettings.enabled == false then
        DebuffFrame:Hide()
    else
        DebuffFrame:Show()
    end

end


local function UpdateConditionBorder(frame)
    local CB = _G.MidnightUI_ConditionBorder
    if CB and CB.Update then CB.Update(frame) end
end


local function UpdatePlayerDebuffAlertVisual()
    local frame = playerFrame or _G["MidnightUI_PlayerFrame"]
    if not frame then return end

    local iconFrame = frame.debuffAlert
    local border = frame.debuffBorder
    local outline = frame.debuffOutline
    if border then border:Hide() end
    if outline then outline:Hide() end
    if iconFrame then iconFrame:Hide() end
    if frame.SetBackdropBorderColor then
        frame:SetBackdropBorderColor(0, 0, 0, 1)
    end

    UpdateConditionBorder(frame)
end


local playerFrame
local debuffCombatTicker

local function IsPlayerEventUnit(unitToken)
    if type(unitToken) ~= "string" then
        return false
    end
    if issecretvalue and issecretvalue(unitToken) then
        return false
    end
    return unitToken == "player"
end

function MidnightUI_RefreshPlayerFrame()
    local frame = playerFrame or _G["MidnightUI_PlayerFrame"]
    if frame and frame:IsShown() then
        UpdateAll(frame)
        UpdatePlayerDebuffAlertVisual()
    end
end

local function StartDebuffCombatRefresh()
    if debuffCombatTicker then return end
    if not C_Timer or not C_Timer.NewTicker then return end
    debuffCombatTicker = C_Timer.NewTicker(0.15, function()
        local frame = playerFrame or _G["MidnightUI_PlayerFrame"]
        if frame and frame:IsShown() then
            UpdatePlayerDebuffAlertVisual()
        end
    end)
end

local function StopDebuffCombatRefresh()
    if not debuffCombatTicker then return end
    debuffCombatTicker:Cancel()
    debuffCombatTicker = nil
end

PlayerFrameManager:RegisterEvent("ADDON_LOADED")
PlayerFrameManager:RegisterEvent("PLAYER_ENTERING_WORLD")
PlayerFrameManager:RegisterUnitEvent("UNIT_AURA", "player")
PlayerFrameManager:RegisterUnitEvent("UNIT_HEALTH", "player")
PlayerFrameManager:RegisterUnitEvent("UNIT_MAXHEALTH", "player")
PlayerFrameManager:RegisterUnitEvent("UNIT_POWER_UPDATE", "player")
PlayerFrameManager:RegisterUnitEvent("UNIT_MAXPOWER", "player")
PlayerFrameManager:RegisterUnitEvent("UNIT_DISPLAYPOWER", "player")
PlayerFrameManager:RegisterUnitEvent("UNIT_ABSORB_AMOUNT_CHANGED", "player")
PlayerFrameManager:RegisterUnitEvent("UNIT_HEAL_PREDICTION", "player")
PlayerFrameManager:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
PlayerFrameManager:RegisterEvent("INCOMING_RESURRECT_CHANGED")
PlayerFrameManager:RegisterEvent("PLAYER_DEAD")
PlayerFrameManager:RegisterEvent("PLAYER_ALIVE")
PlayerFrameManager:RegisterEvent("PLAYER_UNGHOST")
PlayerFrameManager:RegisterEvent("PLAYER_REGEN_DISABLED")
PlayerFrameManager:RegisterEvent("PLAYER_REGEN_ENABLED")

PlayerFrameManager:SetScript("OnEvent", function(self, event, ...)
    local arg1 = ...

    if event == "ADDON_LOADED" then
        if arg1 == ADDON_NAME then
            if PlayerFrame then
                if not (MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.enabled == false) then
                    PlayerFrame:SetAlpha(0)
                    PlayerFrame:EnableMouse(false)
                end
            end
        end

    elseif event == "PLAYER_ENTERING_WORLD" then
        InitializeAuraSettings()
        
        if not playerFrame then
            playerFrame = CreatePlayerFrame()
        end

        if MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.enabled == false then
            playerFrame:Hide()
            playerFrame:SetAlpha(0)
            if PlayerFrame then
                PlayerFrame:SetAlpha(1)
                PlayerFrame:EnableMouse(true)
            end
            return
        end

        playerFrame:Show()
        UpdateAll(playerFrame)
        UpdatePlayerDebuffAlertVisual()
        if InCombatLockdown and InCombatLockdown() then
            StartDebuffCombatRefresh()
        else
            StopDebuffCombatRefresh()
        end
        if _G.MidnightUI_AttachPlayerCastBar then
            _G.MidnightUI_AttachPlayerCastBar(playerFrame)
        end
        InitBuffFrame(playerFrame)
        InitDebuffFrame(playerFrame)

        local MR = _G.MidnightUI_MoverRegistry
        if MR and MR.RegisterMover then
            MR.RegisterMover("PlayerFrame", playerFrame, "BOTTOM,UIParent,BOTTOM,-280.00,240.67", {
                category = "unit",
                label = "Player Frame",
                onLock = function(f)
                    f:EnableMouse(true)
                    if not UnitExists("player") then f:Hide() end
                end,
                onUnlock = function(f)
                    f:Show()
                    f:EnableMouse(true)
                end,
            })
            if BuffFrame then
                MR.RegisterMover("PlayerAuras", BuffFrame, "CENTER,UIParent,CENTER,-369.95,-255.06", {
                    category = "auras",
                    label = "Player Buffs",
                    shouldShowOverlay = function()
                        local as = MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.auras
                        return not (as and as.enabled == false)
                    end,
                    onUnlock = function(f)
                        f:SetSize(GetPlayerAuraOverlaySize("auras"))
                        f:Show()
                        local as = MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.auras
                        if f.dragOverlay then
                            UpdateAuraEditPreview(f.dragOverlay, as and as.maxShown or 32)
                        end
                    end,
                })
            end
            if DebuffFrame then
                MR.RegisterMover("PlayerDebuffs", DebuffFrame, "BOTTOM,UIParent,BOTTOM,-449.51,193.03", {
                    category = "auras",
                    label = "Player Debuffs",
                    shouldShowOverlay = function()
                        local ds = MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.debuffs
                        return not (ds and ds.enabled == false)
                    end,
                    onUnlock = function(f)
                        f:SetSize(GetPlayerAuraOverlaySize("debuffs"))
                        f:Show()
                        local ds = MidnightUISettings and MidnightUISettings.PlayerFrame and MidnightUISettings.PlayerFrame.debuffs
                        if f.dragOverlay then
                            UpdateAuraEditPreview(f.dragOverlay, ds and ds.maxShown or 16)
                        end
                    end,
                })
            end
        end

        _G.MidnightUI_InitSoulShards(playerFrame)
        _G.MidnightUI_InitComboPoints(playerFrame)
        _G.MidnightUI_InitDKRunes(playerFrame)
        if _G.MidnightUI_InitMonkChi then _G.MidnightUI_InitMonkChi(playerFrame) end
        if _G.MidnightUI_InitMonkStagger then _G.MidnightUI_InitMonkStagger(playerFrame) end
        if _G.MidnightUI_InitHolyPower then _G.MidnightUI_InitHolyPower(playerFrame) end
        if _G.MidnightUI_InitArcaneCharges then _G.MidnightUI_InitArcaneCharges(playerFrame) end
        if _G.MidnightUI_InitEvokerEssence then _G.MidnightUI_InitEvokerEssence(playerFrame) end

    elseif event == "UNIT_AURA" then
        if playerFrame and playerFrame:IsShown() then
            UpdatePlayerDebuffAlertVisual()
        end

    elseif event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then
        if playerFrame and playerFrame:IsShown() then
            UpdateHealth(playerFrame)
            UpdateAbsorbs(playerFrame)
        end

    elseif event == "UNIT_POWER_UPDATE" or event == "UNIT_MAXPOWER" or event == "UNIT_DISPLAYPOWER" then
        if playerFrame and playerFrame:IsShown() then
            UpdatePower(playerFrame)
        end

    elseif event == "UNIT_ABSORB_AMOUNT_CHANGED" or event == "UNIT_HEAL_PREDICTION" then
        if playerFrame and playerFrame:IsShown() then
            UpdateAbsorbs(playerFrame)
        end
    elseif event == "PLAYER_SPECIALIZATION_CHANGED" then
        if playerFrame and playerFrame:IsShown() then
            UpdatePlayerDebuffAlertVisual()
        end

    elseif event == "INCOMING_RESURRECT_CHANGED" or event == "PLAYER_DEAD" or event == "PLAYER_ALIVE" or event == "PLAYER_UNGHOST" then
        if playerFrame and playerFrame:IsShown() then
            if not arg1 or IsPlayerEventUnit(arg1) then
                UpdateAll(playerFrame)
                UpdatePlayerDebuffAlertVisual()
            end
        end
    elseif event == "PLAYER_REGEN_DISABLED" then
        StartDebuffCombatRefresh()
        if playerFrame and playerFrame:IsShown() then
            UpdatePlayerDebuffAlertVisual()
        end
    elseif event == "PLAYER_REGEN_ENABLED" then
        StopDebuffCombatRefresh()
        if playerFrame and playerFrame:IsShown() then
            UpdatePlayerDebuffAlertVisual()
        end
    end
end)

-- ============================================================
-- SECTION: Class-Specific Overlays
-- Creates secondary resource overlays that sit on top of the
-- player frame for classes with non-standard power meters:
--   Warlock — Soul Shards bar (orange pips or continuous bar)
--   Monk    — Chi orbs row
--   Paladin — Holy Power pips
--   Druid   — Combo Points / Astral Power overlay
--   Rogue/DH — Combo Points / Fury secondary strip
-- Each overlay is created lazily on PLAYER_LOGIN once the
-- player class is known, then resized/repositioned whenever
-- the player frame layout changes.
--
-- READS:  UnitClass("player"), Settings.playerFrame.classOverlay.*,
--         playerFrame dimensions
-- WRITES: soulShardsContainer, chiContainer, holyPowerContainer,
--         druiComboContainer, genericSecondaryOverlay (module-local
--         upvalues), MoverRegistry overlay entries
--
-- FUNCTIONS:
--   EnsureSoulShardsSettings() — creates/configures Warlock overlay
--   EnsureChiSettings() — creates Monk Chi orb row
--   EnsureHolyPowerSettings() — creates Paladin Holy Power pips
--   EnsureClassOverlay() — dispatcher: checks UnitClass and calls
--     the appropriate Ensure* function
--   ApplyClassOverlayLayout() — repositions all active overlays
--     after frame resize or setting change
--   UpdateClassOverlay() — refreshes pip states / fill levels
--     on UNIT_POWER_UPDATE for "player"
-- ============================================================
local soulShardsContainer = nil

local function EnsureSoulShardsSettings()
    if not MidnightUISettings then return nil end
    if not MidnightUISettings.SoulShards then
        MidnightUISettings.SoulShards = {}
    end
    local s = MidnightUISettings.SoulShards
    if s.enabled == nil then s.enabled = true end
    if not s.scale then s.scale = 100 end
    if not s.alpha then s.alpha = 1.0 end
    return s
end

local function ApplySoulShardsPosition()
    if not soulShardsContainer then return end
    local s = EnsureSoulShardsSettings()
    if not s then return end
    soulShardsContainer:SetScale((s.scale or 100) / 100)
    soulShardsContainer:SetAlpha(s.alpha or 1.0)
    if s.position and _G.MidnightUI_ApplyOverlayPosition then
        _G.MidnightUI_ApplyOverlayPosition(soulShardsContainer, s.position)
    end
end

local function EnsureSoulShardsOverlay()
    if not soulShardsContainer or soulShardsContainer.dragOverlay then return end

    local overlay = CreateFrame("Frame", nil, soulShardsContainer, "BackdropTemplate")
    overlay:SetAllPoints()
    overlay:SetFrameStrata("HIGH")
    overlay:SetFrameLevel((soulShardsContainer:GetFrameLevel() or 0) + 10)

    if _G.MidnightUI_StyleOverlay then
        _G.MidnightUI_StyleOverlay(overlay, nil, nil, "unit")
    else
        overlay:SetBackdrop({
            bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            edgeSize = 1,
        })
        overlay:SetBackdropColor(0.05, 0.08, 0.11, 0.30)
        overlay:SetBackdropBorderColor(0.30, 0.46, 0.58, 0.78)
    end

    local label = overlay:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    label:SetPoint("CENTER")
    label:SetText("SOUL SHARDS")
    label:SetTextColor(1, 1, 1)

    overlay:EnableMouse(true)
    overlay:RegisterForDrag("LeftButton")
    overlay:SetScript("OnDragStart", function()
        if InCombatLockdown and InCombatLockdown() then return end
        soulShardsContainer._muiIsDragging = true
        soulShardsContainer:StartMoving()
    end)
    overlay:SetScript("OnDragStop", function()
        if not soulShardsContainer._muiIsDragging then return end
        soulShardsContainer:StopMovingOrSizing()
        soulShardsContainer._muiIsDragging = false
        local s = EnsureSoulShardsSettings()
        if s and _G.MidnightUI_SaveOverlayPosition then
            _G.MidnightUI_SaveOverlayPosition(soulShardsContainer, s)
        end
        local MR = _G.MidnightUI_MoverRegistry
        if MR and MR.SavePosition then MR.SavePosition("SoulShards") end
    end)

    if _G.MidnightUI_AttachOverlaySettings then
        _G.MidnightUI_AttachOverlaySettings(overlay, "SoulShards")
    end

    soulShardsContainer.dragOverlay = overlay
end

local function ApplySoulShardsLockState(locked)
    if not soulShardsContainer then return end
    local s = EnsureSoulShardsSettings()
    if not s or s.enabled == false then
        soulShardsContainer.forceShow = false
        if soulShardsContainer.dragOverlay then soulShardsContainer.dragOverlay:Hide() end
        soulShardsContainer:SetAlpha(0)
        soulShardsContainer:EnableMouse(false)
        return
    end

    soulShardsContainer.forceShow = not locked
    ApplySoulShardsPosition()

    if locked then
        if soulShardsContainer.dragOverlay then soulShardsContainer.dragOverlay:Hide() end
        soulShardsContainer:EnableMouse(false)
    else
        EnsureSoulShardsOverlay()
        if soulShardsContainer.dragOverlay then soulShardsContainer.dragOverlay:Show() end
        soulShardsContainer:EnableMouse(true)
    end
    soulShardsContainer:Show()
    _G.MidnightUI_ApplySoulShardsSettings()
end

local function CreateSoulShardsContainer(pFrame)
    if soulShardsContainer then return end

    local wpf = _G.WarlockPowerFrame
    if not wpf then return end

    if wpf.requiredShownLevel then
        wpf.requiredShownLevel = 1
    end

    if wpf.Setup then
        pcall(wpf.Setup, wpf)
    end

    local container = CreateFrame("Frame", "MidnightUI_SoulShardsContainer", UIParent)
    container:SetSize(200, 30)
    container:SetMovable(true)
    container:SetClampedToScreen(true)
    container:SetFrameStrata(pFrame:GetFrameStrata())
    container:SetFrameLevel(pFrame:GetFrameLevel() + 10)

    local s = EnsureSoulShardsSettings()
    if s then
        container:SetScale((s.scale or 100) / 100)
        container:SetAlpha(s.alpha or 1.0)
    end
    if s and s.position and _G.MidnightUI_ApplyOverlayPosition then
        _G.MidnightUI_ApplyOverlayPosition(container, s.position)
    else
        container:SetPoint("TOPRIGHT", pFrame.powerContainer, "BOTTOMRIGHT", 0, -2)
    end

    wpf:SetParent(container)
    wpf:ClearAllPoints()
    wpf:SetPoint("CENTER", container, "CENTER", 0, 0)
    wpf:SetScale(1)
    wpf:SetAlpha(1)
    wpf:Show()

    if wpf.UpdatePower then
        pcall(wpf.UpdatePower, wpf)
    end

    soulShardsContainer = container

    container:Show()
    if s and s.enabled == false then
        container:SetAlpha(0)
        container:EnableMouse(false)
    end
end

function _G.MidnightUI_InitSoulShards(pFrame)
    if not pFrame or pFrame._muiSoulShardsAttached then return end
    local _, playerClass = UnitClass("player")
    if playerClass ~= "WARLOCK" then return end

    CreateSoulShardsContainer(pFrame)
    pFrame._muiSoulShardsAttached = true
end

function _G.MidnightUI_SetSoulShardsLocked(locked)
    local _, playerClass = UnitClass("player")
    if playerClass ~= "WARLOCK" then return end
    if not soulShardsContainer and playerFrame then
        CreateSoulShardsContainer(playerFrame)
    end
    ApplySoulShardsLockState(locked)
end

do
    local MR = _G.MidnightUI_MoverRegistry
    if MR and MR.DefineMover then
        MR.DefineMover("SoulShards", {
            resolveFrame = function()
                local _, pc = UnitClass("player")
                if pc ~= "WARLOCK" then return nil end
                return soulShardsContainer
            end,
            category = "unit", label = "Soul Shards",
            externalOverlay = true,
            onLock = function() ApplySoulShardsLockState(true) end,
            onUnlock = function() ApplySoulShardsLockState(false) end,
        })
    end
end

function _G.MidnightUI_ApplySoulShardsSettings()
    local _, playerClass = UnitClass("player")
    if playerClass ~= "WARLOCK" then return end
    if not soulShardsContainer then return end
    local s = EnsureSoulShardsSettings()
    if not s then return end
    if s.enabled == false then
        soulShardsContainer:SetAlpha(0)
        soulShardsContainer:EnableMouse(false)
    else
        soulShardsContainer:SetScale((s.scale or 100) / 100)
        soulShardsContainer:SetAlpha(s.alpha or 1.0)
        soulShardsContainer:EnableMouse(true)
        soulShardsContainer:Show()
    end
end

local function BuildSoulShardsOverlaySettings(content, key)
    if not _G.MidnightUI_CreateOverlayBuilder then return end
    local s = EnsureSoulShardsSettings()
    if not s then return end

    local b = _G.MidnightUI_CreateOverlayBuilder(content, { startY = -6 })
    b:Header("Soul Shards")
    b:Checkbox("Enable", s.enabled ~= false, function(v)
        s.enabled = v
        _G.MidnightUI_ApplySoulShardsSettings()
    end)
    b:Slider("Scale %", 50, 200, 5, s.scale or 100, function(v)
        s.scale = math.floor(v)
        _G.MidnightUI_ApplySoulShardsSettings()
    end)
    b:Slider("Opacity", 0, 100, 5, math.floor((s.alpha or 1.0) * 100), function(v)
        s.alpha = v / 100
        _G.MidnightUI_ApplySoulShardsSettings()
    end)

    return b:Height()
end

if _G.MidnightUI_RegisterOverlaySettings then
    _G.MidnightUI_RegisterOverlaySettings("SoulShards", {
        title = "Soul Shards",
        build = BuildSoulShardsOverlaySettings,
    })
end


local comboPointsContainer
local comboPointPips = {}

local COMBO_COLORS = {
    [1] = { 0.90, 0.80, 0.20 },
    [2] = { 0.95, 0.65, 0.15 },
    [3] = { 1.00, 0.50, 0.10 },
    [4] = { 1.00, 0.35, 0.08 },
    [5] = { 1.00, 0.20, 0.10 },
    [6] = { 0.80, 0.10, 0.90 },
    [7] = { 0.65, 0.05, 1.00 },
}

local function EnsureComboPointsSettings()
    if not MidnightUISettings then return nil end
    if not MidnightUISettings.ComboPoints then
        MidnightUISettings.ComboPoints = {}
    end
    local s = MidnightUISettings.ComboPoints
    if s.enabled == nil then s.enabled = true end
    if not s.scale then s.scale = 100 end
    if not s.alpha then s.alpha = 1.0 end
    return s
end

local function ApplyComboPointsPosition()
    if not comboPointsContainer then return end
    local s = EnsureComboPointsSettings()
    if not s then return end
    comboPointsContainer:SetScale((s.scale or 100) / 100)
    comboPointsContainer:SetAlpha(s.alpha or 1.0)
    local MR = _G.MidnightUI_MoverRegistry
    if MR and MR.GetPosition and MR.ApplyPosition and MR.GetPosition("ComboPoints") then
        MR.ApplyPosition("ComboPoints")
    elseif s.position and _G.MidnightUI_ApplyOverlayPosition then
        _G.MidnightUI_ApplyOverlayPosition(comboPointsContainer, s.position)
    end
end

local COMBO_PIP_SIZE = 14
local COMBO_PIP_SPACING = 4

local function UpdateComboPoints()
    if not comboPointsContainer then return end
    local s = EnsureComboPointsSettings()
    if s and s.enabled == false then return end

    local cur = UnitPower("player", Enum.PowerType.ComboPoints) or 0
    local maxPts = UnitPowerMax("player", Enum.PowerType.ComboPoints) or 5

    local _, playerClass = UnitClass("player")
    local comboAvailable = true
    if playerClass == "DRUID" then
        comboAvailable = (GetShapeshiftFormID and GetShapeshiftFormID() == 1)
    end
    if not comboAvailable and not comboPointsContainer.forceShow then
        comboPointsContainer:Hide()
        return
    end
    comboPointsContainer:Show()

    local containerWidth = (COMBO_PIP_SIZE + COMBO_PIP_SPACING) * maxPts - COMBO_PIP_SPACING + 8
    comboPointsContainer:SetWidth(containerWidth)

    for i = 1, #comboPointPips do
        local pip = comboPointPips[i]
        if i <= maxPts then
            pip:Show()
            if i <= cur then
                local c = COMBO_COLORS[i] or COMBO_COLORS[5]
                pip.fill:SetVertexColor(c[1], c[2], c[3], 1)
                pip.fill:Show()
                pip.glow:SetVertexColor(c[1], c[2], c[3], 0.5)
                pip.glow:Show()
            else
                pip.fill:Hide()
                pip.glow:Hide()
            end
        else
            pip:Hide()
        end
    end
end

local function CreateComboPointsContainer(pFrame)
    if comboPointsContainer then return end

    local MAX_PIPS = 7
    local PIP_SIZE = COMBO_PIP_SIZE
    local PIP_SPACING = COMBO_PIP_SPACING

    local curMax = UnitPowerMax("player", Enum.PowerType.ComboPoints) or 5
    local container = CreateFrame("Frame", "MidnightUI_ComboPointsContainer", UIParent)
    container:SetSize((PIP_SIZE + PIP_SPACING) * curMax - PIP_SPACING + 8, PIP_SIZE + 8)
    container:SetMovable(true)
    container:SetClampedToScreen(true)
    container:SetFrameStrata(pFrame:GetFrameStrata())
    container:SetFrameLevel(pFrame:GetFrameLevel() + 10)

    local bg = container:CreateTexture(nil, "BACKGROUND")
    bg:SetAllPoints()
    bg:SetColorTexture(0, 0, 0, 0.55)

    for i = 1, MAX_PIPS do
        local pip = CreateFrame("Frame", nil, container)
        pip:SetSize(PIP_SIZE, PIP_SIZE)
        pip:SetPoint("LEFT", container, "LEFT", 4 + (i - 1) * (PIP_SIZE + PIP_SPACING), 0)

        local fill = pip:CreateTexture(nil, "ARTWORK")
        fill:SetAllPoints()
        fill:SetTexture("Interface\\Buttons\\WHITE8X8")
        fill:Hide()
        pip.fill = fill

        local glow = pip:CreateTexture(nil, "BACKGROUND", nil, 1)
        glow:SetPoint("TOPLEFT", -2, 2)
        glow:SetPoint("BOTTOMRIGHT", 2, -2)
        glow:SetTexture("Interface\\Buttons\\WHITE8X8")
        glow:SetBlendMode("ADD")
        glow:Hide()
        pip.glow = glow

        local empty = pip:CreateTexture(nil, "ARTWORK", nil, -1)
        empty:SetAllPoints()
        empty:SetColorTexture(0.15, 0.15, 0.15, 0.6)
        pip.empty = empty

        comboPointPips[i] = pip
    end

    local s = EnsureComboPointsSettings()
    local MR = _G.MidnightUI_MoverRegistry
    if s then
        container:SetScale((s.scale or 100) / 100)
        container:SetAlpha(s.alpha or 1.0)
    end

    -- Assign the module upvalue BEFORE position application so the mover
    -- registry's resolveFrame callback can locate the frame on first load.
    comboPointsContainer = container

    if MR and MR.GetPosition and MR.ApplyPosition and MR.GetPosition("ComboPoints") then
        MR.ApplyPosition("ComboPoints")
    elseif s and s.position and _G.MidnightUI_ApplyOverlayPosition then
        _G.MidnightUI_ApplyOverlayPosition(container, s.position)
    else
        container:SetPoint("TOPRIGHT", pFrame.powerContainer or pFrame, "BOTTOMRIGHT", 0, -2)
    end

    local evFrame = CreateFrame("Frame")
    evFrame:RegisterUnitEvent("UNIT_POWER_UPDATE", "player")
    evFrame:RegisterUnitEvent("UNIT_MAXPOWER", "player")
    evFrame:RegisterUnitEvent("UNIT_DISPLAYPOWER", "player")
    evFrame:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
    evFrame:RegisterEvent("PLAYER_TARGET_CHANGED")
    evFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
    evFrame:SetScript("OnEvent", UpdateComboPoints)

    container:Show()

    if s and s.enabled == false then
        container:SetAlpha(0)
        container:EnableMouse(false)
    end

    UpdateComboPoints()
end

local function EnsureComboPointsOverlay()
    if not comboPointsContainer or comboPointsContainer.dragOverlay then return end

    local overlay = CreateFrame("Frame", nil, comboPointsContainer, "BackdropTemplate")
    overlay:SetAllPoints()
    overlay:SetFrameStrata("HIGH")
    overlay:SetFrameLevel((comboPointsContainer:GetFrameLevel() or 0) + 10)

    if _G.MidnightUI_StyleOverlay then
        _G.MidnightUI_StyleOverlay(overlay, nil, nil, "unit")
    end

    local label = overlay:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    label:SetPoint("CENTER")
    label:SetText("COMBO POINTS")
    label:SetTextColor(1, 1, 1)

    overlay:EnableMouse(true)
    overlay:RegisterForDrag("LeftButton")
    overlay:SetScript("OnDragStart", function()
        if InCombatLockdown and InCombatLockdown() then return end
        comboPointsContainer._muiIsDragging = true
        comboPointsContainer:StartMoving()
    end)
    overlay:SetScript("OnDragStop", function()
        if not comboPointsContainer._muiIsDragging then return end
        comboPointsContainer:StopMovingOrSizing()
        comboPointsContainer._muiIsDragging = false
        if _G.MidnightUI_EndFrameSnap then
            _G.MidnightUI_EndFrameSnap(comboPointsContainer)
        end
        local s = EnsureComboPointsSettings()
        if s and _G.MidnightUI_SaveOverlayPosition then
            _G.MidnightUI_SaveOverlayPosition(comboPointsContainer, s)
        end
        local MR = _G.MidnightUI_MoverRegistry
        if MR and MR.SavePosition then MR.SavePosition("ComboPoints") end
    end)

    if _G.MidnightUI_AttachOverlaySettings then
        _G.MidnightUI_AttachOverlaySettings(overlay, "ComboPoints")
    end

    comboPointsContainer.dragOverlay = overlay
end

local function ApplyComboPointsLockState(locked)
    if not comboPointsContainer then return end
    local s = EnsureComboPointsSettings()
    if not s or s.enabled == false then
        comboPointsContainer.forceShow = false
        if comboPointsContainer.dragOverlay then comboPointsContainer.dragOverlay:Hide() end
        comboPointsContainer:SetAlpha(0)
        comboPointsContainer:EnableMouse(false)
        return
    end

    comboPointsContainer.forceShow = not locked
    ApplyComboPointsPosition()

    if locked then
        if comboPointsContainer.dragOverlay then comboPointsContainer.dragOverlay:Hide() end
        comboPointsContainer:EnableMouse(false)
    else
        EnsureComboPointsOverlay()
        if comboPointsContainer.dragOverlay then comboPointsContainer.dragOverlay:Show() end
        comboPointsContainer:EnableMouse(true)
    end
    comboPointsContainer:Show()
    _G.MidnightUI_ApplyComboPointsSettings()
end

function _G.MidnightUI_InitComboPoints(pFrame)
    if not pFrame or pFrame._muiComboPointsAttached then return end
    local _, playerClass = UnitClass("player")
    if playerClass ~= "ROGUE" and playerClass ~= "DRUID" then return end

    CreateComboPointsContainer(pFrame)
    pFrame._muiComboPointsAttached = true
end

function _G.MidnightUI_SetComboPointsLocked(locked)
    local _, playerClass = UnitClass("player")
    if playerClass ~= "ROGUE" and playerClass ~= "DRUID" then return end
    if not comboPointsContainer and playerFrame then
        CreateComboPointsContainer(playerFrame)
    end
    ApplyComboPointsLockState(locked)
end

function _G.MidnightUI_ApplyComboPointsSettings()
    local _, playerClass = UnitClass("player")
    if playerClass ~= "ROGUE" and playerClass ~= "DRUID" then return end
    if not comboPointsContainer then return end
    local s = EnsureComboPointsSettings()
    if not s then return end
    if s.enabled == false then
        comboPointsContainer:SetAlpha(0)
        comboPointsContainer:EnableMouse(false)
    else
        comboPointsContainer:SetScale((s.scale or 100) / 100)
        comboPointsContainer:SetAlpha(s.alpha or 1.0)
        comboPointsContainer:EnableMouse(true)
        comboPointsContainer:Show()
        UpdateComboPoints()
    end
end

do
    local MR = _G.MidnightUI_MoverRegistry
    if MR and MR.DefineMover then
        MR.DefineMover("ComboPoints", {
            resolveFrame = function()
                local _, pc = UnitClass("player")
                if pc ~= "ROGUE" and pc ~= "DRUID" then return nil end
                return comboPointsContainer
            end,
            category = "unit", label = "Combo Points",
            externalOverlay = true,
            onLock = function() ApplyComboPointsLockState(true) end,
            onUnlock = function() ApplyComboPointsLockState(false) end,
        })
    end
end

local function BuildComboPointsOverlaySettings(content, key)
    if not _G.MidnightUI_CreateOverlayBuilder then return end
    local s = EnsureComboPointsSettings()
    if not s then return end

    local b = _G.MidnightUI_CreateOverlayBuilder(content, { startY = -6 })
    b:Header("Combo Points")
    b:Checkbox("Enable", s.enabled ~= false, function(v)
        s.enabled = v
        _G.MidnightUI_ApplyComboPointsSettings()
    end)
    b:Slider("Scale %", 50, 200, 5, s.scale or 100, function(v)
        s.scale = math.floor(v)
        _G.MidnightUI_ApplyComboPointsSettings()
    end)
    b:Slider("Opacity", 0, 100, 5, math.floor((s.alpha or 1.0) * 100), function(v)
        s.alpha = v / 100
        _G.MidnightUI_ApplyComboPointsSettings()
    end)

    return b:Height()
end

if _G.MidnightUI_RegisterOverlaySettings then
    _G.MidnightUI_RegisterOverlaySettings("ComboPoints", {
        title = "Combo Points",
        build = BuildComboPointsOverlaySettings,
    })
end


local dkRunesContainer

local function EnsureDKRunesSettings()
    if not MidnightUISettings then return nil end
    if not MidnightUISettings.DKRunes then
        MidnightUISettings.DKRunes = {}
    end
    local s = MidnightUISettings.DKRunes
    if s.enabled == nil then s.enabled = true end
    if not s.scale then s.scale = 100 end
    if not s.alpha then s.alpha = 1.0 end
    return s
end

local function ApplyDKRunesPosition()
    if not dkRunesContainer then return end
    local s = EnsureDKRunesSettings()
    if not s then return end
    dkRunesContainer:SetScale((s.scale or 100) / 100)
    dkRunesContainer:SetAlpha(s.alpha or 1.0)
    if s.position and _G.MidnightUI_ApplyOverlayPosition then
        _G.MidnightUI_ApplyOverlayPosition(dkRunesContainer, s.position)
    end
end

local function CreateDKRunesContainer(pFrame)
    if dkRunesContainer then return end

    local rf = _G.RuneFrame
    if not rf then return end

    if rf.requiredShownLevel then
        rf.requiredShownLevel = 1
    end

    if rf.Setup then
        pcall(rf.Setup, rf)
    end

    local container = CreateFrame("Frame", "MidnightUI_DKRunesContainer", UIParent)
    container:SetSize(200, 30)
    container:SetMovable(true)
    container:SetClampedToScreen(true)
    container:SetFrameStrata(pFrame:GetFrameStrata())
    container:SetFrameLevel(pFrame:GetFrameLevel() + 10)

    local s = EnsureDKRunesSettings()
    if s then
        container:SetScale((s.scale or 100) / 100)
        container:SetAlpha(s.alpha or 1.0)
    end
    if s and s.position and _G.MidnightUI_ApplyOverlayPosition then
        _G.MidnightUI_ApplyOverlayPosition(container, s.position)
    else
        container:SetPoint("TOPRIGHT", pFrame.powerContainer or pFrame, "BOTTOMRIGHT", 0, -2)
    end

    local function ReparentRuneFrame()
        if not rf or not container then return end
        if rf:GetParent() ~= container then
            pcall(rf.SetParent, rf, container)
        end
        rf:ClearAllPoints()
        rf:SetPoint("CENTER", container, "CENTER", 0, 0)
        rf:SetScale(1)
        rf:SetAlpha(1)
        if not rf:IsShown() then rf:Show() end
    end
    ReparentRuneFrame()

    if not rf._muiHookInstalled then
        rf._muiHookInstalled = true
        rf:HookScript("OnHide", function(self)
            if dkRunesContainer and not InCombatLockdown() then
                C_Timer.After(0, ReparentRuneFrame)
            end
        end)
        if rf.SetPoint then
            hooksecurefunc(rf, "SetPoint", function(self, _, parent)
                if dkRunesContainer and parent ~= dkRunesContainer
                    and not self._muiAnchorGuard then
                    self._muiAnchorGuard = true
                    C_Timer.After(0, function()
                        ReparentRuneFrame()
                        self._muiAnchorGuard = nil
                    end)
                end
            end)
        end
    end

    if rf.UpdatePower then
        pcall(rf.UpdatePower, rf)
    end

    dkRunesContainer = container

    container:Show()
    if s and s.enabled == false then
        container:SetAlpha(0)
        container:EnableMouse(false)
    end
end

local function EnsureDKRunesOverlay()
    if not dkRunesContainer or dkRunesContainer.dragOverlay then return end

    local overlay = CreateFrame("Frame", nil, dkRunesContainer, "BackdropTemplate")
    overlay:SetAllPoints()
    overlay:SetFrameStrata("HIGH")
    overlay:SetFrameLevel((dkRunesContainer:GetFrameLevel() or 0) + 10)

    if _G.MidnightUI_StyleOverlay then
        _G.MidnightUI_StyleOverlay(overlay, nil, nil, "unit")
    end

    local label = overlay:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    label:SetPoint("CENTER")
    label:SetText("DK RUNES")
    label:SetTextColor(1, 1, 1)

    overlay:EnableMouse(true)
    overlay:RegisterForDrag("LeftButton")
    overlay:SetScript("OnDragStart", function()
        if InCombatLockdown and InCombatLockdown() then return end
        dkRunesContainer._muiIsDragging = true
        dkRunesContainer:StartMoving()
    end)
    overlay:SetScript("OnDragStop", function()
        if not dkRunesContainer._muiIsDragging then return end
        dkRunesContainer:StopMovingOrSizing()
        dkRunesContainer._muiIsDragging = false
        local s = EnsureDKRunesSettings()
        if s and _G.MidnightUI_SaveOverlayPosition then
            _G.MidnightUI_SaveOverlayPosition(dkRunesContainer, s)
        end
        local MR = _G.MidnightUI_MoverRegistry
        if MR and MR.SavePosition then MR.SavePosition("DKRunes") end
    end)

    if _G.MidnightUI_AttachOverlaySettings then
        _G.MidnightUI_AttachOverlaySettings(overlay, "DKRunes")
    end

    dkRunesContainer.dragOverlay = overlay
end

local function ApplyDKRunesLockState(locked)
    if not dkRunesContainer then return end
    local s = EnsureDKRunesSettings()
    if not s or s.enabled == false then
        dkRunesContainer.forceShow = false
        if dkRunesContainer.dragOverlay then dkRunesContainer.dragOverlay:Hide() end
        dkRunesContainer:SetAlpha(0)
        dkRunesContainer:EnableMouse(false)
        return
    end

    dkRunesContainer.forceShow = not locked
    ApplyDKRunesPosition()

    if locked then
        if dkRunesContainer.dragOverlay then dkRunesContainer.dragOverlay:Hide() end
        dkRunesContainer:EnableMouse(false)
    else
        EnsureDKRunesOverlay()
        if dkRunesContainer.dragOverlay then dkRunesContainer.dragOverlay:Show() end
        dkRunesContainer:EnableMouse(true)
    end
    dkRunesContainer:Show()
    _G.MidnightUI_ApplyDKRunesSettings()
end

function _G.MidnightUI_InitDKRunes(pFrame)
    if not pFrame or pFrame._muiDKRunesAttached then return end
    local _, playerClass = UnitClass("player")
    if playerClass ~= "DEATHKNIGHT" then return end

    CreateDKRunesContainer(pFrame)
    pFrame._muiDKRunesAttached = true
end

function _G.MidnightUI_SetDKRunesLocked(locked)
    local _, playerClass = UnitClass("player")
    if playerClass ~= "DEATHKNIGHT" then return end
    if not dkRunesContainer and playerFrame then
        CreateDKRunesContainer(playerFrame)
    end
    ApplyDKRunesLockState(locked)
end

function _G.MidnightUI_ApplyDKRunesSettings()
    local _, playerClass = UnitClass("player")
    if playerClass ~= "DEATHKNIGHT" then return end
    if not dkRunesContainer then return end
    local s = EnsureDKRunesSettings()
    if not s then return end
    if s.enabled == false then
        dkRunesContainer:SetAlpha(0)
        dkRunesContainer:EnableMouse(false)
    else
        dkRunesContainer:SetScale((s.scale or 100) / 100)
        dkRunesContainer:SetAlpha(s.alpha or 1.0)
        dkRunesContainer:EnableMouse(true)
        dkRunesContainer:Show()
    end
end

do
    local MR = _G.MidnightUI_MoverRegistry
    if MR and MR.DefineMover then
        MR.DefineMover("DKRunes", {
            resolveFrame = function()
                local _, pc = UnitClass("player")
                if pc ~= "DEATHKNIGHT" then return nil end
                return dkRunesContainer
            end,
            category = "unit", label = "DK Runes",
            externalOverlay = true,
            onLock = function() ApplyDKRunesLockState(true) end,
            onUnlock = function() ApplyDKRunesLockState(false) end,
        })
    end
end

local function BuildDKRunesOverlaySettings(content, key)
    if not _G.MidnightUI_CreateOverlayBuilder then return end
    local s = EnsureDKRunesSettings()
    if not s then return end

    local b = _G.MidnightUI_CreateOverlayBuilder(content, { startY = -6 })
    b:Header("DK Runes")
    b:Checkbox("Enable", s.enabled ~= false, function(v)
        s.enabled = v
        _G.MidnightUI_ApplyDKRunesSettings()
    end)
    b:Slider("Scale %", 50, 200, 5, s.scale or 100, function(v)
        s.scale = math.floor(v)
        _G.MidnightUI_ApplyDKRunesSettings()
    end)
    b:Slider("Opacity", 0, 100, 5, math.floor((s.alpha or 1.0) * 100), function(v)
        s.alpha = v / 100
        _G.MidnightUI_ApplyDKRunesSettings()
    end)

    return b:Height()
end

if _G.MidnightUI_RegisterOverlaySettings then
    _G.MidnightUI_RegisterOverlaySettings("DKRunes", {
        title = "DK Runes",
        build = BuildDKRunesOverlaySettings,
    })
end


local CLASS_POWER_DEFS = {
    { key = "MonkChi",      class = "MONK",    frame = "MonkHarmonyBarFrame",     label = "CHI" },
    { key = "MonkStagger",  class = "MONK",    frame = "MonkStaggerBar",          label = "STAGGER" },
    { key = "HolyPower",    class = "PALADIN", frame = "PaladinPowerBarFrame",    label = "HOLY POWER" },
    { key = "ArcaneCharges",class = "MAGE",    frame = "MageArcaneChargesFrame",  label = "ARCANE CHARGES" },
    { key = "EvokerEssence",class = "EVOKER",  frame = "EssencePlayerFrame",      label = "ESSENCE" },
}

for _, def in ipairs(CLASS_POWER_DEFS) do
    local containerRef

    local function EnsureSettings()
        if not MidnightUISettings then return nil end
        if not MidnightUISettings[def.key] then MidnightUISettings[def.key] = {} end
        local s = MidnightUISettings[def.key]
        if s.enabled == nil then s.enabled = true end
        if not s.scale then s.scale = 100 end
        if not s.alpha then s.alpha = 1.0 end
        return s
    end

    local function ApplyPosition()
        if not containerRef then return end
        local s = EnsureSettings()
        if not s then return end
        containerRef:SetScale((s.scale or 100) / 100)
        containerRef:SetAlpha(s.alpha or 1.0)
        if s.position and _G.MidnightUI_ApplyOverlayPosition then
            _G.MidnightUI_ApplyOverlayPosition(containerRef, s.position)
        end
    end

    local function CreateContainer(pFrame)
        if containerRef then return end
        local bf = _G[def.frame]
        if not bf then return end

        if bf.requiredShownLevel then bf.requiredShownLevel = 1 end
        if bf.Setup then pcall(bf.Setup, bf) end

        local container = CreateFrame("Frame", "MidnightUI_" .. def.key .. "Container", UIParent)
        container:SetSize(200, 30)
        container:SetMovable(true)
        container:SetClampedToScreen(true)
        container:SetFrameStrata(pFrame:GetFrameStrata())
        container:SetFrameLevel(pFrame:GetFrameLevel() + 10)

        local s = EnsureSettings()
        if s then
            container:SetScale((s.scale or 100) / 100)
            container:SetAlpha(s.alpha or 1.0)
        end
        if s and s.position and _G.MidnightUI_ApplyOverlayPosition then
            _G.MidnightUI_ApplyOverlayPosition(container, s.position)
        else
            container:SetPoint("TOPRIGHT", pFrame.powerContainer or pFrame, "BOTTOMRIGHT", 0, -2)
        end

        bf:SetParent(container)
        bf:ClearAllPoints()
        bf:SetPoint("CENTER", container, "CENTER", 0, 0)
        bf:SetScale(1)
        bf:SetAlpha(1)
        bf:Show()

        if bf.UpdatePower then pcall(bf.UpdatePower, bf) end

        containerRef = container
        container:Show()
        if s and s.enabled == false then
            container:SetAlpha(0)
            container:EnableMouse(false)
        end
    end

    local function EnsureOverlay()
        if not containerRef or containerRef.dragOverlay then return end
        local overlay = CreateFrame("Frame", nil, containerRef, "BackdropTemplate")
        overlay:SetAllPoints()
        overlay:SetFrameStrata("DIALOG")
        overlay:SetFrameLevel((containerRef:GetFrameLevel() or 0) + 10)
        if _G.MidnightUI_StyleOverlay then _G.MidnightUI_StyleOverlay(overlay, nil, nil, "unit") end
        local lbl = overlay:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        lbl:SetPoint("CENTER"); lbl:SetText(def.label); lbl:SetTextColor(1, 1, 1)
        overlay:EnableMouse(true)
        overlay:RegisterForDrag("LeftButton")
        overlay:SetScript("OnDragStart", function()
            if InCombatLockdown and InCombatLockdown() then return end
            containerRef._muiIsDragging = true; containerRef:StartMoving()
        end)
        overlay:SetScript("OnDragStop", function()
            if not containerRef._muiIsDragging then return end
            containerRef:StopMovingOrSizing(); containerRef._muiIsDragging = false
            local s = EnsureSettings()
            if s and _G.MidnightUI_SaveOverlayPosition then _G.MidnightUI_SaveOverlayPosition(containerRef, s) end
            local MR = _G.MidnightUI_MoverRegistry
            if MR and MR.SavePosition then MR.SavePosition(def.key) end
        end)
        if _G.MidnightUI_AttachOverlaySettings then _G.MidnightUI_AttachOverlaySettings(overlay, def.key) end
        containerRef.dragOverlay = overlay
    end

    local function ApplyLockState(locked)
        if not containerRef then return end
        local s = EnsureSettings()
        if not s or s.enabled == false then
            containerRef.forceShow = false
            if containerRef.dragOverlay then containerRef.dragOverlay:Hide() end
            containerRef:SetAlpha(0); containerRef:EnableMouse(false)
            return
        end
        containerRef.forceShow = not locked
        ApplyPosition()
        if locked then
            if containerRef.dragOverlay then containerRef.dragOverlay:Hide() end
            containerRef:EnableMouse(false)
        else
            EnsureOverlay()
            if containerRef.dragOverlay then containerRef.dragOverlay:Show() end
            containerRef:EnableMouse(true)
        end
        containerRef:Show()
        _G["MidnightUI_Apply" .. def.key .. "Settings"]()
    end

    _G["MidnightUI_Init" .. def.key] = function(pFrame)
        if not pFrame or pFrame["_mui" .. def.key .. "Attached"] then return end
        local _, pc = UnitClass("player")
        if pc ~= def.class then return end
        CreateContainer(pFrame)
        pFrame["_mui" .. def.key .. "Attached"] = true
    end

    _G["MidnightUI_Set" .. def.key .. "Locked"] = function(locked)
        local _, pc = UnitClass("player")
        if pc ~= def.class then return end
        if not containerRef and playerFrame then CreateContainer(playerFrame) end
        ApplyLockState(locked)
    end

    _G["MidnightUI_Apply" .. def.key .. "Settings"] = function()
        local _, pc = UnitClass("player")
        if pc ~= def.class then return end
        if not containerRef then return end
        local s = EnsureSettings()
        if not s then return end
        if s.enabled == false then
            containerRef:SetAlpha(0); containerRef:EnableMouse(false)
        else
            containerRef:SetScale((s.scale or 100) / 100)
            containerRef:SetAlpha(s.alpha or 1.0)
            containerRef:EnableMouse(true); containerRef:Show()
        end
    end

    do
        local MR = _G.MidnightUI_MoverRegistry
        if MR and MR.DefineMover then
            MR.DefineMover(def.key, {
                resolveFrame = function()
                    local _, pc = UnitClass("player")
                    if pc ~= def.class then return nil end
                    return containerRef
                end,
                category = "unit", label = def.label,
                externalOverlay = true,
                onLock = function() ApplyLockState(true) end,
                onUnlock = function() ApplyLockState(false) end,
            })
        end
    end

    local function BuildOverlaySettings(content, key)
        if not _G.MidnightUI_CreateOverlayBuilder then return end
        local s = EnsureSettings()
        if not s then return end
        local b = _G.MidnightUI_CreateOverlayBuilder(content, { startY = -6 })
        b:Header(def.label)
        b:Checkbox("Enable", s.enabled ~= false, function(v)
            s.enabled = v; _G["MidnightUI_Apply" .. def.key .. "Settings"]()
        end)
        b:Slider("Scale %", 50, 200, 5, s.scale or 100, function(v)
            s.scale = math.floor(v); _G["MidnightUI_Apply" .. def.key .. "Settings"]()
        end)
        b:Slider("Opacity", 0, 100, 5, math.floor((s.alpha or 1.0) * 100), function(v)
            s.alpha = v / 100; _G["MidnightUI_Apply" .. def.key .. "Settings"]()
        end)
        return b:Height()
    end

    if _G.MidnightUI_RegisterOverlaySettings then
        _G.MidnightUI_RegisterOverlaySettings(def.key, {
            title = def.label, build = BuildOverlaySettings,
        })
    end
end
