-- ============================================================
--  EllesmereUI_koKR_Castbar — 대상/주시/보스 시전바 옵션 (개인용/공유 가능)
--  secret 값 (cb.notInterruptible, cdTime:IsZero) 읽기를 이 애드온에 격리.
--  → 본체 EllesmereUI_koKR 의 번역/기타 후크에 taint 전파 차단.
--  EUI 메인 프레임 우상단에 [시전바] 버튼 추가.
-- ============================================================

local SV_NAME = "EllesmereUIKorCastbarDB"

-- ────────── DB ──────────
local DEFAULTS = {
    castbarSolidEnabled       = false,
    castbarSolidColor         = { r = 0.114, g = 0.655, b = 0.514, a = 1.0 },
    castbarGradientEnabled    = false,
    castbarGradientStartColor = { r = 0.0,   g = 0.0,   b = 0.0,   a = 1.0 },
    castbarGradientColor      = { r = 0.114, g = 0.655, b = 0.514, a = 1.0 },
    castbarGradientDir        = "HORIZONTAL",
    castbarShieldedEnabled = false,
    castbarShieldedColor   = { r = 0.8, g = 0.2, b = 0.2 },
    castbarShieldedGradientEnabled    = false,
    castbarShieldedGradientStartColor = { r = 0.0, g = 0.0, b = 0.0, a = 1.0 },
    castbarShieldedGradientColor      = { r = 0.8, g = 0.2, b = 0.2, a = 1.0 },
    castbarShieldedGradientDir        = "HORIZONTAL",
    castbarTextureKey      = "none",
    castbarPopupScale      = 1.0,
}

local function GetDB()
    _G[SV_NAME] = _G[SV_NAME] or {}
    local db = _G[SV_NAME]
    for k, v in pairs(DEFAULTS) do
        if db[k] == nil then db[k] = v end
    end
    return db
end

-- EllesmereUIKorAdditionsDB 에서 한 번만 마이그레이션
local function MigrateFromAdditions()
    local src = _G.EllesmereUIKorAdditionsDB
    local db = GetDB()
    if not src or db._migrated then return end
    for k in pairs(DEFAULTS) do
        if src[k] ~= nil then db[k] = src[k] end
    end
    -- 시전바 배경 투명도도 캐스트바 도메인으로 이전 (구 Additions DB → Castbar DB).
    if src.castbarBgAlpha ~= nil then db.castbarBgAlpha = src.castbarBgAlpha end
    db._migrated = true
end

-- ────────── 공용 헬퍼 ──────────
local POPUP_MAIN_BG  = { 0.12, 0.13, 0.15, 1 }
local POPUP_TITLE_BG = { 0.17, 0.18, 0.20, 1 }
local POPUP_BTN_BG   = { 0.01, 0.02, 0.03, 1 }
local POPUP_STEP_BG  = { 0.01, 0.02, 0.03, 1 }
local POPUP_BTN_HOVER= { 0.20, 0.20, 0.22, 0.8 }

local function GetFont()
    return "Fonts\\2002.TTF"   -- 추가기능 글자는 모두 클라 한글 폰트 (Expressway 는 한글 글리프 없음)
end

local function MakeFont(parent, size, flags, r, g, b)
    local fs = parent:CreateFontString(nil, "OVERLAY")
    fs:SetFont(GetFont(), size or 12, flags or "")
    fs:SetTextColor(r or 1, g or 1, b or 1, 1)
    return fs
end

local function SolidTex(parent, layer, r, g, b, a)
    local t = parent:CreateTexture(nil, layer or "BACKGROUND")
    t:SetColorTexture(r, g, b, a or 1)
    return t
end

local function MakeBorder(frame, r, g, b, a)
    a = a or 1
    local borders = {}
    local top = frame:CreateTexture(nil, "OVERLAY")
    top:SetColorTexture(r, g, b, a); top:SetHeight(1)
    top:SetPoint("TOPLEFT"); top:SetPoint("TOPRIGHT")
    local bot = frame:CreateTexture(nil, "OVERLAY")
    bot:SetColorTexture(r, g, b, a); bot:SetHeight(1)
    bot:SetPoint("BOTTOMLEFT"); bot:SetPoint("BOTTOMRIGHT")
    local lft = frame:CreateTexture(nil, "OVERLAY")
    lft:SetColorTexture(r, g, b, a); lft:SetWidth(1)
    lft:SetPoint("TOPLEFT"); lft:SetPoint("BOTTOMLEFT")
    local rgt = frame:CreateTexture(nil, "OVERLAY")
    rgt:SetColorTexture(r, g, b, a); rgt:SetWidth(1)
    rgt:SetPoint("TOPRIGHT"); rgt:SetPoint("BOTTOMRIGHT")
    borders.top, borders.bot, borders.lft, borders.rgt = top, bot, lft, rgt
    return borders
end

local function SetBorderColor(borders, r, g, b, a)
    a = a or 1
    for _, t in pairs(borders) do t:SetColorTexture(r, g, b, a) end
end

local function MakeSwitch(parent, anchor, yoff, label, key, onChange)
    local EUI = _G.EllesmereUI
    local gC  = (EUI and EUI.ELLESMERE_GREEN) or { r = 0.05, g = 0.82, b = 0.61 }
    local TW, TH, PAD = 40, 20, 2
    local KN  = TH - PAD * 2
    local row = CreateFrame("Button", nil, parent)
    row:SetSize(220, 22)
    row:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, yoff)
    local lb = MakeFont(row, 11, nil, 0.9, 0.9, 0.95)
    lb:SetPoint("LEFT", row, "LEFT", 0, 0)
    lb:SetText(label)
    local sw = CreateFrame("Frame", nil, row)
    sw:SetSize(TW, TH)
    sw:SetPoint("LEFT", row, "LEFT", 150, 0)
    local tBg = SolidTex(sw, "BACKGROUND", 0.267, 0.267, 0.267, 0.65)
    tBg:SetAllPoints()
    local knob = sw:CreateTexture(nil, "ARTWORK")
    knob:SetSize(KN, KN)
    local function _upd()
        knob:ClearAllPoints()
        if GetDB()[key] ~= false then
            tBg:SetColorTexture(gC.r, gC.g, gC.b, 0.75)
            knob:SetPoint("RIGHT", sw, "RIGHT", -PAD, 0)
            knob:SetColorTexture(1, 1, 1, 1)
        else
            tBg:SetColorTexture(0.267, 0.267, 0.267, 0.65)
            knob:SetPoint("LEFT", sw, "LEFT", PAD, 0)
            knob:SetColorTexture(1, 1, 1, 0.5)
        end
    end
    row:SetScript("OnEnter", function() lb:SetTextColor(1, 1, 1) end)
    row:SetScript("OnLeave", function() lb:SetTextColor(0.9, 0.9, 0.95) end)
    row:SetScript("OnClick", function()
        local db = GetDB()
        db[key] = (db[key] == false) and true or false
        _upd()
        if onChange then onChange() end
    end)
    _upd()
    return row
end

local function MakeStepBtn(parent, side, label, onClick)
    local b = CreateFrame("Button", nil, parent)
    b:SetSize(24, 22)
    if side == "LEFT" then b:SetPoint("LEFT", parent, "LEFT", 2, 0)
    else                   b:SetPoint("RIGHT", parent, "RIGHT", -2, 0) end
    local bg = SolidTex(b, "BACKGROUND", POPUP_BTN_BG[1], POPUP_BTN_BG[2], POPUP_BTN_BG[3], POPUP_BTN_BG[4])
    bg:SetAllPoints()
    local fs = MakeFont(b, 13, "OUTLINE", 0.85, 0.85, 0.9)
    fs:SetPoint("CENTER")
    fs:SetText(label)
    b:SetScript("OnEnter", function() bg:SetColorTexture(POPUP_BTN_HOVER[1], POPUP_BTN_HOVER[2], POPUP_BTN_HOVER[3], POPUP_BTN_HOVER[4]) end)
    b:SetScript("OnLeave", function() bg:SetColorTexture(POPUP_BTN_BG[1], POPUP_BTN_BG[2], POPUP_BTN_BG[3], POPUP_BTN_BG[4]) end)
    b:SetScript("OnClick", onClick)
    return b
end

local function MakeColorSwatch(parent, getFn, setFn, hasAlpha, size)
    local sz = size or 18
    local btn = CreateFrame("Button", nil, parent)
    btn:SetSize(sz, sz)
    local bg = btn:CreateTexture(nil, "BACKGROUND")
    bg:SetAllPoints()
    bg:SetColorTexture(0, 0, 0, 1)
    local sw = btn:CreateTexture(nil, "ARTWORK")
    sw:SetPoint("TOPLEFT", 1, -1)
    sw:SetPoint("BOTTOMRIGHT", -1, 1)
    sw:SetColorTexture(1, 1, 1, 1)
    local function refresh()
        local r, g, b = getFn()
        sw:SetColorTexture(r or 1, g or 1, b or 1, 1)
    end
    btn._refresh = refresh
    btn:SetScript("OnClick", function()
        local r, g, b, a = getFn()
        local function readAlpha()
            if ColorPickerFrame.GetColorAlpha then
                return ColorPickerFrame:GetColorAlpha()
            elseif OpacitySliderFrame and OpacitySliderFrame.GetValue then
                return 1 - OpacitySliderFrame:GetValue()
            end
            return 1
        end
        local info = {
            r = r, g = g, b = b, opacity = (a or 1), hasOpacity = hasAlpha and true or false,
            swatchFunc = function()
                local nr, ng, nb = ColorPickerFrame:GetColorRGB()
                setFn(nr, ng, nb, readAlpha()); refresh()
            end,
            opacityFunc = function()
                local nr, ng, nb = ColorPickerFrame:GetColorRGB()
                setFn(nr, ng, nb, readAlpha()); refresh()
            end,
            cancelFunc = function(prev) setFn(prev.r, prev.g, prev.b, prev.opacity or 1); refresh() end,
        }
        if ColorPickerFrame.SetupColorPickerAndShow then
            ColorPickerFrame:SetupColorPickerAndShow(info)
        else
            ColorPickerFrame.func = info.swatchFunc
            ColorPickerFrame.opacityFunc = info.opacityFunc
            ColorPickerFrame.cancelFunc = info.cancelFunc
            ColorPickerFrame.hasOpacity = info.hasOpacity
            ColorPickerFrame.opacity = info.opacity
            ColorPickerFrame.previousValues = { r = r, g = g, b = b, opacity = a or 1 }
            ColorPickerFrame:SetColorRGB(r, g, b)
            ColorPickerFrame:Hide(); ColorPickerFrame:Show()
        end
        -- 팝업(_dimmer/p) 이 FULLSCREEN_DIALOG strata 라 ColorPickerFrame 이 가려짐.
        -- TOOLTIP 으로 띄우고 OnHide 시 1회 한정으로 복귀 훅 설치.
        if not ColorPickerFrame.__cbStrataHook then
            ColorPickerFrame.__cbStrataHook = true
            ColorPickerFrame:HookScript("OnHide", function(self)
                self:SetFrameStrata("FULLSCREEN_DIALOG")
            end)
        end
        ColorPickerFrame:SetFrameStrata("TOOLTIP")
        ColorPickerFrame:Raise()
    end)
    refresh()
    return btn
end

-- ────────── 시전바 대상 프레임 + 텍스처 옵션 ──────────
local CASTBAR_TARGET_FRAMES = {
    "EllesmereUIUnitFrames_Target",
    "EllesmereUIUnitFrames_Focus",
}
for i = 1, 8 do
    CASTBAR_TARGET_FRAMES[#CASTBAR_TARGET_FRAMES+1] = "EllesmereUIUnitFrames_Boss" .. i
end

local _CB_TEX_BASE = "Interface\\AddOns\\EllesmereUI\\media\\textures\\"
local CASTBAR_TEXTURE_OPTIONS = {
    { key = "none",             label = "단색",          path = "Interface\\Buttons\\WHITE8X8" },
    { key = "melli",            label = "Melli",         path = _CB_TEX_BASE .. "melli.tga" },
    { key = "atrocity",         label = "Atrocity",      path = _CB_TEX_BASE .. "atrocity.tga" },
    { key = "fade",             label = "Fade",          path = _CB_TEX_BASE .. "fade.tga" },
    { key = "fade-right",       label = "Fade Right",    path = _CB_TEX_BASE .. "fade-right.tga" },
    { key = "thin-line-top",    label = "Thin Top",      path = _CB_TEX_BASE .. "thin-line-top.tga" },
    { key = "thin-line-bottom", label = "Thin Bottom",   path = _CB_TEX_BASE .. "thin-line-bottom.tga" },
    { key = "beautiful",        label = "Beautiful",     path = _CB_TEX_BASE .. "beautiful.tga" },
    { key = "plating",          label = "Plating",       path = _CB_TEX_BASE .. "plating.tga" },
    { key = "divide",           label = "Divide",        path = _CB_TEX_BASE .. "divide.tga" },
    { key = "glass",            label = "Glass",         path = _CB_TEX_BASE .. "glass.tga" },
    { key = "gradient-lr",      label = "Gradient ▶",    path = _CB_TEX_BASE .. "gradient-lr.tga" },
    { key = "gradient-rl",      label = "Gradient ◀",    path = _CB_TEX_BASE .. "gradient-rl.tga" },
    { key = "gradient-bt",      label = "Gradient ▲",    path = _CB_TEX_BASE .. "gradient-bt.tga" },
    { key = "gradient-tb",      label = "Gradient ▼",    path = _CB_TEX_BASE .. "gradient-tb.tga" },
    { key = "matte",            label = "Matte",         path = _CB_TEX_BASE .. "matte.tga" },
    { key = "sheer",            label = "Sheer",         path = _CB_TEX_BASE .. "sheer.tga" },
}

local function _CB_GetTextureOptByKey(key)
    for _, o in ipairs(CASTBAR_TEXTURE_OPTIONS) do
        if o.key == key then return o end
    end
    return CASTBAR_TEXTURE_OPTIONS[1]
end

local function _CB_GetTargetCastbars()
    local out = {}
    for _, name in ipairs(CASTBAR_TARGET_FRAMES) do
        local f = _G[name]
        if f and f.Castbar then out[#out+1] = f.Castbar end
    end
    return out
end

-- ────────── 인터럽트 스킬 감지 ──────────
local _CB_KICK_SPELLS = {
    DEATHKNIGHT = {47528},
    WARRIOR     = {6552},
    WARLOCK     = {19647, 89766, 119910, 1276467, 132409},
    SHAMAN      = {57994},
    ROGUE       = {1766},
    PRIEST      = {15487},
    PALADIN     = {31935, 96231},
    MONK        = {116705},
    MAGE        = {2139},
    HUNTER      = {187707, 147362},
    EVOKER      = {351338},
    DRUID       = {38675, 78675, 106839},
    DEMONHUNTER = {183752},
}
local _cbActiveKick
local function _CB_RefreshKick()
    _cbActiveKick = nil
    local class = UnitClassBase("player")
    local list = class and _CB_KICK_SPELLS[class]
    if not list then return end
    for i = 1, #list do
        local id = list[i]
        if C_SpellBook and C_SpellBook.IsSpellKnownOrInSpellBook then
            local known = C_SpellBook.IsSpellKnownOrInSpellBook(id)
            local petKnown = Enum and Enum.SpellBookSpellBank and C_SpellBook.IsSpellKnownOrInSpellBook(id, Enum.SpellBookSpellBank.Pet)
            if known or petKnown then _cbActiveKick = id end
        elseif IsSpellKnown and IsSpellKnown(id) then
            _cbActiveKick = id
        end
    end
end

-- ────────── 적용 함수들 ──────────
local function _CB_ApplyInterruptTint(cb)
    if not cb or not _cbActiveKick then return end
    if not (C_Spell and C_Spell.GetSpellCooldownDuration) then return end
    local cdTime = C_Spell.GetSpellCooldownDuration(_cbActiveKick)
    if not (cdTime and cdTime.IsZero) then return end
    local offCD = cdTime:IsZero()
    local tint = cb._kickCdTint
    if not tint then
        tint = cb:CreateTexture(nil, "ARTWORK", nil, 3)
        tint:SetColorTexture(1, 1, 1, 1)
        cb._kickCdTint = tint
    end
    tint:ClearAllPoints()
    local anchor = cb.castTintLayer or (cb.GetStatusBarTexture and cb:GetStatusBarTexture())
    if anchor then tint:SetAllPoints(anchor) else tint:SetAllPoints(cb) end
    local db = GetDB()
    if db.castbarShieldedEnabled then
        local sc = db.castbarShieldedColor or DEFAULTS.castbarShieldedColor
        tint:SetVertexColor(sc.r or 0.8, sc.g or 0.2, sc.b or 0.2, 1)
    else
        tint:SetVertexColor(0.5, 0.5, 0.5, 1)
    end
    tint:SetAlphaFromBoolean(offCD, 0, 1)
end

local function _CB_ApplyShielded(cb)
    local tint = cb and cb._shieldedTint
    if not tint then return end
    local db = GetDB()
    if db.castbarShieldedEnabled then
        local c = db.castbarShieldedColor
        tint:SetVertexColor(c.r, c.g, c.b)
    else
        tint:SetVertexColor(0.5, 0.5, 0.5)
    end
end

local function _CB_ApplyShieldedGradient(cb)
    if not cb then return end
    local db = GetDB()
    local function ensureWrap(frameKey, texKey, sublayer)
        local f = cb[frameKey]
        if not f then
            f = CreateFrame("Frame", nil, cb)
            f:SetFrameLevel(cb:GetFrameLevel() + sublayer)
            local t = f:CreateTexture(nil, "ARTWORK")
            t:SetTexture("Interface\\Buttons\\WHITE8X8")
            t:SetAllPoints(f)
            cb[frameKey] = f
            cb[texKey] = t
        end
        f:ClearAllPoints()
        local anchor = cb.castTintLayer or (cb.GetStatusBarTexture and cb:GetStatusBarTexture())
        if anchor then f:SetAllPoints(anchor) else f:SetAllPoints(cb) end
        return f, cb[texKey]
    end
    local fNI, tNI = ensureWrap("_shieldedGradFrame_NI", "_shieldedGradTex_NI", 5)
    local fCD, tCD = ensureWrap("_shieldedGradFrame_CD", "_shieldedGradTex_CD", 6)
    if not db.castbarShieldedGradientEnabled then
        fNI:Hide(); fCD:Hide(); return
    end
    if not (cb.casting or cb.channeling) then
        fNI:Hide(); fCD:Hide(); return
    end
    fNI:Show(); fCD:Show()
    local sc = db.castbarShieldedGradientStartColor or { r = 0, g = 0, b = 0, a = 1 }
    local gc = db.castbarShieldedGradientColor      or { r = 0.8, g = 0.2, b = 0.2, a = 1 }
    local dir = db.castbarShieldedGradientDir or "HORIZONTAL"
    tNI:SetVertexColor(1, 1, 1, 1)
    tNI:SetGradient(dir, CreateColor(sc.r, sc.g, sc.b, sc.a or 1),
                         CreateColor(gc.r, gc.g, gc.b, gc.a or 1))
    tCD:SetVertexColor(1, 1, 1, 1)
    tCD:SetGradient(dir, CreateColor(sc.r, sc.g, sc.b, sc.a or 1),
                         CreateColor(gc.r, gc.g, gc.b, gc.a or 1))
    fNI:SetAlpha(0)
    pcall(fNI.SetAlphaFromBoolean, fNI, cb.notInterruptible, 1, 0)
    fCD:SetAlpha(0)
    if not _cbActiveKick then
        fCD:SetAlpha(1)
    elseif C_Spell and C_Spell.GetSpellCooldownDuration then
        local cdTime = C_Spell.GetSpellCooldownDuration(_cbActiveKick)
        if cdTime and cdTime.IsZero then
            local offCD = cdTime:IsZero()
            pcall(fCD.SetAlphaFromBoolean, fCD, offCD, 0, 1)
        end
    end
end

local function _CB_ApplyTexture(cb)
    if not cb then return end
    local db = GetDB()
    local key = db.castbarTextureKey or "none"
    if key == "none" then return end
    local opt = _CB_GetTextureOptByKey(key)
    if cb.SetStatusBarTexture then
        local r, g, b, a = cb:GetStatusBarColor()
        cb:SetStatusBarTexture(opt.path)
        local st = cb:GetStatusBarTexture()
        if st and st.SetHorizTile then st:SetHorizTile(false) end
        if r then cb:SetStatusBarColor(r, g, b, a or 1) end
    end
    if cb.castTintLayer and cb.castTintLayer.SetTexture then
        local r, g, b, a = cb.castTintLayer:GetVertexColor()
        cb.castTintLayer:SetTexture(opt.path)
        if r then cb.castTintLayer:SetVertexColor(r, g, b, a) end
    end
end

local function _CB_ApplyColor(cb)
    if not cb then return end
    local layer = cb.castTintLayer
    local stTex = cb.GetStatusBarTexture and cb:GetStatusBarTexture()
    local db = GetDB()
    if db.castbarGradientEnabled and stTex and stTex.SetGradient then
        if layer then layer:SetAlpha(0) end
        stTex:SetVertexColor(1, 1, 1, 1)
        local sc = db.castbarGradientStartColor or { r = 0, g = 0, b = 0, a = 1 }
        local gc = db.castbarGradientColor      or { r = 1, g = 1, b = 1, a = 1 }
        local dir = db.castbarGradientDir or "HORIZONTAL"
        stTex:SetGradient(dir, CreateColor(sc.r, sc.g, sc.b, sc.a or 1), CreateColor(gc.r, gc.g, gc.b, gc.a or 1))
    elseif db.castbarSolidEnabled and layer then
        layer:SetAlpha(1)
        local c = db.castbarSolidColor or { r = 1, g = 1, b = 1, a = 1 }
        layer:SetVertexColor(c.r, c.g, c.b)
    else
        if layer then layer:SetAlpha(1) end
    end
end

local _cbHookWrapped = setmetatable({}, { __mode = "k" })
local function _CB_InstallPostCastHook(cb)
    if not cb or _cbHookWrapped[cb] then return end
    _cbHookWrapped[cb] = true
    local origStart    = cb.PostCastStart
    local origChannel  = cb.PostChannelStart
    local origInterrupt = cb.PostCastInterruptible
    local function ourPost(self)
        _CB_ApplyTexture(self)
        _CB_ApplyShielded(self)
        _CB_ApplyShieldedGradient(self)
        _CB_ApplyColor(self)
        _CB_ApplyInterruptTint(self)
    end
    local function hideShOverlay(self)
        if self._shieldedGradFrame_NI then self._shieldedGradFrame_NI:Hide() end
        if self._shieldedGradFrame_CD then self._shieldedGradFrame_CD:Hide() end
    end
    cb.PostCastStart = function(self, ...)
        if origStart then origStart(self, ...) end
        ourPost(self)
    end
    cb.PostChannelStart = function(self, ...)
        if origChannel then origChannel(self, ...) end
        ourPost(self)
    end
    cb.PostCastInterruptible = function(self, ...)
        if origInterrupt then origInterrupt(self, ...) end
        ourPost(self)
    end
    local origStop      = cb.PostCastStop
    local origChStop    = cb.PostChannelStop
    local origFail      = cb.PostCastFail
    local origInterrupt2= cb.PostCastInterrupt
    cb.PostCastStop = function(self, ...)
        if origStop then origStop(self, ...) end
        hideShOverlay(self)
    end
    cb.PostChannelStop = function(self, ...)
        if origChStop then origChStop(self, ...) end
        hideShOverlay(self)
    end
    cb.PostCastFail = function(self, ...)
        if origFail then origFail(self, ...) end
        hideShOverlay(self)
    end
    cb.PostCastInterrupt = function(self, ...)
        if origInterrupt2 then origInterrupt2(self, ...) end
        hideShOverlay(self)
    end
end

local function _CB_ApplyAll()
    for _, cb in ipairs(_CB_GetTargetCastbars()) do
        _CB_InstallPostCastHook(cb)
        _CB_ApplyTexture(cb)
        _CB_ApplyShielded(cb)
        _CB_ApplyShieldedGradient(cb)
        _CB_ApplyColor(cb)
    end
end

-- ────────── 시전바 배경 투명도 (구 Additions castbarBgAlpha — 캐스트바 도메인) ──────────
-- 플레이어/대상/주시/펫 시전바 배경 알파 일괄. nil = EUI 기본값(적용 안 함).
local CASTBAR_BG_FRAMES = {
    "EllesmereUIUnitFrames_Player",
    "EllesmereUIUnitFrames_Target",
    "EllesmereUIUnitFrames_Focus",
    "EllesmereUIUnitFrames_Pet",
}
local function ApplyCastbarBg()
    local alpha = GetDB().castbarBgAlpha
    if type(alpha) ~= "number" then return end
    for _, name in ipairs(CASTBAR_BG_FRAMES) do
        local frame = _G[name]
        local castbar = frame and frame.Castbar
        local castbarBg = castbar and castbar:GetParent()
        if castbarBg and castbarBg.GetRegions then
            for _, rgn in ipairs({ castbarBg:GetRegions() }) do
                if rgn and rgn.GetObjectType and rgn:GetObjectType() == "Texture"
                   and rgn:GetDrawLayer() == "BACKGROUND" then
                    pcall(rgn.SetTexture, rgn, nil)
                    pcall(rgn.SetColorTexture, rgn, 0, 0, 0, alpha)
                end
            end
        end
    end
    local erb = _G.ERB_CastBarFrame
    if erb and erb._bg and erb._bg.SetColorTexture then
        local cb = _G._ERB_AceDB and _G._ERB_AceDB.profile and _G._ERB_AceDB.profile.castBar
        local r = (cb and cb.bgR) or 0
        local g = (cb and cb.bgG) or 0
        local b = (cb and cb.bgB) or 0
        pcall(erb._bg.SetTexture, erb._bg, nil)
        pcall(erb._bg.SetColorTexture, erb._bg, r, g, b, alpha)
    end
end
_G.EllesmereUIKorCastbar_ApplyBg = ApplyCastbarBg   -- 외부(초기화 버튼 등) 호출용

-- ────────── 이벤트 ──────────
do
    local f = CreateFrame("Frame")
    f:RegisterEvent("PLAYER_FOCUS_CHANGED")
    f:RegisterEvent("PLAYER_TARGET_CHANGED")
    f:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT")
    f:RegisterEvent("PLAYER_ENTERING_WORLD")
    f:RegisterEvent("SPELLS_CHANGED")
    f:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
    f:RegisterEvent("PLAYER_LOGIN")
    f:SetScript("OnEvent", function(_, event)
        if event == "PLAYER_LOGIN" then
            MigrateFromAdditions()
        end
        if event == "PLAYER_ENTERING_WORLD" or event == "SPELLS_CHANGED"
           or event == "PLAYER_SPECIALIZATION_CHANGED" or event == "PLAYER_LOGIN" then
            pcall(_CB_RefreshKick)
        end
        pcall(_CB_ApplyAll)
        pcall(ApplyCastbarBg)
    end)
end

-- ────────── 팝업 ──────────
local _popup, _dimmer
local function CreatePopup()
    if _popup then return _popup, _dimmer end

    _dimmer = CreateFrame("Frame", nil, UIParent)
    _dimmer:SetAllPoints(UIParent)
    _dimmer:SetFrameStrata("FULLSCREEN_DIALOG")
    _dimmer:EnableMouse(true)
    SolidTex(_dimmer, "BACKGROUND", 0, 0, 0, 0.5):SetAllPoints()
    _dimmer:Hide()
    _dimmer:SetScript("OnMouseDown", function(_, btn) if btn == "RightButton" then _dimmer:Hide() end end)
    _dimmer:EnableKeyboard(true)
    _dimmer:SetPropagateKeyboardInput(true)
    _dimmer:SetScript("OnKeyDown", function(self, key) if key == "ESCAPE" then self:Hide() end end)

    local CBP_W, CBP_H = 520, 560
    local CBP_SCALE_MIN, CBP_SCALE_MAX, CBP_SCALE_STEP = 0.5, 2.0, 0.05
    local COL_W = 480

    local p = CreateFrame("Frame", "CIMCastbarPopupStandalone", _dimmer)
    _popup = p
    p:SetSize(CBP_W, CBP_H)
    p:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
    p:SetFrameStrata("FULLSCREEN_DIALOG")
    p:EnableMouse(true)
    p:SetClampedToScreen(true)
    SolidTex(p, "BACKGROUND", POPUP_MAIN_BG[1], POPUP_MAIN_BG[2], POPUP_MAIN_BG[3], POPUP_MAIN_BG[4]):SetAllPoints()
    MakeBorder(p, 0.7, 0.7, 0.75, 0.25)

    -- 타이틀
    local title = MakeFont(p, 14, nil, 1, 1, 1)
    title:SetPoint("TOPLEFT", p, "TOPLEFT", 16, -14)
    title:SetText("시전바 옵션 (대상 / 주시대상)")
    local sub = MakeFont(p, 9, nil, 0.7, 0.7, 0.7)
    sub:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -2)
    sub:SetText("저장 즉시 적용 — 시전 시작 시점에 반영됨")

    -- 우상단 팝업 배율
    local scaleRow = CreateFrame("Frame", nil, p)
    scaleRow:SetSize(120, 22)
    scaleRow:SetPoint("TOPRIGHT", p, "TOPRIGHT", -16, -14)
    SolidTex(scaleRow, "BACKGROUND", POPUP_STEP_BG[1], POPUP_STEP_BG[2], POPUP_STEP_BG[3], POPUP_STEP_BG[4]):SetAllPoints()
    local scaleVal = MakeFont(scaleRow, 11, nil, 0.9, 0.9, 0.95)
    scaleVal:SetPoint("CENTER")
    local function _updPopupScale()
        local s = GetDB().castbarPopupScale or 1.0
        scaleVal:SetText(string.format("크기 %.2fx", s))
        pcall(p.SetScale, p, s)
    end
    local sL = MakeStepBtn(scaleRow, "LEFT", "◀", function()
        local db = GetDB()
        db.castbarPopupScale = math.max(CBP_SCALE_MIN, (db.castbarPopupScale or 1.0) - CBP_SCALE_STEP)
        _updPopupScale()
    end)
    local sR = MakeStepBtn(scaleRow, "RIGHT", "▶", function()
        local db = GetDB()
        db.castbarPopupScale = math.min(CBP_SCALE_MAX, (db.castbarPopupScale or 1.0) + CBP_SCALE_STEP)
        _updPopupScale()
    end)
    sL:SetFrameLevel(scaleRow:GetFrameLevel() + 1)
    sR:SetFrameLevel(scaleRow:GetFrameLevel() + 1)
    _updPopupScale()

    local function _divCB(hdr)
        local ln = SolidTex(p, "ARTWORK", 0.45, 0.55, 0.70, 0.55)
        ln:SetPoint("BOTTOMLEFT", hdr, "TOPLEFT", -5, 6)
        ln:SetSize(COL_W - 7, 2)
        local hbg = SolidTex(p, "BACKGROUND", 0.01, 0.02, 0.03, 1)
        hbg:SetPoint("TOPLEFT",  ln, "BOTTOMLEFT",  0, 3)
        hbg:SetPoint("TOPRIGHT", ln, "BOTTOMRIGHT", 0, 3)
        hbg:SetHeight(26)
    end

    local sectionY = -56

    -- 섹션 1: 시전바 색상
    local h1 = MakeFont(p, 11, nil, 0.7, 0.85, 1)
    h1:SetPoint("TOPLEFT", p, "TOPLEFT", 20, sectionY)
    h1:SetText("대상/주시대상 시전바")
    _divCB(h1)
    local d1 = MakeFont(p, 9, nil, 0.6, 0.6, 0.65)
    d1:SetPoint("TOPLEFT", h1, "BOTTOMLEFT", 0, -10)
    d1:SetText("그라데이션 ON 시 단색 무시. 차단불가는 보스의 차단 불가 시전.")

    local _refreshSolid
    local solidSw = MakeSwitch(p, d1, -10, "일반 단색", "castbarSolidEnabled", function() _CB_ApplyAll(); if _refreshSolid then _refreshSolid() end end)
    local solidBtn = MakeColorSwatch(p,
        function() local c = GetDB().castbarSolidColor or { r=1,g=1,b=1,a=1 }; return c.r, c.g, c.b, c.a or 1 end,
        function(r, g, b, a) GetDB().castbarSolidColor = { r=r, g=g, b=b, a=a or 1 }; _CB_ApplyAll() end,
        true, 20)
    solidBtn:SetPoint("LEFT", solidSw, "RIGHT", 24, 0)
    -- 단색 체크해야 색상 버튼 표시
    _refreshSolid = function()
        if GetDB().castbarSolidEnabled then solidBtn:Show() else solidBtn:Hide() end
    end
    _refreshSolid()

    local _refreshGrad
    local gSw = MakeSwitch(p, solidSw, -8, "일반 그라데이션", "castbarGradientEnabled", function() _CB_ApplyAll(); if _refreshGrad then _refreshGrad() end end)
    local gStartLbl = MakeFont(p, 10, nil, 0.7, 0.7, 0.75)
    gStartLbl:SetPoint("LEFT", gSw, "RIGHT", 18, 0)
    gStartLbl:SetText("시작")
    local gStartBtn = MakeColorSwatch(p,
        function() local c = GetDB().castbarGradientStartColor or { r=0,g=0,b=0,a=1 }; return c.r, c.g, c.b, c.a or 1 end,
        function(r, g, b, a) GetDB().castbarGradientStartColor = { r=r, g=g, b=b, a=a or 1 }; _CB_ApplyAll() end,
        true, 20)
    gStartBtn:SetPoint("LEFT", gStartLbl, "RIGHT", 4, 0)
    local gEndLbl = MakeFont(p, 10, nil, 0.7, 0.7, 0.75)
    gEndLbl:SetPoint("LEFT", gStartBtn, "RIGHT", 8, 0)
    gEndLbl:SetText("끝")
    local gEndBtn = MakeColorSwatch(p,
        function() local c = GetDB().castbarGradientColor or { r=1,g=1,b=1,a=1 }; return c.r, c.g, c.b, c.a or 1 end,
        function(r, g, b, a) GetDB().castbarGradientColor = { r=r, g=g, b=b, a=a or 1 }; _CB_ApplyAll() end,
        true, 20)
    gEndBtn:SetPoint("LEFT", gEndLbl, "RIGHT", 4, 0)

    local gDirLbl = MakeFont(p, 11, nil, 0.9, 0.9, 0.95)
    gDirLbl:SetPoint("LEFT", gEndBtn, "RIGHT", 16, 0)
    gDirLbl:SetText("방향:")
    local function MakeRadio(parent, anchor, label, val, x, dbKey, dbDefault)
        local b = CreateFrame("Button", nil, parent)
        b:SetSize(50, 20)
        b:SetPoint("LEFT", anchor, "RIGHT", x, 0)
        b._bg = SolidTex(b, "BACKGROUND", POPUP_BTN_BG[1], POPUP_BTN_BG[2], POPUP_BTN_BG[3], POPUP_BTN_BG[4])
        b._bg:SetAllPoints()
        b._fs = MakeFont(b, 10, "OUTLINE", 0.75, 0.75, 0.78)
        b._fs:SetPoint("CENTER")
        b._fs:SetText(label)
        b._val = val
        b._dbKey = dbKey
        b._dbDefault = dbDefault
        return b
    end
    local function refreshRadio(group)
        for _, b in ipairs(group) do
            local cur = GetDB()[b._dbKey] or b._dbDefault
            if b._val == cur then
                b._bg:SetColorTexture(0.18, 0.42, 0.65, 0.85)
                b._fs:SetTextColor(1, 1, 1, 1)
            else
                b._bg:SetColorTexture(POPUP_BTN_BG[1], POPUP_BTN_BG[2], POPUP_BTN_BG[3], POPUP_BTN_BG[4])
                b._fs:SetTextColor(0.75, 0.75, 0.78, 1)
            end
        end
    end
    local function wireRadio(group)
        for _, b in ipairs(group) do
            b:SetScript("OnEnter", function() if (GetDB()[b._dbKey] or b._dbDefault) ~= b._val then b._bg:SetColorTexture(POPUP_BTN_HOVER[1], POPUP_BTN_HOVER[2], POPUP_BTN_HOVER[3], POPUP_BTN_HOVER[4]) end end)
            b:SetScript("OnLeave", function() refreshRadio(group) end)
            b:SetScript("OnClick", function()
                GetDB()[b._dbKey] = b._val
                refreshRadio(group)
                _CB_ApplyAll()
            end)
        end
    end
    local gH = MakeRadio(p, gDirLbl, "가로", "HORIZONTAL", 6, "castbarGradientDir", "HORIZONTAL")
    local gV = MakeRadio(p, gH,      "세로", "VERTICAL",   2, "castbarGradientDir", "HORIZONTAL")
    wireRadio({ gH, gV }); refreshRadio({ gH, gV })
    -- 그라데이션 체크해야 시작/끝 색·방향 표시
    _refreshGrad = function()
        local on = (GetDB().castbarGradientEnabled ~= false) and (GetDB().castbarGradientEnabled and true or false)
        local f = on and "Show" or "Hide"
        for _, w in ipairs({ gStartLbl, gStartBtn, gEndLbl, gEndBtn, gDirLbl, gH, gV }) do w[f](w) end
    end
    _refreshGrad()

    -- 차단불가
    local _refreshShSolid
    local sh1Sw = MakeSwitch(p, gSw, -16, "차단불가 단색", "castbarShieldedEnabled", function() _CB_ApplyAll(); if _refreshShSolid then _refreshShSolid() end end)
    local sh1Btn = MakeColorSwatch(p,
        function() local c = GetDB().castbarShieldedColor or { r=0.8,g=0.2,b=0.2,a=1 }; return c.r, c.g, c.b, c.a or 1 end,
        function(r, g, b, a) GetDB().castbarShieldedColor = { r=r, g=g, b=b, a=a or 1 }; _CB_ApplyAll() end,
        true, 20)
    sh1Btn:SetPoint("LEFT", sh1Sw, "RIGHT", 24, 0)
    -- 차단불가 단색 체크해야 색상 버튼 표시
    _refreshShSolid = function()
        if GetDB().castbarShieldedEnabled then sh1Btn:Show() else sh1Btn:Hide() end
    end
    _refreshShSolid()

    local _refreshShGrad
    local shGSw = MakeSwitch(p, sh1Sw, -8, "차단불가 그라데이션", "castbarShieldedGradientEnabled", function() _CB_ApplyAll(); if _refreshShGrad then _refreshShGrad() end end)
    local shGStartLbl = MakeFont(p, 10, nil, 0.7, 0.7, 0.75)
    shGStartLbl:SetPoint("LEFT", shGSw, "RIGHT", 18, 0)
    shGStartLbl:SetText("시작")
    local shGStartBtn = MakeColorSwatch(p,
        function() local c = GetDB().castbarShieldedGradientStartColor or { r=0,g=0,b=0,a=1 }; return c.r, c.g, c.b, c.a or 1 end,
        function(r, g, b, a) GetDB().castbarShieldedGradientStartColor = { r=r, g=g, b=b, a=a or 1 }; _CB_ApplyAll() end,
        true, 20)
    shGStartBtn:SetPoint("LEFT", shGStartLbl, "RIGHT", 4, 0)
    local shGEndLbl = MakeFont(p, 10, nil, 0.7, 0.7, 0.75)
    shGEndLbl:SetPoint("LEFT", shGStartBtn, "RIGHT", 8, 0)
    shGEndLbl:SetText("끝")
    local shGEndBtn = MakeColorSwatch(p,
        function() local c = GetDB().castbarShieldedGradientColor or { r=0.8,g=0.2,b=0.2,a=1 }; return c.r, c.g, c.b, c.a or 1 end,
        function(r, g, b, a) GetDB().castbarShieldedGradientColor = { r=r, g=g, b=b, a=a or 1 }; _CB_ApplyAll() end,
        true, 20)
    shGEndBtn:SetPoint("LEFT", shGEndLbl, "RIGHT", 4, 0)
    local shGDirLbl = MakeFont(p, 11, nil, 0.9, 0.9, 0.95)
    shGDirLbl:SetPoint("LEFT", shGEndBtn, "RIGHT", 16, 0)
    shGDirLbl:SetText("방향:")
    local shGH = MakeRadio(p, shGDirLbl, "가로", "HORIZONTAL", 6, "castbarShieldedGradientDir", "HORIZONTAL")
    local shGV = MakeRadio(p, shGH,      "세로", "VERTICAL",   2, "castbarShieldedGradientDir", "HORIZONTAL")
    wireRadio({ shGH, shGV }); refreshRadio({ shGH, shGV })
    -- 차단불가 그라데이션 체크해야 시작/끝 색·방향 표시
    _refreshShGrad = function()
        local on = GetDB().castbarShieldedGradientEnabled and true or false
        local f = on and "Show" or "Hide"
        for _, w in ipairs({ shGStartLbl, shGStartBtn, shGEndLbl, shGEndBtn, shGDirLbl, shGH, shGV }) do w[f](w) end
    end
    _refreshShGrad()

    -- 텍스처
    local h3 = MakeFont(p, 11, nil, 0.7, 0.85, 1)
    h3:SetPoint("TOPLEFT", shGSw, "BOTTOMLEFT", 0, -30)
    h3:SetText("텍스처")
    _divCB(h3)
    local d3 = MakeFont(p, 9, nil, 0.6, 0.6, 0.65)
    d3:SetPoint("TOPLEFT", h3, "BOTTOMLEFT", 0, -10)
    d3:SetText("시전바 StatusBar 텍스처 — 단색이 기본, 그라데이션은 단색에 잘 어울림.")

    local tLbl = MakeFont(p, 11, nil, 0.9, 0.9, 0.95)
    tLbl:SetPoint("TOPLEFT", d3, "BOTTOMLEFT", 0, -10)
    tLbl:SetText("종류:")

    local dd = CreateFrame("Frame", "CIMCastbarTexDDStandalone", p, "UIDropDownMenuTemplate")
    dd:SetPoint("LEFT", tLbl, "RIGHT", -16, 0)
    UIDropDownMenu_SetWidth(dd, 150)

    local prev = p:CreateTexture(nil, "ARTWORK")
    prev:SetSize(180, 16)
    prev:SetPoint("LEFT", dd, "RIGHT", -8, 2)

    local function _updPrev()
        local key = GetDB().castbarTextureKey or "none"
        local opt = _CB_GetTextureOptByKey(key)
        prev:SetTexture(opt.path)
        prev:SetVertexColor(0.8, 0.8, 0.9, 1)
    end

    UIDropDownMenu_Initialize(dd, function(self, level)
        for _, opt in ipairs(CASTBAR_TEXTURE_OPTIONS) do
            local info = UIDropDownMenu_CreateInfo()
            info.text = opt.label
            info.value = opt.key
            info.checked = (GetDB().castbarTextureKey or "none") == opt.key
            info.func = function()
                GetDB().castbarTextureKey = opt.key
                UIDropDownMenu_SetSelectedValue(dd, opt.key)
                UIDropDownMenu_SetText(dd, opt.label)
                _updPrev()
                _CB_ApplyAll()
            end
            UIDropDownMenu_AddButton(info, level)
        end
    end)
    do
        local cur = GetDB().castbarTextureKey or "none"
        local opt = _CB_GetTextureOptByKey(cur)
        UIDropDownMenu_SetSelectedValue(dd, cur)
        UIDropDownMenu_SetText(dd, opt.label)
        _updPrev()
    end

    -- 시전바 배경 투명도 (구 Additions castbarBgAlpha — 캐스트바 도메인으로 통합)
    local h4 = MakeFont(p, 11, nil, 0.7, 0.85, 1)
    h4:SetPoint("TOPLEFT", tLbl, "BOTTOMLEFT", 0, -34)
    h4:SetText("배경 투명도")
    _divCB(h4)
    local d4 = MakeFont(p, 9, nil, 0.6, 0.6, 0.65)
    d4:SetPoint("TOPLEFT", h4, "BOTTOMLEFT", 0, -10)
    d4:SetText("플레이어/대상/주시/펫 시전바 배경 투명도 일괄. 초기화 = EUI 기본값.")
    local aRow = CreateFrame("Frame", nil, p)
    aRow:SetSize(150, 24)
    aRow:SetPoint("TOPLEFT", d4, "BOTTOMLEFT", 0, -10)
    aRow:SetFrameLevel((p:GetFrameLevel() or 0) + 10)
    SolidTex(aRow, "BACKGROUND", POPUP_STEP_BG[1], POPUP_STEP_BG[2], POPUP_STEP_BG[3], POPUP_STEP_BG[4]):SetAllPoints()
    local aVal = MakeFont(aRow, 12, nil, 0.9, 0.9, 0.95)
    aVal:SetPoint("CENTER")
    local function _updA()
        local a = GetDB().castbarBgAlpha
        if type(a) == "number" then aVal:SetText(string.format("투명도 %.2f", a))
        else aVal:SetText("|cffaaffaa기본값|r") end
    end
    local function _stepA(delta)
        local db = GetDB()
        local a = type(db.castbarBgAlpha) == "number" and db.castbarBgAlpha or 0.5
        a = math.max(0, math.min(1, a + delta))
        db.castbarBgAlpha = a; _updA(); ApplyCastbarBg()
    end
    local aL = MakeStepBtn(aRow, "LEFT",  "◀", function() _stepA(-0.05) end)
    local aR = MakeStepBtn(aRow, "RIGHT", "▶", function() _stepA(0.05) end)
    aL:SetFrameLevel(aRow:GetFrameLevel() + 1)
    aR:SetFrameLevel(aRow:GetFrameLevel() + 1)
    local aReset = CreateFrame("Button", nil, p)
    aReset:SetSize(70, 22)
    aReset:SetPoint("LEFT", aRow, "RIGHT", 8, 0)
    local arBg = SolidTex(aReset, "BACKGROUND", POPUP_BTN_BG[1], POPUP_BTN_BG[2], POPUP_BTN_BG[3], POPUP_BTN_BG[4])
    arBg:SetAllPoints()
    local arLbl = MakeFont(aReset, 10, "OUTLINE", 0.75, 0.75, 0.78)
    arLbl:SetPoint("CENTER"); arLbl:SetText("초기화")
    aReset:SetScript("OnEnter", function() arBg:SetColorTexture(POPUP_BTN_HOVER[1], POPUP_BTN_HOVER[2], POPUP_BTN_HOVER[3], POPUP_BTN_HOVER[4]) end)
    aReset:SetScript("OnLeave", function() arBg:SetColorTexture(POPUP_BTN_BG[1], POPUP_BTN_BG[2], POPUP_BTN_BG[3], POPUP_BTN_BG[4]) end)
    aReset:SetScript("OnClick", function()
        GetDB().castbarBgAlpha = nil; _updA()
        if type(_G._EUF_ReloadFrames) == "function" then pcall(_G._EUF_ReloadFrames) end
        if _G._ERB_Apply then pcall(_G._ERB_Apply) end
    end)
    _updA()

    -- 닫기
    local closeBtn = CreateFrame("Button", nil, p)
    closeBtn:SetSize(100, 26)
    closeBtn:SetPoint("BOTTOM", p, "BOTTOM", 0, 16)
    local closeBg = SolidTex(closeBtn, "BACKGROUND", POPUP_BTN_BG[1], POPUP_BTN_BG[2], POPUP_BTN_BG[3], POPUP_BTN_BG[4])
    closeBg:SetAllPoints()
    MakeBorder(closeBtn, 0.5, 0.7, 1.0, 0.6)
    local closeLbl = MakeFont(closeBtn, 12, "OUTLINE", 1, 1, 1)
    closeLbl:SetPoint("CENTER")
    closeLbl:SetText("닫기")
    closeBtn:SetScript("OnEnter", function() closeBg:SetColorTexture(0.25, 0.55, 0.85, 0.95) end)
    closeBtn:SetScript("OnLeave", function() closeBg:SetColorTexture(POPUP_BTN_BG[1], POPUP_BTN_BG[2], POPUP_BTN_BG[3], POPUP_BTN_BG[4]) end)
    closeBtn:SetScript("OnClick", function() _dimmer:Hide() end)

    return p, _dimmer
end

local function TogglePopup()
    local _, d = CreatePopup()
    if d:IsShown() then d:Hide() else d:Show() end
end

-- ────────── EUI 메인 프레임 우상단 버튼 ("핀 크기" 왼쪽) ──────────
-- "추가 기능": TOPRIGHT (-59, -11) | "핀 크기": TOPRIGHT (-139, -11) | "시전바": TOPRIGHT (-219, -11)
local function InstallButton()
    local mf = _G.EllesmereUI and _G.EllesmereUI._mainFrame
    if not mf or mf.__cbOptBtn then return end
    local clickArea = _G.EllesmereUIClickArea
    if not clickArea then return end

    local btn = CreateFrame("Button", nil, clickArea)
    btn:SetSize(76, 38)
    btn:SetPoint("TOPRIGHT", clickArea, "TOPRIGHT", -219, -11)
    btn:SetFrameLevel(clickArea:GetFrameLevel() + 21)
    mf.__cbOptBtn = btn

    local bg = SolidTex(btn, "BACKGROUND", 0.08, 0.10, 0.14, 0.30)
    bg:SetAllPoints()
    local borders = MakeBorder(btn, 0.5, 0.7, 1.0, 0.45)

    local fs = btn:CreateFontString(nil, "OVERLAY")
    fs:SetFont(GetFont(), 13, "OUTLINE")
    fs:SetPoint("CENTER")
    fs:SetText("|cff0091ed시전바|r")

    btn:SetScript("OnEnter", function()
        bg:SetColorTexture(0.15, 0.20, 0.30, 0.55)
        SetBorderColor(borders, 0.5, 0.7, 1.0, 1)
        GameTooltip:SetOwner(btn, "ANCHOR_TOP")
        GameTooltip:AddLine("시전바 옵션 (대상/주시/보스)", 0.4, 0.7, 1)
        GameTooltip:AddLine("색상·그라데이션·텍스처·인터럽트 CD 색 변화.", 0.7, 0.7, 0.7, true)
        GameTooltip:Show()
    end)
    btn:SetScript("OnLeave", function()
        bg:SetColorTexture(0.08, 0.10, 0.14, 0.30)
        SetBorderColor(borders, 0.5, 0.7, 1.0, 0.45)
        GameTooltip:Hide()
    end)
    btn:SetScript("OnClick", TogglePopup)
end

local _showHooked = false
local function TryInstallButton()
    if not (_G.EllesmereUI and _G.EllesmereUI._mainFrame and _G.EllesmereUIClickArea) then return false end
    local mf, clickArea = _G.EllesmereUI._mainFrame, _G.EllesmereUIClickArea
    -- 메인 프레임이 생기면 OnShow 후크 1회 설치 → 메뉴를 열 때마다 버튼 재확인.
    -- (EUI 가 clickArea 를 재생성해 버튼이 떨어지거나, 폴링 종료 후 처음 메뉴를 열어도 즉시 재부착)
    if not _showHooked and mf.HookScript then
        _showHooked = true
        mf:HookScript("OnShow", function() TryInstallButton() end)
        -- 패널 표시 중에만 0.3초마다 확인 → 항상 보이게 보장. 닫으면 안 돎(사용량 0).
        local acc = 0
        mf:HookScript("OnUpdate", function(_, elapsed)
            acc = acc + (elapsed or 0)
            if acc < 0.3 then return end
            acc = 0
            TryInstallButton()
        end)
    end
    -- EUI 옵션창 열기/페이지 전환 시 재부착 + 지연 재확인 (clickArea 재생성 대응)
    local EUI = _G.EllesmereUI
    if EUI and not EUI.__cbPanelReattach then
        EUI.__cbPanelReattach = true
        local function deferred()
            TryInstallButton()
            if C_Timer and C_Timer.After then
                C_Timer.After(0.05, TryInstallButton)
                C_Timer.After(0.20, TryInstallButton)
            end
        end
        if type(EUI.Show)        == "function" then hooksecurefunc(EUI, "Show",        deferred) end
        if type(EUI.Toggle)      == "function" then hooksecurefunc(EUI, "Toggle",      deferred) end
        if type(EUI.SelectPage)  == "function" then hooksecurefunc(EUI, "SelectPage",  deferred) end
        if type(EUI.RefreshPage) == "function" then hooksecurefunc(EUI, "RefreshPage", deferred) end
    end
    local btn = mf.__cbOptBtn
    if btn and btn:GetParent() == clickArea then
        if btn.IsShown and not btn:IsShown() and btn.Show then btn:Show() end
        return true
    end
    mf.__cbOptBtn = nil
    InstallButton()
    return mf.__cbOptBtn ~= nil
end

-- ────────── 버튼 폴링 ──────────
do
    local f = CreateFrame("Frame")
    f:RegisterEvent("PLAYER_LOGIN")
    f:RegisterEvent("PLAYER_ENTERING_WORLD")
    f:SetScript("OnEvent", function()
        if C_Timer and C_Timer.NewTicker then
            local count = 0
            local ticker
            ticker = C_Timer.NewTicker(0.5, function()
                count = count + 1
                TryInstallButton()
                -- OnShow 후크가 걸리면(메인 프레임 등장) 이후엔 그 후크가 버튼을 보장하므로 폴링 종료.
                if _showHooked or count > 600 then
                    if ticker then ticker:Cancel() end
                end
            end)
        end
    end)
end
