local addon, ns = ...

local LSM = LibStub("LibSharedMedia-3.0")

ns.addonName = addon
ns.mediapath = "Interface\\AddOns\\rOrbs\\media\\"

-- oUF_Diablo never sets frequentUpdates on Health/Power proxies → UNIT_POWER_UPDATE /
-- UNIT_HEALTH only (discrete jumps + ExponentialEaseOut between targets). true would
-- register UNIT_POWER_FREQUENT and make the fill creep up steadily tick-by-tick.
ns.ORB_FILL_FREQUENT_UPDATES = false

-- Smooth fill between discrete target updates — 1:1 oUF_Diablo PostUpdate SetValue(..., ExponentialEaseOut).
ns.ORB_FILL_SMOOTHING = (Enum and Enum.StatusBarInterpolation and Enum.StatusBarInterpolation.ExponentialEaseOut) or nil

-- PlayerModel clips 3D to its own rectangle. Viewport = holder size + 2*pad per axis.
-- Minimum pad (px); fraction of holder width per side (stacked +30% vs prior 1.30x → ~1.69x total width).
ns.ORB_MODEL_VIEW_PAD = 8
ns.ORB_MODEL_VIEW_PAD_FRAC = 0.345  -- per side → total width ≈ holder * (1 + 2*0.345) ≈ 1.69x

-- 3D template live offset sliders (Template Adjustment + custom preview creator).
ns.ORB_TMPL_LIVE_OFFSET_MIN = -3.0
ns.ORB_TMPL_LIVE_OFFSET_MAX = 3.0
ns.ORB_TMPL_PREVIEW_OFFSET_MIN = -24.0
ns.ORB_TMPL_PREVIEW_OFFSET_MAX = 24.0

-- PlayerModel:SetPosition(x, y, z) — rOrbs convention:
--   X offset = horizontal (left/right) → 2nd SetPosition arg
--   Z offset = vertical (up/down)      → 3rd SetPosition arg
-- posAdjustY in templates is legacy; folded into horizontal when present.
function ns.ResolveOrbModelPlacement(orb, model, tmpl, orbType, isPreview)
  if not model or not tmpl then return 1, 0, 0, 0, 1 end
  local db = rOrbsDB or {}
  local preview = isPreview or tmpl._isPreview
  local orbTypeKey = orbType or ""
  local liveScale = db["tmplScale_" .. orbTypeKey] or 1.0
  local liveOffX  = db["tmplOffsetX_" .. orbTypeKey] or 0.0
  local liveOffZ  = db["tmplOffsetZ_" .. orbTypeKey] or 0.0
  local liveOpacity = db["tmplOpacity_" .. orbTypeKey]

  local cam = (tmpl.camScale or 1.0) * (preview and 1.0 or liveScale)
  local offH = (tmpl.posAdjustX or 0) + (tmpl.posAdjustY or 0)
  local offV = tmpl.posAdjustZ or 0
  if not preview then
    offH = offH + liveOffX
    offV = offV + liveOffZ
  end

  local scale = (orb and orb:GetScale()) or 1.0
  local px = model._natX or 0
  local py = (model._natY or 0) + offH * scale
  local pz = (model._natZ or 0) + offV * scale
  local opacity = preview and (tmpl.modelOpacity or 1.0)
    or (liveOpacity or tmpl.modelOpacity or 1.0)

  return cam, px, py, pz, opacity
end

function ns.ApplyOrbModelPlacement(orb, model, tmpl, orbType, isPreview)
  local cam, px, py, pz, opacity = ns.ResolveOrbModelPlacement(orb, model, tmpl, orbType, isPreview)
  model._baseX = px
  model._baseY = py
  model._baseZ = pz
  model._cam = cam
  model._opacity = opacity
  model:SetCamDistanceScale(cam)
  model:SetPosition(px, py, pz)
  model:SetRotation(tmpl.rotation or 0)
  model:SetPortraitZoom(0)
  model:SetAlpha(opacity)
end

function ns.ApplyOrbModelAnchors(model, holder, fillShiftY)
  if not model or not holder then return end
  fillShiftY = tonumber(fillShiftY) or 0
  local w = holder:GetWidth()
  if not w or w <= 0 then w = 64 end
  local minPad = ns.ORB_MODEL_VIEW_PAD or 8
  local frac = ns.ORB_MODEL_VIEW_PAD_FRAC or 0.8
  local pad = math.max(minPad, w * frac)
  model:ClearAllPoints()
  model:SetPoint("TOPLEFT", holder, "TOPLEFT", -pad, pad + fillShiftY)
  model:SetPoint("BOTTOMRIGHT", holder, "BOTTOMRIGHT", pad, -pad + fillShiftY)
end

-- Model in holder under clipFrame (frame level above fill).
-- No separate viewport frame on same layer as fill (else fill texture draws on top).
--
-- oUF_Diablo behaviour (DEFAULT): the 3D model stays STATIC and only the clipFrame
-- (anchored to the fill texture top) reveals more/less of it as the orb fills/empties.
-- This makes filling "jump up" cleanly like oUF_Diablo instead of pushing the model
-- upward increment-by-increment (which looked jerky while filling).
-- The legacy "drop/pan model with fill" is now opt-in via rOrbsDB.tmplModelDropWithFill == true.
-- rOrbsDB.tmplModelDropFrac: strength 0..1 (fraction of orb height), default 0.38.
function ns.UpdateOrbModelWithFillRatio(orb, ratio)
  if not orb or not orb.model then return end
  local model = orb.model
  local holder = orb.modelHolder or orb.modelScrollChild
  if not holder then holder = orb end
  if not (rOrbsDB and rOrbsDB.tmplModelDropWithFill == true) then
    ns.ApplyOrbModelAnchors(model, holder, 0)
    return
  end
  local r = tonumber(ratio)
  if not r then r = 1 end
  if r < 0 then r = 0 elseif r > 1 then r = 1 end
  local h = orb:GetHeight()
  if not h or h <= 0 then h = 256 end
  local frac = tonumber(rOrbsDB and rOrbsDB.tmplModelDropFrac) or 0.38
  local shiftY = - (1 - r) * h * frac
  ns.ApplyOrbModelAnchors(model, holder, shiftY)
end

-- Legacy signature (orb, model, ratio) — third arg ignored except via orb.model.
function ns.ApplyOrbModelFillDropShift(orb, model, ratio)
  ns.UpdateOrbModelWithFillRatio(orb, ratio)
end

-- Hidden FontString pattern like ns.UpdateLowHPGlow (core/tags.lua).
function ns.GetOrbFillRatioScratch()
  local s = ns._orbFillRatioScratchFS
  if s then return s end
  local f = CreateFrame("Frame", nil, UIParent)
  f:SetSize(1, 1)
  f:Hide()
  s = f:CreateFontString(nil, "ARTWORK")
  s:SetFont("Fonts\\FRIZQT__.TTF", 10)
  ns._orbFillRatioScratchFS = s
  return s
end

-- 1) Ratio from visible fill height (fill tex vs. bar) — same visual as bar, usually pcall-safe.
-- 2) Fallback: UnitHealthPercent / UnitPowerPercent via scratch string (like tags.lua).
function ns.GetOrbFillRatioForViewport(orb)
  if not orb then return 1 end
  local unit = orb.unit
  if unit and not UnitExists(unit) then return 0 end
  if unit and UnitIsDeadOrGhost(unit) then return 0 end

  if orb.fill then
    local tex = orb.fill:GetStatusBarTexture()
    if tex then
      local ok, r = pcall(function()
        local bh = orb.fill:GetHeight()
        local fh = tex:GetHeight()
        if not bh or bh <= 0 then return nil end
        if not fh or fh < 0 then return nil end
        return fh / bh
      end)
      if ok and r ~= nil and r == r then
        r = math.max(0, math.min(1, r))
        return r
      end
    end
  end

  if not unit then return 1 end

  local scratch = ns.GetOrbFillRatioScratch()
  local isPower = (orb.type == "POWER") or (orb._orbType == "POWER")

  if isPower then
    if type(UnitPowerPercent) ~= "function" then return 1 end
    -- UnitPowerType: 1st return = numeric enum (for UnitPowerPercent), 2nd = token e.g. "MANA"
    local powerType = select(1, UnitPowerType(unit))
    if powerType == nil then return 1 end
    local pct
    if CurveConstants and CurveConstants.ScaleTo100 then
      pct = UnitPowerPercent(unit, powerType, false, CurveConstants.ScaleTo100)
    else
      pct = UnitPowerPercent(unit, powerType, false, true)
    end
    if type(pct) ~= "number" then return 1 end
    scratch:SetFormattedText("%.10f", pct)
    local n = tonumber(scratch:GetText())
    if not n then return 1 end
    if n > 1.0001 then n = n / 100 end
    if n < 0 then n = 0 elseif n > 1 then n = 1 end
    return n
  end

  if type(UnitHealthPercent) ~= "function" then return 1 end
  local pct
  if CurveConstants and CurveConstants.ScaleTo100 then
    pct = UnitHealthPercent(unit, true, CurveConstants.ScaleTo100)
  else
    pct = UnitHealthPercent(unit, true, true)
  end
  if type(pct) ~= "number" then return 1 end
  scratch:SetFormattedText("%.10f", pct)
  local n = tonumber(scratch:GetText())
  if not n then return 1 end
  if n > 1.0001 then n = n / 100 end
  if n < 0 then n = 0 elseif n > 1 then n = 1 end
  return n
end

-- Hide/show 3D orb models for units that may not exist (target, tot, pet).
function ns.RefreshOrbModelVisibility(orb)
  if not orb or not orb.model then return end
  local ratio = ns.GetOrbFillRatioForViewport(orb)
  if (orb._tmpl or (orb.model and (orb.model._tmpl or orb.model._templateConfig))) and ns.OrbTemplateEngine then
    ns.OrbTemplateEngine:AdjustByValue(orb, ratio)
    return
  end
  if ratio <= 0 then
    orb.model:Hide()
    orb.model:SetAlpha(0)
  else
    orb.model:Show()
    local orbType = orb._orbType or orb._orbCfgKey
    local alpha = orb.model._opacity
      or (orbType and rOrbsDB and rOrbsDB["tmplOpacity_" .. orbType])
      or 1
    orb.model:SetAlpha(alpha)
  end
end

do
  local f = CreateFrame("Frame")
  f:RegisterEvent("PLAYER_TARGET_CHANGED")
  f:RegisterEvent("UNIT_PET")
  f:RegisterEvent("PLAYER_ENTERING_WORLD")
  f:SetScript("OnEvent", function(_, event, unit)
    if event == "UNIT_PET" and unit ~= "pet" then return end
    if ns.TargetHealthOrb then ns.RefreshOrbModelVisibility(ns.TargetHealthOrb) end
    if ns.ToTHealthOrb then ns.RefreshOrbModelVisibility(ns.ToTHealthOrb) end
    if event ~= "PLAYER_TARGET_CHANGED" and ns.PetHealthOrb then
      ns.RefreshOrbModelVisibility(ns.PetHealthOrb)
    end
  end)
end

-- Namespace containers
ns.cfg  = {}
ns.unit = CreateFrame("Frame")
ns.func = CreateFrame("Frame")
ns.player = {}

-- Register additional LSM fonts shipped with the addon
LSM:Register("font", "Enchanted Land", ns.mediapath .. "Enchanted_Land.otf")

rOrbs.defaults = { profile = {} }

-- Power bar colours (keyed by power token, e.g. "MANA", "ENERGY", "RAGE" …)
rOrbs.defaults.profile.powercolors = {
    MANA        = { r = 0.00, g = 0.44, b = 0.87 },
    RAGE        = { r = 0.78, g = 0.25, b = 0.25 },
    FOCUS       = { r = 0.78, g = 0.61, b = 0.43 },
    ENERGY      = { r = 1.00, g = 0.91, b = 0.00 },
    COMBO_POINTS= { r = 1.00, g = 0.96, b = 0.41 },
    RUNES       = { r = 0.55, g = 0.57, b = 0.61 },
    RUNIC_POWER = { r = 0.00, g = 0.82, b = 1.00 },
    SOUL_SHARDS = { r = 0.50, g = 0.32, b = 0.55 },
    LUNAR_POWER = { r = 0.30, g = 0.52, b = 0.90 },
    HOLY_POWER  = { r = 0.95, g = 0.90, b = 0.60 },
    MAELSTROM   = { r = 0.00, g = 0.50, b = 1.00 },
    CHI         = { r = 0.71, g = 0.90, b = 0.76 },
    ARCANE_CHARGES={ r= 0.22, g = 0.39, b = 0.98 },
    FURY        = { r = 0.79, g = 0.26, b = 0.99 },
    PAIN        = { r = 1.00, g = 0.61, b = 0.00 },
    ESSENCE     = { r = 0.00, g = 0.80, b = 1.00 },
}

-- Colorswitcher: controls health bar foreground/background colouring
rOrbs.defaults.profile.colorswitcher = {
    classcolored        = true,
    useBrightForeground = false,   -- true = class color on bar, dark on bg; false = reversed
    bright = { r = 0.30, g = 0.65, b = 0.30, a = 1.0 },
    dark   = { r = 0.15, g = 0.15, b = 0.15, a = 0.9 },
}

-- Highlight opacity multiplier for the health bar gloss
rOrbs.defaults.profile.highlightMultiplier = 0.4

rOrbs.defaults.profile.fonts = {
    textFont   = "Enchanted Land",
    numberFont = "Enchanted Land",
}

-- Frame dragging settings
rOrbs.defaults.profile.framesUserplaced = true
rOrbs.defaults.profile.framesLocked     = false

rOrbs.defaults.profile.units = {
    player = {
        show  = true,
        size  = 176,
        scale = 1,
        pos   = { a1 = "BOTTOM", a2 = "BOTTOM", af = "UIParent", x = -16, y = 600 },
        health = { frequentUpdates = false },
        power  = { frequentUpdates = false },
        absorb = { show = true },
    },
    target = {
        show  = true,
        size  = 80,
        scale = 1,
        pos   = { a1 = "BOTTOM", a2 = "BOTTOM", af = "UIParent", x = 264, y = 150 },
    },
    targetHealth = {
        show  = true,
        size  = 160,
        scale = 1,
        pos   = { a1 = "BOTTOM", a2 = "BOTTOM", af = "UIParent", x = 264, y = 2 },
        health = { frequentUpdates = false },
        absorb = { show = true },
    },
    targettarget = {
        show  = true,
        size  = 120,
        scale = 1,
        pos   = { a1 = "BOTTOM", a2 = "BOTTOM", af = "UIParent", x = 420, y = 2 },
        health = { frequentUpdates = false },
        absorb = { show = true },
    },
    pet = {
        show  = true,
        size  = 100,
        scale = 1,
        pos   = { a1 = "BOTTOM", a2 = "BOTTOM", af = "UIParent", x = -264, y = 2 },
        health = { frequentUpdates = false },
    },
    party = {
        show       = true,
        scale      = 1.0,
        width      = 185,
        height     = 64,
        vertwidth  = 64,
        vertheight = 185,
        vertical   = false,
        fontSize   = 11,
        pos = { a1 = "TOPLEFT", a2 = "TOPLEFT", af = "UIParent", x = 100, y = -200 },
        health = {
            texture        = "Interface\\AddOns\\rOrbs\\media\\statusbar",
            frequentUpdates = false,
            fontSize       = 11,
            textPos        = "CENTER",
            x              = 0,
            y              = 0,
            tag            = "[diablo:curhp]",
        },
        power = {
            texture        = "Interface\\AddOns\\rOrbs\\media\\statusbar",
            frequentUpdates = false,
        },
        portrait = {
            show  = true,
            use3D = false,
            width = 52,
        },
        auras = {
            show                   = true,
            showBuffs              = false,
            hideBuffs              = true,
            size                   = 14,
            number                 = 4,
            disableCooldownText    = false,
            showDebuffType         = true,
            onlyShowPlayerDebuffs  = false,
            onlyDispellable        = false,
            whitelist              = {},
            blacklist              = {},
        },
        aurawatch = {
            show          = false,
            size          = 13,
            alpha         = 1,
            onlyShowPresent = true,
            pos           = { a1 = "RIGHT", x = 5, y = 0 },
            spellIds      = { ALL = {}, DRUID = {}, PRIEST = {}, PALADIN = {}, SHAMAN = {} },
        },
        healprediction = {
            show        = true,
            texture     = "Interface\\AddOns\\rOrbs\\media\\statusbar",
            maxoverflow = 1.05,
            color = {
                myself = { r = 0.3, g = 1.0, b = 0.3, a = 0.4 },
                other  = { r = 0.3, g = 0.3, b = 1.0, a = 0.4 },
            },
        },
        totalabsorb = {
            show    = true,
            texture = "Interface\\AddOns\\rOrbs\\media\\statusbar",
            color   = {
                bar = { r = 0.7, g = 0.9, b = 1.0, a = 0.5 },
            },
        },
        alpha = {
            notinrange = 0.4,
        },
        attributes = {
            visibility      = "[@raid6,exists] hide; show",
            showPlayer      = false,
            showSolo        = false,
            showParty       = true,
            -- true: header also drives units in raid-style groups (incl. some instanced squads);
            -- false would skip those units and break auras/frames there.
            showRaid        = true,
            VerticalAnchor  = "TOP",
            yoffset         = 5,
            HorizontalAnchor = "RIGHT",
            hideInArena     = true,
        },
    },
}

-- GUI defaults stored inside rOrbsDB
rOrbs.defaults.gui = {
    minimap    = { hide = false },
    debug      = false,
    frameLocked = false,
    -- guiFrameW / guiFrameH: optional; set when resizing the settings UI (defaults 1100×800 on first open)
}

ns.defaults = rOrbs.defaults

-- Arc layout: top (90°) or bottom (270°) only.
function ns.SnapRuneArcAngle(angle)
    if not angle then return 90 end
    angle = angle % 360
    if angle < 0 then angle = angle + 360 end
    local dTop = math.min(math.abs(angle - 90), 360 - math.abs(angle - 90))
    local dBot = math.min(math.abs(angle - 270), 360 - math.abs(angle - 270))
    if dBot < dTop then return 270 end
    return 90
end

function ns.GetRuneArcSide()
    local angle = (rOrbsDB and rOrbsDB.runeArcAngle) or 90
    return (angle == 270) and "bottom" or "top"
end

function ns.SetRuneArcSide(side)
    if not rOrbsDB then return end
    rOrbsDB.runeArcAngle = (side == "bottom") and 270 or 90
end

-- Stubs (filled in after GUI loads)
function rOrbs:AddConfig() end
function rOrbs:TriggerReload() end

-- Load config after VARIABLES_LOADED
local loader = CreateFrame("Frame")
loader:RegisterEvent("VARIABLES_LOADED")
loader:SetScript("OnEvent", function(self, event)
    self:UnregisterEvent("VARIABLES_LOADED")

    -- ── SavedVariable: rOrbsDB (GUI & position settings) ──────────────
    if not rOrbsDB then
        rOrbsDB = {}
    end
    -- Merge GUI defaults into rOrbsDB without overwriting saved values
    local guiDef = rOrbs.defaults.gui
    if rOrbsDB.minimap == nil    then rOrbsDB.minimap     = { hide = false } end
    if rOrbsDB.debug == nil      then rOrbsDB.debug        = false           end
    -- Default: locked GUI (overlay only with Shift/Alt); do not overwrite saved value
    if rOrbsDB.frameLocked == nil then rOrbsDB.frameLocked = true end

    -- Target orb settings
    if rOrbsDB.targetModelShow   == nil then rOrbsDB.targetModelShow   = true  end
    if rOrbsDB.targetModelAlpha  == nil then rOrbsDB.targetModelAlpha  = 1     end
    if rOrbsDB.targetPortraitShow == nil then rOrbsDB.targetPortraitShow = false end
    if rOrbsDB.showBlizzardUnitFrames == nil then rOrbsDB.showBlizzardUnitFrames = false end
    if rOrbsDB.showOrbsInHUDEditMode == nil then rOrbsDB.showOrbsInHUDEditMode = true end
    if rOrbsDB.blizzardFramesHoldFadeTime == nil then rOrbsDB.blizzardFramesHoldFadeTime = 0.15 end
    if rOrbsDB.targetClassColor  == nil then rOrbsDB.targetClassColor  = true  end
    if rOrbsDB.targetRingShow    == nil then rOrbsDB.targetRingShow    = true  end
    if rOrbsDB.targetRingRotate  == nil then rOrbsDB.targetRingRotate  = false end
    if rOrbsDB.targetRingSpeed   == nil then rOrbsDB.targetRingSpeed   = 30    end
    if rOrbsDB.targetRingDir     == nil then rOrbsDB.targetRingDir     = "CW"  end
    if rOrbsDB.targetOrbEnabled  == nil then rOrbsDB.targetOrbEnabled  = true  end
    if rOrbsDB.targetAurasMode   == nil then rOrbsDB.targetAurasMode   = "mouseover" end
    if rOrbsDB.targetBuffOffY    == nil then rOrbsDB.targetBuffOffY    = -18  end
    if rOrbsDB.targetBuffScale   == nil then rOrbsDB.targetBuffScale   = 1.25 end

    -- Pet orb settings
    if rOrbsDB.petOrbEnabled     == nil then rOrbsDB.petOrbEnabled     = true  end
    if rOrbsDB.petRingShow       == nil then rOrbsDB.petRingShow       = true  end
    if rOrbsDB.petNameShow       == nil then rOrbsDB.petNameShow       = true  end
    if rOrbsDB.petNameFontSize   == nil then rOrbsDB.petNameFontSize   = 16    end
    if rOrbsDB.petNamePos        == nil then rOrbsDB.petNamePos        = "TOP" end
    if rOrbsDB.petNameOffY       == nil then rOrbsDB.petNameOffY       = -8    end

    -- Target-of-Target orb settings
    if rOrbsDB.totOrbEnabled     == nil then rOrbsDB.totOrbEnabled     = false end
    if rOrbsDB.totModelShow      == nil then rOrbsDB.totModelShow      = false end
    if rOrbsDB.totModelAlpha     == nil then rOrbsDB.totModelAlpha     = 1     end
    if rOrbsDB.totPortraitShow   == nil then rOrbsDB.totPortraitShow   = false end
    if rOrbsDB.totClassColor     == nil then rOrbsDB.totClassColor     = true  end
    if rOrbsDB.totRingShow       == nil then rOrbsDB.totRingShow       = true  end
    if rOrbsDB.totNameShow       == nil then rOrbsDB.totNameShow       = true  end
    if rOrbsDB.totNameFontSize   == nil then rOrbsDB.totNameFontSize   = 18    end
    if rOrbsDB.totNamePos        == nil then rOrbsDB.totNamePos        = "TOP" end
    if rOrbsDB.totNameClassColor == nil then rOrbsDB.totNameClassColor = false end
    if rOrbsDB.totNameOffY       == nil then rOrbsDB.totNameOffY       = -8    end

    -- Click-cast (orb: left/middle spell, right unit menu)
    if rOrbsDB.clickCastHealthEnabled == nil then rOrbsDB.clickCastHealthEnabled = false end
    if rOrbsDB.clickCastHealthLeft    == nil then rOrbsDB.clickCastHealthLeft    = ""   end
    if rOrbsDB.clickCastHealthMiddle  == nil then rOrbsDB.clickCastHealthMiddle  = ""   end
    if rOrbsDB.clickCastPowerEnabled  == nil then rOrbsDB.clickCastPowerEnabled  = false end
    if rOrbsDB.clickCastPowerLeft     == nil then rOrbsDB.clickCastPowerLeft     = ""   end
    if rOrbsDB.clickCastPowerMiddle   == nil then rOrbsDB.clickCastPowerMiddle   = ""   end
    if rOrbsDB.clickCastTargetEnabled == nil then rOrbsDB.clickCastTargetEnabled = false end
    if rOrbsDB.clickCastTargetLeft    == nil then rOrbsDB.clickCastTargetLeft    = ""   end
    if rOrbsDB.clickCastTargetMiddle  == nil then rOrbsDB.clickCastTargetMiddle  = ""   end
    if rOrbsDB.clickCastHealthRight   == nil then rOrbsDB.clickCastHealthRight   = ""   end
    if rOrbsDB.clickCastPowerRight    == nil then rOrbsDB.clickCastPowerRight    = ""   end
    if rOrbsDB.clickCastTargetRight   == nil then rOrbsDB.clickCastTargetRight   = ""   end
    if rOrbsDB.clickCastPartyEnabled == nil then rOrbsDB.clickCastPartyEnabled = false end
    if rOrbsDB.clickCastPartyLeft    == nil then rOrbsDB.clickCastPartyLeft    = ""   end
    if rOrbsDB.clickCastPartyMiddle  == nil then rOrbsDB.clickCastPartyMiddle  = ""   end
    if rOrbsDB.clickCastPartyRight   == nil then rOrbsDB.clickCastPartyRight   = ""   end

    -- Health / Power ring + figure visibility
    if rOrbsDB.healthRingShow  == nil then rOrbsDB.healthRingShow  = true end
    if rOrbsDB.powerRingShow   == nil then rOrbsDB.powerRingShow   = true end
    if rOrbsDB.healthAngelShow      == nil then rOrbsDB.healthAngelShow      = true          end
    if rOrbsDB.powerDemonShow        == nil then rOrbsDB.powerDemonShow        = true          end
    if rOrbsDB.runePointsShow        == nil then rOrbsDB.runePointsShow        = true          end
    if rOrbsDB.runePointsBGShow      == nil then rOrbsDB.runePointsBGShow      = true          end
    -- Rune layout: "arc" (top/bottom around orb) or "horizontal" (free row). Migrate legacy 8-way presets once.
    if not rOrbsDB._runeArcLayoutMigrated then
        local p = rOrbsDB.runePointsPosition
        local legacy = {
            inside_top     = { a = 90,  o = false },
            outside_top    = { a = 90,  o = true  },
            inside_bottom  = { a = 270, o = false },
            outside_bottom = { a = 270, o = true  },
            inside_left    = { a = 180, o = false },
            outside_left   = { a = 180, o = true  },
            inside_right   = { a = 0,   o = false },
            outside_right  = { a = 0,   o = true  },
        }
        if p and p ~= "horizontal" and p ~= "arc" and legacy[p] then
            rOrbsDB.runeArcAngle    = legacy[p].a
            rOrbsDB.runeArcOutside  = legacy[p].o
            rOrbsDB.runePointsPosition = "arc"
        elseif p == nil then
            rOrbsDB.runePointsPosition = "arc"
        elseif p ~= "horizontal" and p ~= "arc" then
            rOrbsDB.runePointsPosition = "arc"
        end
        rOrbsDB._runeArcLayoutMigrated = true
    end
    if rOrbsDB.runePointsPosition    == nil then rOrbsDB.runePointsPosition    = "arc" end
    if rOrbsDB.runeArcAngle          == nil then rOrbsDB.runeArcAngle          = 90 end
    if rOrbsDB.runeArcOutside        == nil then rOrbsDB.runeArcOutside        = true end
    if not rOrbsDB._runeArcSideMigrated then
        if rOrbsDB.runeArcAngle ~= nil then
            rOrbsDB.runeArcAngle = ns.SnapRuneArcAngle(rOrbsDB.runeArcAngle)
        end
        rOrbsDB._runeArcSideMigrated = true
    end

    -- Figure position & scale defaults (from SavedVariables reference)
    if rOrbsDB.angelScale == nil then rOrbsDB.angelScale = 0.95 end
    if rOrbsDB.angelOffX  == nil then rOrbsDB.angelOffX  = 57   end
    if rOrbsDB.angelOffY  == nil then rOrbsDB.angelOffY  = 10   end
    if rOrbsDB.demonScale == nil then rOrbsDB.demonScale = 1.0  end
    if rOrbsDB.demonOffX  == nil then rOrbsDB.demonOffX  = -74  end
    if rOrbsDB.demonOffY  == nil then rOrbsDB.demonOffY  = 4    end

    -- Diablo Health figure (health orb, configurable texture)
    if rOrbsDB.diabloHealthShow    == nil then rOrbsDB.diabloHealthShow    = true                    end
    if rOrbsDB.diabloHealthTexture == nil then rOrbsDB.diabloHealthTexture = "diablo_health"         end
    if rOrbsDB.diabloHealthScale   == nil then rOrbsDB.diabloHealthScale   = 1.0                     end
    if rOrbsDB.diabloHealthOffX    == nil then rOrbsDB.diabloHealthOffX    = 0                       end
    if rOrbsDB.diabloHealthOffY    == nil then rOrbsDB.diabloHealthOffY    = 0                       end

    -- Font defaults
    if rOrbsDB["font_HEALTH"]        == nil then rOrbsDB["font_HEALTH"]        = "Enchanted Land" end
    if rOrbsDB["font_POWER"]         == nil then rOrbsDB["font_POWER"]         = "Enchanted Land" end
    if rOrbsDB["font_TARGET_HEALTH"] == nil then rOrbsDB["font_TARGET_HEALTH"] = "Enchanted Land" end

    -- Pet font defaults
    if rOrbsDB["font_PET_HEALTH"]          == nil then rOrbsDB["font_PET_HEALTH"]          = "Enchanted Land" end
    if rOrbsDB["fontSize_PET_HEALTH"]      == nil then rOrbsDB["fontSize_PET_HEALTH"]      = 22 end
    if rOrbsDB["textShow_PET_HEALTH"]      == nil then rOrbsDB["textShow_PET_HEALTH"]      = true end
    if rOrbsDB["textHover_PET_HEALTH"]     == nil then rOrbsDB["textHover_PET_HEALTH"]     = true end
    if rOrbsDB["textAlpha_PET_HEALTH"]     == nil then rOrbsDB["textAlpha_PET_HEALTH"]     = 0.8  end
    if rOrbsDB["glossTex_PET_HEALTH"]      == nil then rOrbsDB["glossTex_PET_HEALTH"]      = "classic" end

    -- ToT font defaults
    if rOrbsDB["font_TOT_HEALTH"]         == nil then rOrbsDB["font_TOT_HEALTH"]         = "Enchanted Land" end
    if rOrbsDB["fontSize_TOT_HEALTH"]     == nil then rOrbsDB["fontSize_TOT_HEALTH"]     = 28 end
    if rOrbsDB["textShow_TOT_HEALTH"]     == nil then rOrbsDB["textShow_TOT_HEALTH"]     = true end
    if rOrbsDB["textHover_TOT_HEALTH"]    == nil then rOrbsDB["textHover_TOT_HEALTH"]    = true end
    if rOrbsDB["textAlpha_TOT_HEALTH"]    == nil then rOrbsDB["textAlpha_TOT_HEALTH"]    = 0.8  end
    if rOrbsDB["glossTex_TOT_HEALTH"]     == nil then rOrbsDB["glossTex_TOT_HEALTH"]     = "classic" end

    -- Font size defaults
    if rOrbsDB["fontSize_HEALTH"]        == nil then rOrbsDB["fontSize_HEALTH"]        = 35 end
    if rOrbsDB["fontSize_POWER"]         == nil then rOrbsDB["fontSize_POWER"]         = 35 end
    if rOrbsDB["fontSize_TARGET_HEALTH"] == nil then rOrbsDB["fontSize_TARGET_HEALTH"] = 34 end

    -- Text show/hover/alpha defaults
    if rOrbsDB["textShow_HEALTH"]        == nil then rOrbsDB["textShow_HEALTH"]        = true end
    if rOrbsDB["textShow_POWER"]         == nil then rOrbsDB["textShow_POWER"]         = true end
    if rOrbsDB["textShow_TARGET_HEALTH"] == nil then rOrbsDB["textShow_TARGET_HEALTH"] = true end
    if rOrbsDB["textHover_HEALTH"]       == nil then rOrbsDB["textHover_HEALTH"]       = true end
    if rOrbsDB["textHover_POWER"]        == nil then rOrbsDB["textHover_POWER"]        = true end
    if rOrbsDB["textHover_TARGET_HEALTH"]== nil then rOrbsDB["textHover_TARGET_HEALTH"]= true end
    if rOrbsDB["textAlpha_HEALTH"]       == nil then rOrbsDB["textAlpha_HEALTH"]       = 0.8  end
    if rOrbsDB["textAlpha_POWER"]        == nil then rOrbsDB["textAlpha_POWER"]        = 0.8  end
    if rOrbsDB["textAlpha_TARGET_HEALTH"]== nil then rOrbsDB["textAlpha_TARGET_HEALTH"]= 0.8  end
    if rOrbsDB["textMode_HEALTH"]        == nil then rOrbsDB["textMode_HEALTH"]        = "BOTH" end

    -- Target name text defaults
    if rOrbsDB["targetNameShow"]       == nil then rOrbsDB["targetNameShow"]       = true  end
    if rOrbsDB["targetNameFontSize"]   == nil then rOrbsDB["targetNameFontSize"]   = 22    end
    if rOrbsDB["targetNamePos"]        == nil then rOrbsDB["targetNamePos"]        = "TOP" end
    if rOrbsDB["targetNameClassColor"] == nil then rOrbsDB["targetNameClassColor"] = false end
    if rOrbsDB["targetNameOffY"]       == nil then rOrbsDB["targetNameOffY"]       = -8    end
    if rOrbsDB.targetQuestIconSize   == nil then rOrbsDB.targetQuestIconSize   = 18 end
    if rOrbsDB.targetQuestIconOffX   == nil then rOrbsDB.targetQuestIconOffX   = 0  end
    if rOrbsDB.targetQuestIconOffY   == nil then rOrbsDB.targetQuestIconOffY   = 0  end

    -- Cast Ring defaults
    if rOrbsDB.castRingEnabled        == nil then rOrbsDB.castRingEnabled        = true    end
    if rOrbsDB.castRingRingScale      == nil then rOrbsDB.castRingRingScale      = 1.0     end
    if rOrbsDB.castRingUseClassColor  == nil then rOrbsDB.castRingUseClassColor  = false   end
    if rOrbsDB.castRingCastR          == nil then rOrbsDB.castRingCastR          = 1.0     end
    if rOrbsDB.castRingCastG          == nil then rOrbsDB.castRingCastG          = 0.15    end
    if rOrbsDB.castRingCastB          == nil then rOrbsDB.castRingCastB          = 0.05    end
    if rOrbsDB.castRingChanR          == nil then rOrbsDB.castRingChanR          = 0.3     end
    if rOrbsDB.castRingChanG          == nil then rOrbsDB.castRingChanG          = 0.6     end
    if rOrbsDB.castRingChanB          == nil then rOrbsDB.castRingChanB          = 1.0     end
    if rOrbsDB.castRingBgMode         == nil then rOrbsDB.castRingBgMode         = "rose_bright" end
    if rOrbsDB.castRingBgAlpha        == nil then rOrbsDB.castRingBgAlpha        = 0.8     end
    if rOrbsDB.castRingFadeTime       == nil then rOrbsDB.castRingFadeTime       = 0.4     end
    if rOrbsDB.castRingBgR            == nil then rOrbsDB.castRingBgR            = 1.0     end
    if rOrbsDB.castRingBgG            == nil then rOrbsDB.castRingBgG            = 1.0     end
    if rOrbsDB.castRingBgB            == nil then rOrbsDB.castRingBgB            = 1.0     end
    if rOrbsDB.castRingBgClassColor   == nil then rOrbsDB.castRingBgClassColor   = false   end

    -- Player Combat Icon: size & position offset (relative to auto-position)
    if rOrbsDB.playerCombatIconSize  == nil then rOrbsDB.playerCombatIconSize  = 28 end
    if rOrbsDB.playerCombatIconOffX  == nil then rOrbsDB.playerCombatIconOffX  = 0  end
    if rOrbsDB.playerCombatIconOffY  == nil then rOrbsDB.playerCombatIconOffY  = 0  end
    -- Player Resting Icon: size & position offset
    if rOrbsDB.playerRestingIconSize == nil then rOrbsDB.playerRestingIconSize = 28 end
    if rOrbsDB.playerRestingIconOffX == nil then rOrbsDB.playerRestingIconOffX = 0  end
    if rOrbsDB.playerRestingIconOffY == nil then rOrbsDB.playerRestingIconOffY = 0  end
    if rOrbsDB.playerRestingIconColor == nil then rOrbsDB.playerRestingIconColor = { r = 1, g = 1, b = 1 } end
    -- Target Combat Icon: size & position offset
    if rOrbsDB.targetCombatIconSize  == nil then rOrbsDB.targetCombatIconSize  = 28 end
    if rOrbsDB.targetCombatIconOffX  == nil then rOrbsDB.targetCombatIconOffX  = 0  end
    if rOrbsDB.targetCombatIconOffY  == nil then rOrbsDB.targetCombatIconOffY  = 0  end

    -- Orb template defaults
    if rOrbsDB["tmplShow_HEALTH"]        == nil then rOrbsDB["tmplShow_HEALTH"]        = true  end
    if rOrbsDB["tmplShow_POWER"]         == nil then rOrbsDB["tmplShow_POWER"]         = true  end
    if rOrbsDB["tmplShow_TARGET_HEALTH"] == nil then rOrbsDB["tmplShow_TARGET_HEALTH"] = false end
    if rOrbsDB["tmplShow_PET_HEALTH"]    == nil then rOrbsDB["tmplShow_PET_HEALTH"]    = false end
    if rOrbsDB["tmplOpacity_HEALTH"]        == nil then rOrbsDB["tmplOpacity_HEALTH"]        = 1 end
    if rOrbsDB["tmplOpacity_POWER"]         == nil then rOrbsDB["tmplOpacity_POWER"]         = 1 end
    if rOrbsDB["tmplOpacity_TARGET_HEALTH"] == nil then rOrbsDB["tmplOpacity_TARGET_HEALTH"] = 1 end
    if rOrbsDB["tmplScale_HEALTH"]        == nil then rOrbsDB["tmplScale_HEALTH"]        = 1 end
    if rOrbsDB["tmplScale_POWER"]         == nil then rOrbsDB["tmplScale_POWER"]         = 1 end
    if rOrbsDB["tmplScale_TARGET_HEALTH"] == nil then rOrbsDB["tmplScale_TARGET_HEALTH"] = 1 end
    if rOrbsDB["tmplScale_TOT_HEALTH"]     == nil then rOrbsDB["tmplScale_TOT_HEALTH"]     = 1 end
    if rOrbsDB["tmplScale_PET_HEALTH"]    == nil then rOrbsDB["tmplScale_PET_HEALTH"]    = 1 end
    if rOrbsDB["tmplColors_HEALTH"] == nil then rOrbsDB["tmplColors_HEALTH"] = false end
    if rOrbsDB["tmplColors_POWER"]  == nil then rOrbsDB["tmplColors_POWER"]  = true  end
    if rOrbsDB["orbTemplate_HEALTH"]        == nil then rOrbsDB["orbTemplate_HEALTH"]        = "red-portal"          end
    if rOrbsDB["orbTemplate_POWER"]         == nil then rOrbsDB["orbTemplate_POWER"]         = "blue-magic-tornado"  end
    if rOrbsDB["orbTemplate_TARGET_HEALTH"] == nil then rOrbsDB["orbTemplate_TARGET_HEALTH"] = "blue-swirly"         end
    if rOrbsDB["orbTemplate_PET_HEALTH"]    == nil then rOrbsDB["orbTemplate_PET_HEALTH"]    = "blue-swirly"         end

    -- Gloss texture selection (new/classic)
    if rOrbsDB["glossTex_HEALTH"]        == nil then rOrbsDB["glossTex_HEALTH"]        = "new"     end
    if rOrbsDB["glossTex_POWER"]         == nil then rOrbsDB["glossTex_POWER"]         = "new"     end
    if rOrbsDB["glossTex_TARGET_HEALTH"] == nil then rOrbsDB["glossTex_TARGET_HEALTH"] = "classic" end

    -- Smooth bar defaults
    if rOrbsDB["smoothBar_HEALTH"] == nil then rOrbsDB["smoothBar_HEALTH"] = true end
    if rOrbsDB["smoothSpeed"]      == nil then rOrbsDB["smoothSpeed"]      = 10   end

    if not rOrbsDB.frameScales    then rOrbsDB.frameScales    = {} end
    if not rOrbsDB.framePositions then rOrbsDB.framePositions = {} end
    if rOrbsDB.comboBarSlotScale == nil then rOrbsDB.comboBarSlotScale = 1 end
    if rOrbsDB.runeHorizUIScale == nil then rOrbsDB.runeHorizUIScale = 1 end

    -- Lock orb drag (SHIFT/ALT + left-click) & snap-to-grid — default off (free placement)
    if rOrbsDB.orbDragLocked == nil then rOrbsDB.orbDragLocked = false end
    if rOrbsDB.snapToGrid     == nil then rOrbsDB.snapToGrid     = false end

    -- ── Apply profile defaults → ns.cfg ───────────────────────────────
    ns.cfg = {}
    for k, v in pairs(rOrbs.defaults.profile) do
        ns.cfg[k] = v
    end

    -- Restore frame lock state from SavedVariables
    ns.cfg.framesUserplaced = true
    ns.cfg.framesLocked     = rOrbsDB.frameLocked or false

    -- Party frame SavedVariable defaults
    if rOrbsDB.partyEnabled     == nil then rOrbsDB.partyEnabled     = true  end
    if rOrbsDB.partyVertical    == nil then rOrbsDB.partyVertical    = false end
    if rOrbsDB.partyHideBlizzardCompactRaidManager == nil then rOrbsDB.partyHideBlizzardCompactRaidManager = false end
    if rOrbsDB.partyScale       == nil then rOrbsDB.partyScale       = 1.0  end
    if rOrbsDB.partyRangeAlpha  == nil then rOrbsDB.partyRangeAlpha  = 0.4  end
    if rOrbsDB.partyShowDebuffs == nil then rOrbsDB.partyShowDebuffs = true  end
    if rOrbsDB.partyShowBuffs   == nil then rOrbsDB.partyShowBuffs   = false end
    -- Party buff/debuff rows (under HP bar): visibility modes + layout (see party.lua).
    if rOrbsDB.partyBuffAurasMode == nil then
        rOrbsDB.partyBuffAurasMode = (rOrbsDB.partyShowBuffs == true) and "on" or "mouseover"
    end
    if rOrbsDB.partyDebuffAurasMode == nil then
        rOrbsDB.partyDebuffAurasMode = (rOrbsDB.partyShowDebuffs == false) and "off" or "mouseover"
    end
    if rOrbsDB.partyDebuffSizeMul == nil then rOrbsDB.partyDebuffSizeMul = 1.25 end
    if rOrbsDB.partyAuraSpacing == nil then rOrbsDB.partyAuraSpacing = 3 end
    if rOrbsDB.partyAuraRowGap == nil then rOrbsDB.partyAuraRowGap = 4 end
    if rOrbsDB.partyAuraHostOffX == nil then rOrbsDB.partyAuraHostOffX = 0 end
    if rOrbsDB.partyAuraHostOffY == nil then rOrbsDB.partyAuraHostOffY = -2 end
    if rOrbsDB.partyAuraBuffOffX == nil then rOrbsDB.partyAuraBuffOffX = 0 end
    if rOrbsDB.partyAuraBuffOffY == nil then rOrbsDB.partyAuraBuffOffY = 0 end
    if rOrbsDB.partyAuraDebuffOffX == nil then rOrbsDB.partyAuraDebuffOffX = 0 end
    if rOrbsDB.partyAuraDebuffOffY == nil then rOrbsDB.partyAuraDebuffOffY = 0 end
    if rOrbsDB.partyShowPlayer  == nil then rOrbsDB.partyShowPlayer  = false end
    if rOrbsDB.partyHideInRaid  == nil then rOrbsDB.partyHideInRaid  = true  end
    if rOrbsDB.partyHideArena   == nil then rOrbsDB.partyHideArena   = true  end
    if rOrbsDB.partyHealPred    == nil then rOrbsDB.partyHealPred    = true  end
    if rOrbsDB.partyTotalAbsorb == nil then rOrbsDB.partyTotalAbsorb = true  end
    if rOrbsDB.partyAuraSize    == nil then rOrbsDB.partyAuraSize    = 14    end

    -- Party frame layout (pixel dimensions)
    if rOrbsDB.partyPortraitSize == nil then rOrbsDB.partyPortraitSize = 95  end
    if rOrbsDB.partyHpHeight     == nil then rOrbsDB.partyHpHeight     = 18  end
    if rOrbsDB.partyHpWidth      == nil then rOrbsDB.partyHpWidth      = 90  end
    if rOrbsDB.partyPwrHeight    == nil then rOrbsDB.partyPwrHeight    = 7   end
    if rOrbsDB.partyPwrWidth     == nil then rOrbsDB.partyPwrWidth     = 76  end
    if rOrbsDB.partyBarGap       == nil then rOrbsDB.partyBarGap       = 2   end
    if rOrbsDB.partyPortrait3D   == nil then rOrbsDB.partyPortrait3D   = false end  -- 2D default (3D blocks clicks)
    if rOrbsDB.partyHpTextFormat  == nil then rOrbsDB.partyHpTextFormat  = "value" end  -- "value" or "percent"
    if rOrbsDB.partyNameHeight    == nil then rOrbsDB.partyNameHeight    = 22  end  -- NAME_H: gap between top of frame and portrait
    -- Artwork: independent position/size relative to HP-bar center
    if rOrbsDB.partyArtW         == nil then rOrbsDB.partyArtW         = 160 end
    if rOrbsDB.partyArtH         == nil then rOrbsDB.partyArtH         = 55  end
    if rOrbsDB.partyArtX         == nil then rOrbsDB.partyArtX         = 0   end  -- X offset from HP-bar center
    if rOrbsDB.partyArtY         == nil then rOrbsDB.partyArtY         = 0   end  -- Y offset from HP-bar center
    if rOrbsDB.partyPosX         == nil then rOrbsDB.partyPosX         = nil end  -- nil = use cfg default
    if rOrbsDB.partyPosY         == nil then rOrbsDB.partyPosY         = nil end

    -- Apply saved position/size overrides to unit config
    local unitKeys = { "player", "targetHealth", "target", "targettarget", "pet" }
    for _, uKey in ipairs(unitKeys) do
        if ns.cfg.units and ns.cfg.units[uKey] then
            local savedX    = rOrbsDB["pos_"  .. uKey .. "_x"]
            local savedY    = rOrbsDB["pos_"  .. uKey .. "_y"]
            local savedSize = rOrbsDB["size_" .. uKey]
            local savedScale = rOrbsDB["scale_" .. uKey]
            if savedX    then ns.cfg.units[uKey].pos.x = savedX    end
            if savedY    then ns.cfg.units[uKey].pos.y = savedY    end
            local savedA1 = rOrbsDB["pos_" .. uKey .. "_a1"]
            local savedA2 = rOrbsDB["pos_" .. uKey .. "_a2"]
            local savedAf = rOrbsDB["pos_" .. uKey .. "_af"]
            if savedA1 then ns.cfg.units[uKey].pos.a1 = savedA1 end
            if savedA2 then ns.cfg.units[uKey].pos.a2 = savedA2 end
            if savedAf then ns.cfg.units[uKey].pos.af = savedAf end
            if savedSize then ns.cfg.units[uKey].size  = savedSize end
            if savedScale then ns.cfg.units[uKey].scale = savedScale end
        end
    end

    -- Apply saved font overrides (always set, falling back to "Enchanted Land" default)
    local healthFont = rOrbsDB["font_HEALTH"] or "Enchanted Land"
    local powerFont  = rOrbsDB["font_POWER"]  or "Enchanted Land"
    local targetFont = rOrbsDB["font_TARGET_HEALTH"] or "Enchanted Land"
    if LSM:Fetch("font", healthFont) then
        ns.cfg.fonts.textFont = healthFont
    else
        ns.cfg.fonts.textFont = "Enchanted Land"
    end
    if LSM:Fetch("font", powerFont) then
        ns.cfg.fonts.numberFont = powerFont
    else
        ns.cfg.fonts.numberFont = "Enchanted Land"
    end
    -- targetFont is handled directly in target-health.lua

    -- Apply saved party settings to ns.cfg
    if ns.cfg.units and ns.cfg.units.party then
        local pcfg = ns.cfg.units.party
        pcfg.show                          = rOrbsDB.partyEnabled     == true
        pcfg.vertical                      = rOrbsDB.partyVertical    == true
        pcfg.scale                         = rOrbsDB.partyScale       or 1.0
        pcfg.alpha.notinrange              = rOrbsDB.partyRangeAlpha  or 0.4
        pcfg.auras.show                    = rOrbsDB.partyShowDebuffs ~= false
        pcfg.auras.showBuffs               = rOrbsDB.partyShowBuffs   == true
        pcfg.auras.hideBuffs               = not (rOrbsDB.partyShowBuffs == true)
        pcfg.auras.size                    = rOrbsDB.partyAuraSize    or 14
        pcfg.attributes.showPlayer         = rOrbsDB.partyShowPlayer  == true
        pcfg.attributes.visibility         = (rOrbsDB.partyHideInRaid ~= false)
                                             and "[@raid6,exists] hide; show"
                                             or  "show"
        pcfg.attributes.hideInArena        = rOrbsDB.partyHideArena   ~= false
        pcfg.healprediction.show           = rOrbsDB.partyHealPred    ~= false
        pcfg.totalabsorb.show              = rOrbsDB.partyTotalAbsorb ~= false
        if rOrbsDB.partyPosX then pcfg.pos.x = rOrbsDB.partyPosX end
        if rOrbsDB.partyPosY then pcfg.pos.y = rOrbsDB.partyPosY end
    end

    -- Debug flag
    ns.debug = rOrbsDB.debug == true

    -- Spawn orbs
    C_Timer.After(0, function()
        ns:Init()
        if ns.ApplyBlizzardCompactRaidFrameManagerSetting then
            ns.ApplyBlizzardCompactRaidFrameManagerSetting()
        end
    end)

    -- NOTE: rOrbsDB is SavedVariablesPerCharacter, so each character
    -- automatically has their own independent settings. No profile
    -- auto-apply is needed on login.
end)
