local addonName, ns = ...
local Fatebound = ns

-- ================================
-- CONFIGURATION
-- ================================
local PATH_ICONS = {
    OLD_GODS = 3194610,
    PANTHEON = 2032224,
    LOA = 1850551,
}

-- ================================
-- Panel Art Paths
-- ================================
local PANEL_ART = {
    -- Panel 1
    { 
        left = {
            OLD_GODS = "Interface\\AddOns\\Fatebound\\Media\\cthun_art.tga",
            PANTHEON = "Interface\\AddOns\\Fatebound\\Media\\Pantheon_Art2.tga",
            LOA      = "Interface\\AddOns\\Fatebound\\Media\\Loa_Art2.tga",
        },
        right = {
            OLD_GODS = "Interface\\AddOns\\Fatebound\\Media\\cthun_bg_fade.tga",
            PANTHEON = "Interface\\AddOns\\Fatebound\\Media\\Pantheon_Art2.tga",
            LOA      = "Interface\\AddOns\\Fatebound\\Media\\Loa_Art2.tga",
        }
    },
    -- Panel 2
    { 
        left = {
            OLD_GODS = "Interface\\AddOns\\Fatebound\\Media\\yogg_art.tga",
            PANTHEON = "Interface\\AddOns\\Fatebound\\Media\\Pantheon_Art2.tga",
            LOA      = "Interface\\AddOns\\Fatebound\\Media\\Loa_Art2.tga",
        },
        right = {
            OLD_GODS = "Interface\\AddOns\\Fatebound\\Media\\yogg_bg_fade.tga",
            PANTHEON = "Interface\\AddOns\\Fatebound\\Media\\Pantheon_Art2.tga",
            LOA      = "Interface\\AddOns\\Fatebound\\Media\\Loa_Art2.tga",
        }
    },
    -- Panel 3
    { 
        left = {
            OLD_GODS = "Interface\\AddOns\\Fatebound\\Media\\yshaarj_art.tga",
            PANTHEON = "Interface\\AddOns\\Fatebound\\Media\\Pantheon_Art.tga",
            LOA      = "Interface\\AddOns\\Fatebound\\Media\\Loa_Art.tga",
        },
        right = {
            OLD_GODS = "Interface\\AddOns\\Fatebound\\Media\\yshaarj_bg_fade.tga",
            PANTHEON = "Interface\\AddOns\\Fatebound\\Media\\Pantheon_Art.tga",
            LOA      = "Interface\\AddOns\\Fatebound\\Media\\Loa_Art.tga",
        }
    },
    -- Panel 4
    { 
        left = {
            OLD_GODS = "Interface\\AddOns\\Fatebound\\Media\\ghuun_art.tga",
            PANTHEON = "Interface\\AddOns\\Fatebound\\Media\\Pantheon_Art2.tga",
            LOA      = "Interface\\AddOns\\Fatebound\\Media\\Loa_Art2.tga",
        },
        right = {
            OLD_GODS = "Interface\\AddOns\\Fatebound\\Media\\ghuun_bg_fade.tga",
            PANTHEON = "Interface\\AddOns\\Fatebound\\Media\\Pantheon_Art2.tga",
            LOA      = "Interface\\AddOns\\Fatebound\\Media\\Loa_Art2.tga",
        }
    },
    -- Panel 5
    { 
        left = {
            OLD_GODS = "Interface\\AddOns\\Fatebound\\Media\\nzoth_art.tga",
            PANTHEON = "Interface\\AddOns\\Fatebound\\Media\\Pantheon_Art2.tga",
            LOA      = "Interface\\AddOns\\Fatebound\\Media\\Loa_Art2.tga",
        },
        right = {
            OLD_GODS = "Interface\\AddOns\\Fatebound\\Media\\nzoth_bg_fade.tga",
            PANTHEON = "Interface\\AddOns\\Fatebound\\Media\\Pantheon_Art2.tga",
            LOA      = "Interface\\AddOns\\Fatebound\\Media\\Loa_Art2.tga",
        }
    },
}

ns.MAX_EXP = 96
ns.EXP_PER_QUEST = 3
ns.MAX_FAVOR = 40

function Fatebound:UpdateFavorLabel()
    if not FateboundChosenPathFrame or not FateboundChosenPathFrame.favorLabel then return end
    local favor = FateboundDB.favor or 0
    FateboundChosenPathFrame.favorLabel:SetText("Favor: " .. favor .. " / " .. ns.MAX_FAVOR)
end

-- ==================================================
-- Update icon saturation based on favor
-- ==================================================
function Fatebound:UpdateIconSaturation(slot)
	if not slot or not slot.icon then return end

	local favor = FateboundDB.favor or 0

	if favor <= 0 then
		slot.icon:SetDesaturated(true)
	else
		slot.icon:SetDesaturated(false)
	end
end

function Fatebound:RefreshPerkIcons()
	if not FateboundChosenPathFrame then return end

	local chosenPath = FateboundChosenPathFrame

	local Trees = {"CthunPerks", "YoggPerks", "YshaarjPerks", "GhuunPerks", "NzothPerks"}

	for _, treeKey in ipairs(Trees) do
		local slots = chosenPath[treeKey .. "_slots"]
		local eliteSlot = chosenPath[treeKey .. "_elite"]

		if slots then
			for _, slot in ipairs(slots) do
				self:UpdateIconSaturation(slot)
			end
		end

		if eliteSlot then
			self:UpdateIconSaturation(eliteSlot)
		end
	end
end

-- ================================
-- DRAIN TIER MESSAGE FUNCTION
-- ================================
local function GetDrainTierMessage(tier)
    if tier == 1 then
        return "|cffbb2f2aThe Old Gods stir… your favor begins to slip away.|r"
    elseif tier == 2 then
        return "|cffbb2f2aYour bond strains… the Old Gods demand more, and favor drains faster.|r"
    elseif tier == 3 then
        return "|cffbb2f2aThe connection fractures… your favor bleeds away at an alarming rate.|r"
    elseif tier == 4 then
        return "|cffbb2f2aFaith erodes rapidly… the Old Gods barely acknowledge your presence.|r"
    elseif tier == 5 then
        return "|cffbb2f2aThe Old Gods fall silent… what favor remains drains into the void.|r"
    end
end

-- ================================
-- DRAIN EXPERIENCE SYSTEM
-- ================================
local DRAIN_INTERVAL = 10  -- seconds between drain updates
local BASE_PAUSE_DURATION = 30 * 60  -- 30 minutes

local DRAIN_AMOUNTS = {
    [1] = 1,
    [2] = 3,
    [3] = 5,
    [4] = 8,
    [5] = 12,
}

-- Helper: check if drain is paused
local function IsDrainPaused()
    return (FateboundDB.drainPausedUntil or 0) > time()
end

-- Perform drain
local function DrainExp()
    -- Only drain if path is selected, not paused, and player is not devoted
    if IsDrainPaused() or not FateboundDB.path or FateboundDB.devoted then return end

	if not FateboundDB.drainStartTime then
		FateboundDB.drainStartTime = time()
		FateboundDB.lastDrainTier = 0
	end

    -- If player has no exp AND no favor, pause and reset tier ramp
    if FateboundDB.exp == 0 and FateboundDB.favor == 0 then
        FateboundDB.drainStartTime = time()  -- reset tier ramp
        FateboundDB.lastDrainTier = 0
        return
    end

    -- Determine current tier based on elapsed minutes since drainStartTime
    local elapsedMinutes = math.floor((time() - FateboundDB.drainStartTime) / 60)
    local currentTier = math.min(1 + elapsedMinutes, 5)

    -- Show tier messages only if tier increased and player has exp or favor
    if currentTier > (FateboundDB.lastDrainTier or 0) then
        FateboundDB.lastDrainTier = currentTier
        local msg = GetDrainTierMessage(currentTier)
        if msg and (FateboundDB.exp > 0 or FateboundDB.favor > 0) then
            ns.ShowCenterMessage(msg, FateboundDB.path)
        end
    end

    -- Drain EXP
    local drainAmount = DRAIN_AMOUNTS[currentTier] or 1
    FateboundDB.exp = FateboundDB.exp - drainAmount

    -- Overflow into favor if EXP goes below 0
    while FateboundDB.exp < 0 and FateboundDB.favor > 0 do
        FateboundDB.favor = FateboundDB.favor - 1
        FateboundDB.exp = ns.MAX_EXP + FateboundDB.exp
        ns.ShowCenterMessage("|cffbb2f2aYour devotion falters… you lose 1 Favor!|r", FateboundDB.path)
    end

    -- Clamp both to zero
    FateboundDB.exp = math.max(FateboundDB.exp, 0)
    FateboundDB.favor = math.max(FateboundDB.favor, 0)

    -- Fully drained message: only if player had exp/favor to lose
    if FateboundDB.exp == 0 and FateboundDB.favor == 0 and not FateboundDB.fullyDrainedShown then
        ns.ShowCenterMessage(
            "|cffbb2f2aAll Favor Lost! Your devotion has been severed. The Old Gods no longer heed your call.|r",
            FateboundDB.path
        )
        FateboundDB.fullyDrainedShown = true
    end

    -- Reset fullyDrainedShown if player gains exp/favor again
    if FateboundDB.fullyDrainedShown and (FateboundDB.exp > 0 or FateboundDB.favor > 0) then
        FateboundDB.fullyDrainedShown = false
    end
	
	Fatebound:RefreshPerkIcons()
	Fatebound:UpdateFavorLabel()
    Fatebound:RefreshAll()
    ns.UpdateChosenPathFrame()
end

-- ================================
-- Drain OnUpdate Frame
-- ================================
local drainFrame = ns.drainFrame or CreateFrame("Frame")
ns.drainFrame = drainFrame
drainFrame:Show()

local drainElapsed = 0

drainFrame:SetScript("OnUpdate", function(_, dt)
    drainElapsed = drainElapsed + dt
    if drainElapsed < DRAIN_INTERVAL then return end
    drainElapsed = 0

    DrainExp()
end)

-- ================================
-- EXPERIENCE BAR
-- ================================
function Fatebound:UpdateExpBar()
	local frame = FateboundChosenPathFrame
	if not frame or not frame.expBar or not frame.expBar.fill then return end

	local expBar = frame.expBar
	local padding = expBar.fillPadding or 0
	local usableWidth = expBar:GetWidth() - (padding * 2)

	local favor = FateboundDB.favor or 0
	local exp   = FateboundDB.exp or 0

	-- Calculate fill width
	local fillWidth
	if favor >= ns.MAX_FAVOR then
		fillWidth = usableWidth
	else
		fillWidth = (exp / ns.MAX_EXP) * usableWidth
	end
	fillWidth = math.max(0, math.min(fillWidth, usableWidth))

	-- Hide if empty
	if fillWidth <= 0 then
		expBar.fill:Hide()
		return
	end

	-- Set color based on devotion
	if FateboundDB.devoted then
		expBar.fill:SetVertexColor(0.475, 0.333, 0.784, 1) -- purple
	else
		expBar.fill:SetVertexColor(0.733, 0.184, 0.165, 1) -- red
	end

	-- Apply size and position
	expBar.fill:Show()
	expBar.fill:ClearAllPoints()
	expBar.fill:SetPoint("LEFT", expBar, "LEFT", padding, 1)
	expBar.fill:SetWidth(fillWidth)
end


-- ================================
-- Update Frame Content
-- ================================
ns.UpdateChosenPathFrame = function()
    if not FateboundChosenPathFrame then return end

    local pathID = FateboundDB.path or "None"

    -- --- Tab labels ---
    local tabSets = {
        {frame = FateboundChosenPathFrame.pathTab, names = FateboundChosenPathFrame.tabNames},
        {frame = FateboundChosenPathFrame.pathTab2, names = FateboundChosenPathFrame.tabNames2},
        {frame = FateboundChosenPathFrame.pathTab3, names = FateboundChosenPathFrame.tabNames3},
        {frame = FateboundChosenPathFrame.pathTab4, names = FateboundChosenPathFrame.tabNames4},
        {frame = FateboundChosenPathFrame.pathTab5, names = FateboundChosenPathFrame.tabNames5},
        {frame = FateboundChosenPathFrame.pathTabForge, names = FateboundChosenPathFrame.tabNamesForge},
    }

    for _, tab in ipairs(tabSets) do
        if tab.frame then
            tab.frame:SetLabel(tab.names[pathID] or "Unknown")
        end
    end

    -- --- Panel art ---
	local panels = {
		FateboundChosenPathFrame.panel1,
		FateboundChosenPathFrame.panel2,
		FateboundChosenPathFrame.panel3,
		FateboundChosenPathFrame.panel4,
		FateboundChosenPathFrame.panel5,
	}

	for i, panel in ipairs(panels) do
		if panel then
			-- LEFT ART
			if panel.artLeft then
				local tex = PANEL_ART[i].left[pathID]
				if tex then
					panel.artLeft:SetTexture(tex)
					panel.artLeft:Show()
				else
					panel.artLeft:Hide()
				end
			end
			-- RIGHT ART
			if panel.artRight then
				local tex = PANEL_ART[i].right[pathID]
				if tex then
					panel.artRight:SetTexture(tex)
					panel.artRight:Show()
				else
					panel.artRight:Hide()
				end
			end
		end
	end

    -- --- Title ---
    if FateboundChosenPathFrame.title then
        FateboundChosenPathFrame.title:SetText("Fatebound")
    end

    -- --- Path Label ---
    local displayNames = {OLD_GODS = "Old Gods", PANTHEON = "Pantheon", LOA = "Loa"}
    local pathName = displayNames[pathID] or pathID
    if FateboundChosenPathFrame.pathLabel then
        FateboundChosenPathFrame.pathLabel:SetText("Path of the " .. pathName)
    end

    -- --- Icon ---
    if FateboundChosenPathFrame.pathIcon then
        FateboundChosenPathFrame.pathIcon:SetTexture(PATH_ICONS[pathID] or nil)
    end

	Fatebound:RefreshPerkIcons()
    Fatebound:UpdateFavorLabel()
    Fatebound:UpdateExpBar()
	Fatebound:RefreshAll()
end

-- ================================
-- TAB CREATION
-- ================================
local function CreateFateboundTab(parent)
    local tab = CreateFrame("Button", nil, parent)

    tab:SetSize(100, 36)
	tab:SetFrameLevel(parent:GetFrameLevel() - 1)

    ---------------------------------------------------------
    -- UNSELECTED STATE ART
    ---------------------------------------------------------
    -- LEFT
    tab.left = tab:CreateTexture(nil, "ARTWORK")
    tab.left:SetTexture("interface/framegeneral/uiframetabs")
    tab.left:SetSize(35, 36)
    tab.left:SetPoint("LEFT", tab, "LEFT", 0, 0)
    tab.left:SetTexCoord(0.015625, 0.5625, 0.81640625, 0.95703125)

    -- RIGHT
    tab.right = tab:CreateTexture(nil, "ARTWORK")
    tab.right:SetTexture("interface/framegeneral/uiframetabs")
    tab.right:SetSize(37, 36)
    tab.right:SetPoint("RIGHT", tab, "RIGHT", 0, 0)
    tab.right:SetTexCoord(0.015625, 0.59375, 0.66796875, 0.80859375)

    -- MIDDLE (stretchable)
    tab.middle = tab:CreateTexture(nil, "ARTWORK")
    tab.middle:SetTexture("interface/framegeneral/uiframetabs")
    tab.middle:SetPoint("LEFT", tab.left, "RIGHT", 0, 0)
    tab.middle:SetPoint("RIGHT", tab.right, "LEFT", 0, 0)
    tab.middle:SetHeight(36)
    tab.middle:SetTexCoord(0, 0.015625, 0.17578125, 0.31640625)

    ---------------------------------------------------------
    -- SELECTED STATE ART
    ---------------------------------------------------------
    tab.leftSel = tab:CreateTexture(nil, "ARTWORK")
    tab.leftSel:SetTexture("interface/framegeneral/uiframetabs")
    tab.leftSel:SetSize(35, 42)
    tab.leftSel:SetPoint("LEFT", tab, "LEFT", 0, 0)
    tab.leftSel:SetTexCoord(0.015625, 0.5625, 0.49609375, 0.66015625)
    tab.leftSel:Hide()

    tab.rightSel = tab:CreateTexture(nil, "ARTWORK")
    tab.rightSel:SetTexture("interface/framegeneral/uiframetabs")
    tab.rightSel:SetSize(37, 42)
    tab.rightSel:SetPoint("RIGHT", tab, "RIGHT", 0, 0)
    tab.rightSel:SetTexCoord(0.015625, 0.59375, 0.32421875, 0.48828125)
    tab.rightSel:Hide()

    tab.middleSel = tab:CreateTexture(nil, "ARTWORK")
    tab.middleSel:SetTexture("interface/framegeneral/uiframetabs")
    tab.middleSel:SetPoint("LEFT", tab.leftSel, "RIGHT", 0, 0)
    tab.middleSel:SetPoint("RIGHT", tab.rightSel, "LEFT", 0, 0)
    tab.middleSel:SetHeight(42)
    tab.middleSel:SetTexCoord(0, 0.015625, 0.00390625, 0.16796875)
    tab.middleSel:Hide()

    ---------------------------------------------------------
    -- TEXT
    ---------------------------------------------------------
    tab.text = tab:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    tab.text:SetPoint("CENTER", tab, "CENTER", -2, 1)

    ---------------------------------------------------------
    -- LABEL
    ---------------------------------------------------------
    function tab:SetLabel(str)
        str = str and #str > 12 and str:sub(1, 12) .. "…" or str
        tab.text:SetText(str or "")

        local width = tab.text:GetStringWidth() + 20
        if width < 96 then width = 96 end
        tab:SetWidth(width)
    end

	-- -----------------------------
	-- SELECTED STATE TOGGLER
	-- -----------------------------
	function tab:SetSelected(state)
		state = not not state
		tab.selected = state

		tab.leftSel:SetShown(state)
		tab.middleSel:SetShown(state)
		tab.rightSel:SetShown(state)

		tab.left:SetShown(not state)
		tab.middle:SetShown(not state)
		tab.right:SetShown(not state)

		tab:SetHeight(36)

		if state then
			tab.text:SetFontObject("GameFontWhiteSmall")
		else
			tab.text:SetFontObject("GameFontNormalSmall")
		end
	end

	-- Mouseover effects
	tab:SetScript("OnEnter", function(self)
		self.text:SetFontObject("GameFontWhiteSmall")
	end)

	tab:SetScript("OnLeave", function(self)
		if not self.selected then
			self.text:SetFontObject("GameFontNormalSmall")
		end
	end)

	tab:SetSelected(false)

	return tab
end

-- Hide/show Devotion UI
local function SetDevotionUIVisible(visible)
    if not FateboundChosenPathFrame then return end

    local btn = FateboundChosenPathFrame.devotionButton
    local txt = FateboundChosenPathFrame.devotionTimerText

    if visible then
        if txt and (FateboundDB.drainPausedUntil or 0) > time() then
            txt:Show()
        end
        if btn then btn:Show() end
    else
        if btn then btn:Hide() end
        if txt then txt:Hide() end
    end
end

-- ================================
-- FRAME CREATION
-- ================================
local function CreateChosenPathFrame()
    if FateboundChosenPathFrame then return end

	local chosenPath = CreateFrame("Frame", "FateboundChosenPathFrame", UIParent, "PortraitFrameTemplate")
	chosenPath:SetSize(737, 470)
	chosenPath:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
	chosenPath:SetClampedToScreen(true)
	chosenPath:EnableMouse(true)
	chosenPath:SetMovable(true)

	-- Drag handling
	chosenPath:RegisterForDrag("LeftButton")
	chosenPath:SetScript("OnDragStart", function(self) self:StartMoving() end)
	chosenPath:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)

	-- BACKGROUND
	-- --------------------------------------------------
	chosenPath.bg = chosenPath:CreateTexture(nil, "BACKGROUND", nil, 0)
	chosenPath.bg:SetTexture("interface/store/perksactivities")
	chosenPath.bg:SetTexCoord(0.19287109375, 0.57666015625, 0.0849609375, 0.5)
	chosenPath.bg:SetPoint("TOPLEFT", chosenPath, "TOPLEFT", 4, -54)
	chosenPath.bg:SetPoint("BOTTOMRIGHT", chosenPath, "BOTTOMRIGHT", -4, 4)
	
	-- INSET FRAME
	-- --------------------------------------------------
	local inset = CreateFrame("Frame", nil, chosenPath)
	inset:SetPoint("TOPLEFT", chosenPath, "TOPLEFT", 0, -50)
	inset:SetPoint("BOTTOMRIGHT", chosenPath, "BOTTOMRIGHT", 0, 0)
	inset:SetFrameLevel(chosenPath:GetFrameLevel() + 10) -- important

	chosenPath.inset = inset

	-- BORDERS
	-- --------------------------------------------------
	-- Corners
	local tl = inset:CreateTexture(nil, "OVERLAY")
	tl:SetTexture("interface/framegeneral/generalframeinsetborders")
	tl:SetTexCoord(0.703125, 0.828125, 0.03125, 0.28125)
	tl:SetPoint("TOPLEFT", inset, "TOPLEFT", 4, -4)
	tl:SetSize(9, 9)

	local tr = inset:CreateTexture(nil, "OVERLAY")
	tr:SetTexture("interface/framegeneral/generalframeinsetborders")
	tr:SetTexCoord(0.859375, 0.984375, 0.03125, 0.28125)
	tr:SetPoint("TOPRIGHT", inset, "TOPRIGHT", -4, -4)
	tr:SetSize(8, 8)

	local bl = inset:CreateTexture(nil, "OVERLAY")
	bl:SetTexture("interface/framegeneral/generalframeinsetborders")
	bl:SetTexCoord(0.328125, 0.453125, 0.6875, 0.9375)
	bl:SetPoint("BOTTOMLEFT", inset, "BOTTOMLEFT", 4, 4)
	bl:SetSize(8, 8)

	local br = inset:CreateTexture(nil, "OVERLAY")
	br:SetTexture("interface/framegeneral/generalframeinsetborders")
	br:SetTexCoord(0.515625, 0.640625, 0.6875, 0.9375)
	br:SetPoint("BOTTOMRIGHT", inset, "BOTTOMRIGHT", -4, 4)
	br:SetSize(8, 8)

	-- Sides
	local top = inset:CreateTexture(nil, "OVERLAY")
	top:SetTexture("interface/framegeneral/generalframeinsetbordershorizontal")
	top:SetTexCoord(0, 0.5, 0.65625, 0.90625)
	top:SetPoint("TOPLEFT", tl, "TOPRIGHT", 0, 0)
	top:SetPoint("TOPRIGHT", tr, "TOPLEFT", 0, 0)
	top:SetHeight(8)
	top:SetHorizTile(true)

	local bottom = inset:CreateTexture(nil, "OVERLAY")
	bottom:SetTexture("interface/framegeneral/generalframeinsetbordershorizontal")
	bottom:SetTexCoord(0, 0.5, 0.34375, 0.59375)
	bottom:SetPoint("BOTTOMLEFT", bl, "BOTTOMRIGHT", 0, 0)
	bottom:SetPoint("BOTTOMRIGHT", br, "BOTTOMLEFT", 0, 0)
	bottom:SetHeight(8)
	bottom:SetHorizTile(true)

	local left = inset:CreateTexture(nil, "OVERLAY")
	left:SetTexture("interface/framegeneral/generalframeinsetborders")
	left:SetTexCoord(0.015625, 0.140625, 0, 1)
	left:SetPoint("TOPLEFT", tl, "BOTTOMLEFT", 0, 0)
	left:SetPoint("BOTTOMLEFT", bl, "TOPLEFT", 0, 0)
	left:SetWidth(8)
	left:SetVertTile(true)

	local right = inset:CreateTexture(nil, "OVERLAY")
	right:SetTexture("interface/framegeneral/generalframeinsetborders")
	right:SetTexCoord(0.171875, 0.296875, 0, 1)
	right:SetPoint("TOPRIGHT", tr, "BOTTOMRIGHT", 0, 0)
	right:SetPoint("BOTTOMRIGHT", br, "TOPRIGHT", 0, 0)
	right:SetWidth(8)
	right:SetVertTile(true)

	-- Save references
	inset.tl = tl
	inset.tr = tr
	inset.bl = bl
	inset.br = br
	inset.top = top
	inset.bottom = bottom
	inset.left = left
	inset.right = right

	-- Frame title
	local titleName = chosenPath:GetName() and (chosenPath:GetName() .. "TitleText") or nil
	if titleName and _G[titleName] then
		_G[titleName]:SetText("Fatebound")
	else
		chosenPath.Title = chosenPath:CreateFontString(nil, "ARTWORK", "GameFontNormal")
		chosenPath.Title:SetPoint("TOP", chosenPath, "TOP", 0, -8)
		chosenPath.Title:SetText("Fatebound")
	end
	
	-- Portrait
	local pathPortrait = chosenPath.Portrait or _G[chosenPath:GetName() .. "Portrait"]
	if pathPortrait then
		pathPortrait:SetTexture(132886)
	else
		-- Create a fallback portrait texture in the corner
		pathPortrait = chosenPath:CreateTexture(nil, "ARTWORK")
		pathPortrait:SetSize(64, 64)
		pathPortrait:SetPoint("TOPLEFT", 12, -36)
		pathPortrait:SetTexture(132886)
	end

	-- --------------------------------------------------
	-- Panels & descriptions
	local PANEL_TITLES = {
		[1] = "C'Thun",
		[2] = "Yogg-Saron",
		[3] = "Y’Shaarj",
		[4] = "G'huun",
		[5] = "N'Zoth",
	}

	local PANEL_BODIES = {
		[1] = "The Old God of madness and chaos, ruler of the qiraji. Imprisoned beneath Ahn’Qiraj for millennia, it twisted its minions into relentless servants of destruction. Though ultimately defeated by Azeroth’s champions, C’Thun’s pervasive influence and nightmarish power left lasting scars on the land and the minds of those who faced it.",
		[2] = "The Old God of death and madness, imprisoned in Ulduar for millennia. Its corrupting influence spread through saronite and the Emerald Dream, warping mortals and the titan-forged alike. Though defeated by Azeroth’s champions, Yogg-Saron’s whispers and nightmarish power continue to haunt the world.",
		[3] = "The strongest of the Old Gods, slain by the titans at terrible cost. Its dying breath birthed the sha, living echoes of despair and fear. Though its heart once pulsed beneath Pandaria, nothing of Y’Shaarj now remains\n—save the scars it carved into Azeroth’s soul.",
		[4] = "An artificial Old God born from titan experiments, known as the Blood God. Its essence is pure rot, pestilence, and decay, and it sought to spread corruption across Azeroth. Though worshiped by the blood trolls and nearly unleashed from Uldir, Azeroth’s champions ultimately destroyed it.",
		[5] = "The insidious Old God of the Deep, master of shadows and corruption. Hidden beneath the oceans for millennia, it twisted mortals and heroes alike, bending even mighty beings like Deathwing and Queen Azshara to its will. Its awakening brought Ny’alotha, the Sleeping City, and the resurgence of the Black Empire, spreading nightmares and despair across Azeroth. Though ultimately defeated, its whispers linger, promising power to those who dare listen.",
	}

	-- --------------------------------------------------
	-- Panels container
	chosenPath.panels = {}

	for i = 1, 5 do
		local panel = CreateFrame("Frame", nil, chosenPath)
		panel:SetAllPoints()

		if i == 1 then
			panel:Show()
		else
			panel:Hide()
		end

		chosenPath["panel"..i] = panel
		chosenPath.panels[i] = panel

		-- --------------------------------------------------
		-- Panel artwork right
		local artRight = panel:CreateTexture(nil, "ARTWORK")
		artRight:SetSize(727, 460)
		artRight:SetPoint("BOTTOM", 0, 5)
		artRight:SetDrawLayer("ARTWORK", 1)
		artRight:SetAlpha(0.8)
		panel.artRight = artRight

		-- --------------------------------------------------
		-- Panel artwork left
		local artLeft = panel:CreateTexture(nil, "ARTWORK")
		artLeft:SetSize(266, 306)
		artLeft:SetPoint("LEFT", 25, -55)
		artLeft:SetDrawLayer("ARTWORK", 3)
		artLeft:SetAlpha(0.8)
		panel.artLeft = artLeft

		-- --------------------------------------------------
		-- Panel description title (large)
		local title = panel:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
		title:SetWidth(230)
		title:SetJustifyH("LEFT")
		title:SetJustifyV("TOP")
		title:SetPoint("TOP", artLeft, "TOP", 0, -20)
		title:SetText(PANEL_TITLES[i] or "")
		title:SetDrawLayer("OVERLAY", 7)
		title:SetShadowColor(0, 0, 0, 0.8)
		title:SetShadowOffset(1, -1)
		do
			local font, _, flags = title:GetFont()
			title:SetFont(font, 18, flags)
		end
		panel.descriptionTitle = title

		-- --------------------------------------------------
		-- Panel description body (normal font)
		local body = panel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
		body:SetWidth(230)
		body:SetJustifyH("LEFT")
		body:SetJustifyV("TOP")
		body:SetPoint("TOPLEFT", title, "TOPLEFT", 0, -40)
		body:SetText(PANEL_BODIES[i] or "")
		body:SetDrawLayer("OVERLAY", 7)
		body:SetShadowColor(0, 0, 0, 0.8)
		body:SetShadowOffset(1, -1)
		do
			local font, _, flags = body:GetFont()
			body:SetFont(font, 14, flags)
		end
		panel.descriptionBody = body

		-- Hide descriptions by default
		title:Hide()
		body:Hide()
	end

	-- --------------------------------------------------
	-- Description visibility checkbox
	local showDescCheckbox = CreateFrame("CheckButton", nil, chosenPath, "UICheckButtonTemplate")
	showDescCheckbox:SetPoint("BOTTOMLEFT", chosenPath, "BOTTOMLEFT", 15, 20)
	showDescCheckbox:SetFrameStrata(chosenPath:GetFrameStrata())
	showDescCheckbox:SetFrameLevel(chosenPath:GetFrameLevel() + 30)
	showDescCheckbox:SetChecked(false)

	showDescCheckbox.text = showDescCheckbox:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	showDescCheckbox.text:SetPoint("LEFT", showDescCheckbox, "RIGHT", 4, 0)
	showDescCheckbox.text:SetText("Show Description")
	showDescCheckbox.text:SetDrawLayer("OVERLAY", 7)

	-- --------------------------------------------------
	-- Checkbox logic
	local function UpdatePanelDescriptions()
		local show = showDescCheckbox:GetChecked()
		for _, panel in ipairs(chosenPath.panels) do
			if panel.descriptionTitle and panel.descriptionBody then
				-- Toggle descriptions
				panel.descriptionTitle:SetShown(show)
				panel.descriptionBody:SetShown(show)

				-- Fade and desaturate left artwork instead of hiding
				if show then
					panel.artLeft:SetDesaturated(true)
					panel.artLeft:SetVertexColor(0.6, 0.6, 0.6, 0.5) -- faded grayish
				else
					panel.artLeft:SetDesaturated(false)
					panel.artLeft:SetVertexColor(1, 1, 1, 0.8) -- normal color
				end
			end
		end
	end

	showDescCheckbox:SetScript("OnClick", UpdatePanelDescriptions)

	-- Initial sync (descriptions hidden by default)
	UpdatePanelDescriptions()

	-- ==================================================
	-- FORGE PANEL
	-- ==================================================
	chosenPath.panelForge = CreateFrame("Frame", nil, chosenPath)
	chosenPath.panelForge:SetAllPoints()
	chosenPath.panelForge:Hide()

	-- --------------------------------------------------
	-- BACKGROUND
	-- --------------------------------------------------
	chosenPath.panelForgeBG = chosenPath.panelForge:CreateTexture(nil, "BACKGROUND")
	chosenPath.panelForgeBG:SetTexture("Interface\\AddOns\\Fatebound\\Media\\forge_bg_fade.tga")
	chosenPath.panelForgeBG:SetSize(727, 420)
	chosenPath.panelForgeBG:SetPoint("BOTTOM", 0, 5)
	chosenPath.panelForgeBG:SetAlpha(0.5)

	chosenPath.panelForgeBGLeft = chosenPath.panelForge:CreateTexture(nil, "ARTWORK")
	chosenPath.panelForgeBGLeft:SetTexture("interface/garrison/talenttreeexpedition")
	chosenPath.panelForgeBGLeft:SetTexCoord(0.001953125, 0.748046875, 0.001953125, 0.994140625)
	chosenPath.panelForgeBGLeft:SetSize(270, 358)
	chosenPath.panelForgeBGLeft:SetPoint("BOTTOMLEFT", 5, 5)
	
	----------------------------------------------------
	-- COOLDOWN LIST CONTAINER (placed over BGLeft)
	----------------------------------------------------
	chosenPath.cooldownFrame = CreateFrame("Frame", nil, chosenPath.panelForge)
	chosenPath.cooldownFrame:SetSize(240, 345)
	chosenPath.cooldownFrame:SetPoint("TOP", chosenPath.panelForgeBGLeft, "TOP", 0, -5)

	----------------------------------------------------
	-- Title
	----------------------------------------------------
	chosenPath.cooldownFrame.title = chosenPath.cooldownFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	chosenPath.cooldownFrame.title:SetPoint("TOP", 0, -5)
	chosenPath.cooldownFrame.title:SetText("Siphon Cooldowns")

	-- Tooltip hover frame for Siphon Cooldowns title
	chosenPath.siphonTooltip = CreateFrame("Frame", nil, chosenPath.cooldownFrame)
	chosenPath.siphonTooltip:SetAllPoints(chosenPath.cooldownFrame.title)
	chosenPath.siphonTooltip:EnableMouse(true)

	chosenPath.siphonTooltip:SetScript("OnEnter", function(self)
		local siphonicon = "|TInterface/Minimap/ObjectIconsAtlas:24:24:0:0:1024:1024:1:69:131:199|t"

		GameTooltip:SetOwner(self, "ANCHOR_TOPRIGHT")
		GameTooltip:ClearLines()
		GameTooltip:SetText("Siphoning", 1, 1, 1)

		GameTooltip:AddLine(" ")
		GameTooltip:AddLine(
			"You can find items throughout the world marked as " .. siphonicon .. " |cff7955c8Siphonable|r.\n\n" ..
			"Siphoning these items reveals unique relics that can be added to perk trees.\n\n" ..
			"Each siphonable item is subject to a 24-hour cooldown.",
			1, 0.82, 0, true
		)

		GameTooltip:Show()
	end)

	chosenPath.siphonTooltip:SetScript("OnLeave", function()
		GameTooltip:Hide()
	end)

	----------------------------------------------------
	-- Scroll Area
	----------------------------------------------------
	local sf = CreateFrame("ScrollFrame", nil, chosenPath.cooldownFrame, "UIPanelScrollFrameTemplate")
	sf:SetPoint("TOPLEFT", 5, -25)
	sf:SetPoint("BOTTOMRIGHT", -28, 5)

	local content = CreateFrame("Frame", nil, sf)
	content:SetSize(200, 200)
	sf:SetScrollChild(content)
	chosenPath.cooldownFrame.content = content
	chosenPath.cooldownFrame.lines = {}

	----------------------------------------------------
	-- Update Cooldown List
	----------------------------------------------------
	function Fatebound:UpdateCooldownList()
		if not chosenPath.cooldownFrame then return end

		local now = time()
		local entries = {}

		FateboundDB.siphonCooldowns = FateboundDB.siphonCooldowns or {}

		-- Build cooldown list
		for itemID, expires in pairs(FateboundDB.siphonCooldowns) do
			local remain = expires - now
			if remain > 0 then
				table.insert(entries, {
					id = itemID,
					remain = remain,
					expires = expires,
				})
			else
				FateboundDB.siphonCooldowns[itemID] = nil
			end
		end

		-- Sort earliest to expire first
		table.sort(entries, function(a, b)
			return a.remain < b.remain
		end)

		-- Clear old lines
		for _, line in ipairs(chosenPath.cooldownFrame.lines) do
			line:Hide()
		end

		local y = -2
		local parent = chosenPath.cooldownFrame.content
		chosenPath.cooldownFrame.lines = {}

		for _, entry in ipairs(entries) do
			local itemName, itemLink, itemQuality, _, _, _, _, _, _, itemIcon = GetItemInfo(entry.id)

			-- If async data isn't loaded yet, queue a retry
			if not itemName then
				C_Timer.After(0.1, function()
					Fatebound:UpdateCooldownList()
				end)
				itemName = "Loading..."
				itemLink = nil
				itemIcon = 134400
				itemQuality = 1
			end

			-- Truncate name safely
			local maxChars = 22
			local displayName = itemName
			if #displayName > maxChars then
				displayName = displayName:sub(1, maxChars) .. "..."
			end

			-- Apply quality color
			local r, g, b = GetItemQualityColor(itemQuality or 1)
			local hexColor = string.format("|cFF%02X%02X%02X", r * 255, g * 255, b * 255)
			local displayText = hexColor .. displayName .. "|r"

			-- Row frame
			local line = CreateFrame("Frame", nil, parent)
			line:SetSize(250, 24)
			line:SetPoint("TOPLEFT", 0, y)

			-- Icon
			local icon = line:CreateTexture(nil, "ARTWORK")
			icon:SetSize(20, 20)
			icon:SetPoint("LEFT", 0, 0)
			icon:SetTexture(itemIcon or 134400)

			-- Item text
			local text = line:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
			text:SetPoint("LEFT", icon, "RIGHT", 5, 0)
			text:SetJustifyH("LEFT")
			text:SetWordWrap(false)
			text:SetWidth(128) -- leave space for timer on the right
			text:SetText(displayText)

			text:SetScript("OnEnter", function(self)
				GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
				if itemLink then
					GameTooltip:SetHyperlink(itemLink)
				else
					GameTooltip:SetText(itemName)
				end
				GameTooltip:Show()
			end)
			text:SetScript("OnLeave", function()
				GameTooltip:Hide()
			end)

			-- Timer text, right-aligned but inside frame width
			local remain = entry.remain
			local hrs = math.floor(remain / 3600)
			local mins = math.floor((remain % 3600) / 60)

			local timer = line:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
			timer:SetPoint("RIGHT", line, "RIGHT", -44, 0)
			timer:SetJustifyH("RIGHT")
			timer:SetText(string.format("%02d:%02d", hrs, mins))

			-- Store for live update
			line.text = text
			line.timerText = timer
			line.expires = entry.expires
			line.itemID = entry.id

			table.insert(chosenPath.cooldownFrame.lines, line)

			y = y - 22
		end

		parent:SetHeight(#entries * 22 + 10)
	end

	----------------------------------------------------
	-- LIVE TIMER UPDATE
	----------------------------------------------------
	local updater = CreateFrame("Frame")
	local elapsed = 0
	updater:SetScript("OnUpdate", function(_, e)
		elapsed = elapsed + e
		if elapsed < 1 then return end
		elapsed = 0

		if not chosenPath.cooldownFrame or not chosenPath.cooldownFrame.lines then return end

		for _, line in ipairs(chosenPath.cooldownFrame.lines) do
			local expires = FateboundDB.siphonCooldowns[line.itemID]
			if expires then
				local remain = expires - time()

				if remain <= 0 then
					FateboundDB.siphonCooldowns[line.itemID] = nil
					Fatebound:UpdateCooldownList()
					return
				end

				local hrs = math.floor(remain / 3600)
				local mins = math.floor((remain % 3600) / 60)

				line.timerText:SetText(hrs .. "h " .. mins .. "m")
			end
		end
	end)

	-- =======================================
	-- FORGE PANEL: SINGLE SLOT FOR EXTRACTION & UPGRADE
	-- =======================================

	-- Ensure SavedVariables for forge slot exist
	FateboundDB.forge = FateboundDB.forge or { slotKey = nil }

	-- Create single forge slot
	local forgeSlot = CreateFrame("Button", nil, chosenPath.panelForge, "BackdropTemplate")
	forgeSlot:SetSize(70, 70)
	forgeSlot:SetPoint("CENTER", chosenPath.panelForge, "CENTER", 138, -55)
	forgeSlot:EnableMouse(true)

	-- SOCKET BG
	forgeSlot.bg = forgeSlot:CreateTexture(nil, "ARTWORK")
	forgeSlot.bg:SetTexture("interface/garrison/orderhalltalents")
	forgeSlot.bg:SetTexCoord(0.142578125, 0.251953125, 0.544921875, 0.662109375)
	forgeSlot.bg:SetSize(56, 60)
	forgeSlot.bg:SetPoint("CENTER")

	-- BORDER
	forgeSlot.border = forgeSlot:CreateTexture(nil, "OVERLAY")
	forgeSlot.border:SetTexture("interface/garrison/orderhalltalents")
	forgeSlot.border:SetTexCoord(0.001953125, 0.138671875, 0.443359125, 0.580078125)
	forgeSlot.border:SetSize(70, 70)
	forgeSlot.border:SetPoint("CENTER")

	-- ICON
	forgeSlot.icon = forgeSlot:CreateTexture(nil, "ARTWORK")
	forgeSlot.icon:SetSize(38, 38)
	forgeSlot.icon:SetPoint("CENTER")
	forgeSlot.icon:SetDrawLayer("ARTWORK", 2)
	forgeSlot.icon:Hide()

	-- Stored item info
	forgeSlot.item = nil
	forgeSlot.itemID = nil 

	-- ===============================
	-- Progress overlay for upgrade + extraction
	-- ===============================
	forgeSlot.progressFrame = CreateFrame("Frame", nil, forgeSlot)
	forgeSlot.progressFrame:SetSize(38, 38)
	forgeSlot.progressFrame:SetPoint("CENTER", forgeSlot, "CENTER")
	forgeSlot.progressFrame:Hide()

	forgeSlot.progressOverlay = forgeSlot.progressFrame:CreateTexture(nil, "OVERLAY")
	forgeSlot.progressOverlay:SetTexture("Interface/HUD/UIActionBarFXX2")
	forgeSlot.progressOverlay:SetTexCoord(0.09228515625, 0.14013671875, 0.80419921875, 0.84912109375)
	forgeSlot.progressOverlay:SetPoint("LEFT", forgeSlot.progressFrame, "LEFT", -1, 0)
	forgeSlot.progressOverlay:SetSize(0, 42)

	-- Success glow over the icon
	forgeSlot.successGlow = forgeSlot:CreateTexture(nil, "ARTWORK")
	forgeSlot.successGlow:SetSize(38, 38)
	forgeSlot.successGlow:SetPoint("CENTER", forgeSlot.icon, "CENTER")
	forgeSlot.successGlow:SetTexture("interface/containerframe/bags")
	forgeSlot.successGlow:SetTexCoord(0.779296875, 0.85546875, 0.00390625, 0.15625)
	forgeSlot.successGlow:SetDrawLayer("ARTWORK", 3)  -- Sublevel 3, above icon
	forgeSlot.successGlow:Hide()

	-- Failure glow over the icon
	forgeSlot.failureGlow = forgeSlot:CreateTexture(nil, "ARTWORK")
	forgeSlot.failureGlow:SetSize(38, 38)
	forgeSlot.failureGlow:SetPoint("CENTER", forgeSlot.icon, "CENTER")
	forgeSlot.failureGlow:SetTexture("interface/hud/uiactionbarfxx2")
	forgeSlot.failureGlow:SetTexCoord(0.14794921875, 0.18994140625, 0.79150390625, 0.83349609375)
	forgeSlot.failureGlow:SetDrawLayer("ARTWORK", 3)  -- Sublevel 3, above icon
	forgeSlot.failureGlow:Hide()

	function Fatebound:ShowUpgradeTextureGlow(slot, success)
		if not slot then return end

		-- Hide both first
		if slot.successGlow then slot.successGlow:Hide() end
		if slot.failureGlow then slot.failureGlow:Hide() end

		if success and slot.successGlow then
			slot.successGlow:Show()
			C_Timer.After(1.5, function()
				if slot.successGlow then slot.successGlow:Hide() end
			end)
		elseif not success and slot.failureGlow then
			slot.failureGlow:Show()
			C_Timer.After(1.5, function()
				if slot.failureGlow then slot.failureGlow:Hide() end
			end)
		end
	end

	-- ===============================
	-- Refresh slot icon
	-- ===============================
	local function RefreshSlot()
		if forgeSlot.item then
			forgeSlot.icon:SetTexture(forgeSlot.item.icon or "Interface/ICONS/INV_Misc_QuestionMark")
			forgeSlot.icon:Show()
		elseif forgeSlot.itemID then
			local icon = C_Item.GetItemIconByID(forgeSlot.itemID) or GetItemIcon(forgeSlot.itemID)
			if icon then
				forgeSlot.icon:SetTexture(icon)
				forgeSlot.icon:Show()
			else
				forgeSlot.icon:Hide()
			end
		else
			forgeSlot.icon:Hide()
		end
	end

	-- ===============================
	-- Set a Fatebound item into the forge slot
	-- ===============================
	local function SetForgeSlotItem(item)
		if not item then return end

		local found = false
		for _, invItem in ipairs(Fatebound.Items) do
			if invItem.key == item.key then
				found = true
				break
			end
		end
		if not found then
			table.insert(Fatebound.Items, item)
		end

		forgeSlot.itemID = nil

		if forgeSlot.item then
			Fatebound.Inventory:AddItem(forgeSlot.item)
		end

		forgeSlot.item = item
		FateboundDB.forge.slotKey = item.key

		RefreshSlot()
	end

	-- ===============================
	-- Restore saved Fatebound item on reload
	-- ===============================
	if FateboundDB.forge.slotKey and Fatebound.Items then
		local found = false
		for _, invItem in ipairs(Fatebound.Items) do
			if invItem.key == FateboundDB.forge.slotKey then
				SetForgeSlotItem(invItem)
				found = true
				break
			end
		end
		if not found then
			FateboundDB.forge.slotKey = nil
		end
	end

	local f = CreateFrame("Frame")
	f:RegisterEvent("PLAYER_LOGOUT")
	f:RegisterEvent("PLAYER_LOGIN") -- optional
	f:RegisterEvent("PLAYER_ENTERING_WORLD") -- optional

	f:SetScript("OnEvent", function(self, event)
		if event == "PLAYER_LOGOUT" then
			if forgeSlot.item then
				-- Add item back to inventory if it’s not already there
				local exists = false
				for _, invItem in ipairs(Fatebound.Inventory.Items) do
					if invItem.key == forgeSlot.item.key then
						exists = true
						break
					end
				end
				if not exists then
					Fatebound.Inventory:AddItem(forgeSlot.item)
				end

				-- Clear the forge slot key so it’s not “stuck”
				FateboundDB.forge.slotKey = nil
				forgeSlot.item = nil
				RefreshSlot()
			end
		elseif event == "PLAYER_LOGIN" or event == "PLAYER_ENTERING_WORLD" then
			-- Restore forge slot if a key exists (optional, safety)
			if FateboundDB.forge.slotKey then
				for _, invItem in ipairs(Fatebound.Inventory.Items) do
					if invItem.key == FateboundDB.forge.slotKey then
						SetForgeSlotItem(invItem)
						break
					end
				end
			end
		end
	end)

	-- ===============================
	-- Tooltip
	-- ===============================
	forgeSlot:SetScript("OnEnter", function(self)
		if self.item then
			ns.ShowItemTooltip(self, self.item)
		elseif self.itemID then
			GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
			GameTooltip:SetItemByID(self.itemID)
			GameTooltip:Show()
		end
	end)

	forgeSlot:SetScript("OnLeave", GameTooltip_Hide)

	-- ===============================
	-- Mouse click handling
	-- ===============================
	forgeSlot:SetScript("OnMouseUp", function(self, button)
		local cursor = Fatebound.Inventory.CursorItem
		local t, itemID = GetCursorInfo()

		if button == "LeftButton" then
			if t == "item" and itemID and Fatebound.AllowedExtractItems[itemID] then
				if forgeSlot.item then
					Fatebound.Inventory:AddItem(forgeSlot.item)
					forgeSlot.item = nil
					FateboundDB.forge.slotKey = nil
				end
				forgeSlot.itemID = itemID
				RefreshSlot()
				ClearCursor()
			elseif cursor and cursor.item then
				SetForgeSlotItem(cursor.item)
				Fatebound.Inventory.CursorItem = nil
				Fatebound:RefreshAll()
			end
		elseif button == "RightButton" then
			if forgeSlot.item then
				Fatebound.Inventory:AddItem(forgeSlot.item)
				forgeSlot.item = nil
				FateboundDB.forge.slotKey = nil
			elseif forgeSlot.itemID then
				forgeSlot.itemID = nil
			end
			RefreshSlot()
		end
	end)

	-- ===============================
	-- Extract button
	-- ===============================
	chosenPath.extractBtn = CreateFrame("Button", nil, chosenPath.panelForge, "UIPanelButtonTemplate")
	chosenPath.extractBtn:SetSize(80, 26)
	chosenPath.extractBtn:SetPoint("TOP", forgeSlot, "BOTTOM", 0, -10)
	chosenPath.extractBtn:SetText("Siphon")
	chosenPath.extractBtn:SetScript("OnClick", function()
		local slot = forgeSlot
		if not slot.item and not slot.itemID then
			UIErrorsFrame:AddMessage("No item to siphon.", 1, 0, 0)
			return
		end

		-- Helper: get remaining cooldown in seconds
		local function GetCooldownRemaining(itemID)
			local expires = FateboundDB.siphonCooldowns[itemID]
			if not expires then return 0 end
			return math.max(0, expires - time())
		end

		-- ==============================
		-- Siphon Blizzard Item
		-- ==============================
		if slot.itemID then
			local id = slot.itemID
			local remaining = GetCooldownRemaining(id)

			if remaining > 0 then
				local hours = math.floor(remaining / 3600)
				local minutes = math.floor((remaining % 3600) / 60)
				UIErrorsFrame:AddMessage(
					"You cannot siphon this item again for " .. hours .. "h " .. minutes .. "m.",
					1, 0, 0
				)
				return
			end

			-- Start siphon animation
			Fatebound:StartUpgradeAnimation(slot, 2, function()
				local fakeItem = Fatebound:CreateFakeItemFromBlizzard(id)
				if fakeItem then
					Fatebound.Inventory:AddItem(fakeItem)
					-- Apply 24h cooldown
					FateboundDB.siphonCooldowns[id] = time() + 86400
				else
					UIErrorsFrame:AddMessage("Failed to siphon item.", 1, 0, 0)
				end
				slot.itemID = nil
				RefreshSlot()
				Fatebound:RefreshAll()
			end)

		-- ==============================
		-- Siphon Soul Essence from Fatebound item
		-- ==============================
		elseif slot.item then
			local itemName = slot.item.name
			StaticPopupDialogs["FATEBOUND_DELETE_ITEM"] = {
				text = "Are you sure you want to siphon |cff82c5ffSoul Essence|r from |cff7955c8["..itemName.."]|r?\nThis will permanently |cffff0000DESTROY|r the item.",
				button1 = "Yes",
				button2 = "No",
				OnAccept = function()
					-- Store the item reference locally
					local currentItem = slot.item

					Fatebound:StartUpgradeAnimation(slot, 2, function()
						-- Only give Soul Essence if the item still exists in the slot
						if slot.item == currentItem then
							local gained = math.random(256, 512)
							FateboundDB.SoulEssence = (FateboundDB.SoulEssence or 0) + gained
							if not FateboundDB.disableSoulPrint then
								print("|cff6f6fff+" .. gained .. " Soul Essence|r")
							end
							slot.item = nil
							FateboundDB.forge.slotKey = nil
							RefreshSlot()
							Fatebound:RefreshAll()
						end
					end)
				end,
				OnCancel = function() end,
				timeout = 0,
				whileDead = true,
				hideOnEscape = true,
				preferredIndex = 3,
			}
			StaticPopup_Show("FATEBOUND_DELETE_ITEM")
		end
	end)

	-- ===============================
	-- Upgrade button
	-- ===============================
	chosenPath.upgradeButton = CreateFrame("Button", "Fatebound_UpgradeButton", chosenPath.panelForge, "UIPanelButtonTemplate")
	chosenPath.upgradeButton:SetSize(80, 26)
	chosenPath.upgradeButton:SetPoint("TOP", forgeSlot, "BOTTOM", 0, -40)
	chosenPath.upgradeButton:SetText("Upgrade")
	chosenPath.upgradeButton:SetScript("OnClick", function()
		local slot = forgeSlot
		if not slot.item then
			UIErrorsFrame:AddMessage("No item to upgrade.", 1, 0, 0)
			return
		end

		-- Prevent upgrades if item is already Rank 3
		if slot.item.rank and slot.item.rank >= 3 then
			UIErrorsFrame:AddMessage("This item is already at maximum rank.", 1, 0, 0)
			return
		end

		-- Check Soul Essence cost before allowing upgrade
		local costPerRank = { [1] = 4200, [2] = 10400 }
		local cost = costPerRank[slot.item.rank] or 4200

		if (FateboundDB.SoulEssence or 0) < cost then
			UIErrorsFrame:AddMessage("Not enough Soul Essence (" .. cost .. " required).", 1, 0, 0)
			return false
		end

		-- Store pending upgrade
		Fatebound.pendingUpgradeItem = slot.item
		Fatebound.pendingForgeSlot = slot

		-- Update confirmation popup text with item name and cost
		if StaticPopupDialogs["FATEBOUND_UPGRADE_CONFIRM"] then
			StaticPopupDialogs["FATEBOUND_UPGRADE_CONFIRM"].text =
				string.format(
					"Are you sure you want to attempt to upgrade |cff7955c8[%s]|r for |cff82c5ff%d Soul Essence|r?\nFailure will |cffff0000DESTROY|r the item.",
					slot.item.name,
					cost
				)
		end

		-- Show confirmation popup
		StaticPopup_Show("FATEBOUND_UPGRADE_CONFIRM")
	end)

	-- ===============================
	-- Upgrade confirmation popup
	-- ===============================
	StaticPopupDialogs["FATEBOUND_UPGRADE_CONFIRM"] = {
		text = "Are you sure you want to attempt to upgrade this item? Failure will |cffff0000DESTROY|r the item.",
		button1 = "Yes",
		button2 = "No",
		OnAccept = function()
			local slot = Fatebound.pendingForgeSlot
			if not slot or not slot.item then return end

			-- Animate progress
			local duration = 3
			Fatebound:StartUpgradeAnimation(slot, duration)

			-- Perform upgrade after animation
			C_Timer.After(duration, function()
				Fatebound:PerformUpgradeAttempt()
			end)
		end,
		timeout = 0,
		whileDead = true,
		hideOnEscape = true,
		preferredIndex = 3,
	}

	-- ===============================
	-- Start upgrade animation
	-- ===============================
	function Fatebound:StartUpgradeAnimation(slot, duration, onComplete)
		if not slot or not slot.progressOverlay or not slot.progressFrame then return end
		slot.progressOverlay:SetWidth(0)
		slot.progressFrame:Show()

		local startTime = GetTime()
		duration = duration or 3

		slot:SetScript("OnUpdate", function(self, elapsed)
			local progress = (GetTime() - startTime) / duration
			if progress >= 1 then
				progress = 1
				self.progressOverlay:SetWidth(progress * 38)
				self.progressFrame:Hide()
				self:SetScript("OnUpdate", nil)
				if onComplete then
					onComplete()
				end
			else
				self.progressOverlay:SetWidth(progress * 38)
			end
		end)
	end

	-- ===============================
	-- Perform upgrade attempt
	-- ===============================
	function Fatebound:PerformUpgradeAttempt()
		local item = Fatebound.pendingUpgradeItem
		local slot = Fatebound.pendingForgeSlot
		if not item or not slot or not slot.item then return end

		local result = Fatebound:UpgradeItem(item)

		if result == true then
			--ns.ShowCenterMessage("|cffffdd00Item was successfully upgraded to Rank " .. item.rank .. "!|r", FateboundDB.path, 2, 1, 18)
			RefreshSlot()
			Fatebound:ShowUpgradeTextureGlow(slot, true)  -- success
		elseif result == "destroyed" then
			UIErrorsFrame:AddMessage("Your item was destroyed in the upgrade attempt.", 1, 0, 0)
			slot.item = nil
			RefreshSlot()
			Fatebound:ShowUpgradeTextureGlow(slot, false) -- failure
		end

		Fatebound:RefreshAll()
	end

	-- ===============================
	-- Return item to inventory on forge close
	-- ===============================
	chosenPath.panelForge:SetScript("OnHide", function()
		if forgeSlot.item then
			Fatebound.Inventory:AddItem(forgeSlot.item)
			forgeSlot.item = nil
			FateboundDB.forge.slotKey = nil
		end
		forgeSlot.itemID = nil
		RefreshSlot()

		if Fatebound.Inventory.CursorItem then
			Fatebound.Inventory.CursorItem = nil

			-- Safely hide cursor frame
			local cf = Fatebound.Inventory.cursorFrame
			if cf then
				cf:Hide()
				cf.texture:SetTexture(nil)
			end
		end
	end)

	-- ==================================================
	-- PERKS CONFIG
	-- ==================================================
	local CIRCLE_MASK = "interface/masks/circlemaskscalable"
	local PERK_SLOT_BG_TEXTURE = "interface/soulbinds/soulbindsassets"
	local PERK_SLOT_BG_TEXCOORD = {0.77685546875, 0.83154296875, 0.1162109375, 0.171875}
	local PERK_SLOT_BG_SIZE = 56

	local PERK_RING_SIZE = 58
	local PERK_RING_TEXTURE = "interface/soulbinds/soulbindsassets"
	local PERK_RING_TEXCOORD = {0.11376953125, 0.16845703125, 0.7919921875, 0.8466796875}

	local CUSTOM_ITEMS = ns.Items or {}

	-- ==================================================
	-- CREATE A PERK SLOT
	-- ==================================================
	local function CreatePerkSlot(parent, x, y, isElite)
		local slot = CreateFrame("Button", nil, parent, "BackdropTemplate")
		slot:SetSize(PERK_SLOT_BG_SIZE, PERK_SLOT_BG_SIZE)
		slot:SetPoint("CENTER", x, y)
		slot:EnableMouse(true)

		-- Background
		slot.bg = slot:CreateTexture(nil, "ARTWORK")
		slot.bg:SetAllPoints()
		slot.bg:SetTexture(PERK_SLOT_BG_TEXTURE)
		slot.bg:SetTexCoord(unpack(PERK_SLOT_BG_TEXCOORD))
		slot.bg:SetDrawLayer("ARTWORK", 1)

		-- Icon
		slot.icon = slot:CreateTexture(nil, "ARTWORK")
		slot.icon:SetSize(48, 48)
		slot.icon:SetPoint("CENTER")
		slot.icon:SetDrawLayer("ARTWORK", 2)
		slot.icon:Hide()

		-- Mask
		slot.iconMask = slot:CreateMaskTexture()
		slot.iconMask:SetTexture(CIRCLE_MASK, "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
		slot.iconMask:SetAllPoints(slot.icon)
		slot.icon:AddMaskTexture(slot.iconMask)

		-- Ring
		slot.ring = slot:CreateTexture(nil, "OVERLAY")
		slot.ring:SetSize(PERK_RING_SIZE, PERK_RING_SIZE)
		slot.ring:SetPoint("CENTER")
		slot.ring:SetTexture(PERK_RING_TEXTURE)
		slot.ring:SetTexCoord(unpack(PERK_RING_TEXCOORD))
		slot.ring:SetDrawLayer("OVERLAY", 1)
		slot.ring:SetDesaturated(true)
		slot.ring:Show()

		-- Elite overlay
		if isElite then
			slot.eliteOverlay = slot:CreateTexture(nil, "OVERLAY")
			slot.eliteOverlay:SetSize(88, 88)
			slot.eliteOverlay:SetPoint("CENTER", 4, -2.5)
			--slot.eliteOverlay:SetTexture("Interface\\AddOns\\Fatebound\\Media\\octopus_ring")
			slot.eliteOverlay:SetTexture("interface/garrison/adventuremissionsframe")
			slot.eliteOverlay:SetTexCoord(0.09765625, 0.1923828125, 0.8642578125, 0.90966796875)
			slot.eliteOverlay:SetDrawLayer("OVERLAY", 2)
			--slot.eliteOverlay:SetVertexColor(0.75, 0.7, 0.95)
			slot.eliteOverlay:Show()

			slot.relicForgeOverlay = slot:CreateTexture(nil, "OVERLAY")
			slot.relicForgeOverlay:SetSize(74, 74)
			slot.relicForgeOverlay:SetPoint("CENTER", slot.eliteOverlay, "CENTER", -4, 2.5)
			slot.relicForgeOverlay:SetTexture("interface/artifacts/relicforge")
			slot.relicForgeOverlay:SetTexCoord(0.876953125, 0.955078125, 0.3193359375, 0.3974609375)
			slot.relicForgeOverlay:SetDrawLayer("OVERLAY", 3)
			slot.relicForgeOverlay:SetDesaturated(true)
			slot.relicForgeOverlay:SetVertexColor(0.60, 0.50, 1.00)
			slot.relicForgeOverlay:Show()

			slot.eliteMiniIcon = slot:CreateTexture(nil, "OVERLAY")
			slot.eliteMiniIcon:SetSize(40, 40)
			slot.eliteMiniIcon:SetPoint("TOPLEFT", slot.eliteOverlay, "TOPLEFT", 0, 0)
			slot.eliteMiniIcon:SetTexture("Interface/Minimap/ObjectIconsAtlas")
			slot.eliteMiniIcon:SetTexCoord(0.0009765625, 0.0673828125, 0.1279296875, 0.1943359375)
			slot.eliteMiniIcon:SetDrawLayer("OVERLAY", 4)
			slot.eliteMiniIcon:Show()
		end

		-- Glow texture (hidden by default)
		slot.glow = slot:CreateTexture(nil, "OVERLAY")
		slot.glow:SetTexture("interface/soulbinds/soulbindsassets")
		slot.glow:SetTexCoord(0.10302734375, 0.16162109375, 0.6728515625, 0.7314453125)
		slot.glow:SetSize(48, 48)
		slot.glow:SetPoint("CENTER")
		slot.glow:Hide()

		slot.item = nil
		slot:RegisterForClicks("AnyUp")

		return slot
	end

	-- ==================================================
	-- TOOLTIP ATTACHMENT
	-- ==================================================
	local function AttachTooltip(slot)
		slot:SetScript("OnEnter", function(self)
			if self.item then
				ns.ShowItemTooltip(self, self.item)
			end
		end)
		slot:SetScript("OnLeave", GameTooltip_Hide)
	end

	-- ==================================================
	-- VALIDATE ITEM FOR SLOT
	-- ==================================================
	local function IsValidItemForSlot(item, slotTree, isEliteSlot)
		if not item then return false end
		if isEliteSlot then
			return item.isElite and item.tree == slotTree
		else
			return not item.isElite and (item.tree == slotTree or item.tree == "any")
		end
	end

	-- ==================================================
	-- CHECK DUPLICATE ITEM BY NAME
	-- ==================================================
	local function IsItemUsedAnywhere(itemName, ignoreItem)
		local Trees = {"YshaarjPerks", "NzothPerks", "CthunPerks", "YoggPerks", "GhuunPerks"}
		for _, treeKey in ipairs(Trees) do
			local treeDB = FateboundDB[treeKey]
			if treeDB then
				for i, slotItem in pairs(treeDB) do
					if type(i) ~= "string" and slotItem and slotItem.name == itemName and slotItem ~= ignoreItem then
						return true
					end
				end
				local eliteItem = treeDB["elite"]
				if eliteItem and eliteItem.name == itemName and eliteItem ~= ignoreItem then
					return true
				end
			end
		end
		return false
	end

	-- ==================================================
	-- STATIC POPUP FOR UNEQUIP CONFIRMATION
	-- ==================================================
	local UNEQUIP_COST = 1000

	-- StaticPopup for unequip with cost
	StaticPopupDialogs["FATEBOUND_UNEQUIP_PERK"] = {
		text = "Removing this perk will cost |cffff0000" .. UNEQUIP_COST .. "|r |cff82c5ffSoul Essence|r. Proceed?",
		button1 = "Yes",
		button2 = "No",
		OnAccept = function(self)
			local slot = self.data
			if not slot or not slot.item then return end

			if (FateboundDB.SoulEssence or 0) < UNEQUIP_COST then
				UIErrorsFrame:AddMessage("Not enough Soul Essence (" .. UNEQUIP_COST .. " required).", 1, 0, 0)
				return
			end

			-- Deduct cost
			FateboundDB.SoulEssence = FateboundDB.SoulEssence - UNEQUIP_COST

			local treeDB = slot.__treeDB
			local index = slot.__slotIndex
			local allowElite = slot.__allowElite

			Fatebound.Inventory:AddItem(slot.item)
			if allowElite then
				treeDB["elite"] = nil
			else
				treeDB[index] = nil
			end

			slot.item = nil
			slot.icon:Hide()
			
			Fatebound:RefreshAll()
		end,
		timeout = 0,
		whileDead = true,
		hideOnEscape = true,
		preferredIndex = 3,
	}

	-- StaticPopup for swapping perks with cost
	StaticPopupDialogs["FATEBOUND_SWAP_PERK"] = {
		text = "Replacing this perk will cost |cffff0000" .. UNEQUIP_COST .. "|r |cff82c5ffSoul Essence|r. Proceed?",
		button1 = "Yes",
		button2 = "No",
		OnAccept = function(self)
			local slot = self.data
			local newItem = slot.__newItem
			if not slot or not slot.item or not newItem then return end

			if (FateboundDB.SoulEssence or 0) < UNEQUIP_COST then
				UIErrorsFrame:AddMessage("Not enough Soul Essence (" .. UNEQUIP_COST .. " required).", 1, 0, 0)
				return
			end

			-- Deduct cost
			FateboundDB.SoulEssence = FateboundDB.SoulEssence - UNEQUIP_COST

			-- Remove old item's stats
			if slot.item.stats and (FateboundDB.favor or 0) > 0 then
				for stat, value in pairs(slot.item.stats) do
					if stat ~= "Corruption" then
						Fatebound:AddStat(stat, -value)
					end
				end
			end

			-- Return old item to inventory
			Fatebound.Inventory:AddItem(slot.item)

			-- Equip new item
			if slot.__allowElite then
				slot.__treeDB["elite"] = newItem
			else
				slot.__treeDB[slot.__slotIndex] = newItem
			end
			slot.item = newItem
			slot.icon:SetTexture(newItem.icon)
			slot.icon:SetSize(48, 48)
			slot.icon:SetPoint("CENTER")
			slot.icon:Show()

			-- Apply new item's stats
			if newItem.stats and (FateboundDB.favor or 0) > 0 then
				for stat, value in pairs(newItem.stats) do
					if stat ~= "Corruption" then
						Fatebound:AddStat(stat, value)
					end
				end
			end

			Fatebound.Inventory.CursorItem = nil
			Fatebound:RefreshAll()

			-- Remove new item from inventory
			for i, invItem in ipairs(Fatebound.Items) do
				if invItem.name == newItem.name then
					table.remove(Fatebound.Items, i)
					break
				end
			end
		end,
		timeout = 0,
		whileDead = true,
		hideOnEscape = true,
		preferredIndex = 3,
	}

	-- ==================================================
	-- SLOT CLICK LOGIC
	-- ==================================================
	local function SetSlotScripts(slot, treeDB, index, allowElite, treeKey)
		local slotTree = treeKey:match("^(%w+)Perks$"):lower()

		-- Glow when a valid cursor item is over this slot
		slot:SetScript("OnUpdate", function(self)
			local cursor = Fatebound.Inventory.CursorItem
			if cursor and cursor.item and IsValidItemForSlot(cursor.item, slotTree, allowElite) then
				self.glow:Show()
			else
				self.glow:Hide()
			end
		end)

		slot:SetScript("OnMouseUp", function(self, button)
			if button ~= "LeftButton" and button ~= "RightButton" then return end

			local cursor = Fatebound.Inventory.CursorItem

			-- LEFT CLICK: Equip item from cursor
			if button == "LeftButton" and cursor and cursor.item then
				local item = cursor.item

				-- Validate item type for slot
				if not IsValidItemForSlot(item, slotTree, allowElite) then
					UIErrorsFrame:AddMessage(allowElite and "Only elite items from this tree can go here." or "This item cannot be placed in this slot.", 1, 0, 0)
					return
				end

				-- Prevent duplicate placement
				if IsItemUsedAnywhere(item.name, self.item) then
					UIErrorsFrame:AddMessage("This item is already placed in another slot.", 1, 0, 0)
					return
				end

				-- If slot already has an item, show confirmation popup
				if self.item then
					-- Store references in slot for the popup callback
					self.__treeDB = treeDB
					self.__slotIndex = index
					self.__allowElite = allowElite
					self.__newItem = item

					StaticPopup_Show("FATEBOUND_SWAP_PERK", nil, nil, self)
					return -- wait for confirmation
				end

				-- If slot is empty, equip directly
				if not self.item then
					-- Equip new item directly
					if allowElite then
						treeDB["elite"] = item
					else
						treeDB[index] = item
					end
					self.item = item
					self.icon:SetTexture(item.icon)
					self.icon:SetSize(48, 48)
					self.icon:SetPoint("CENTER")
					self.icon:Show()

					-- Apply new item's stats only if favor > 0
					if item.stats and (FateboundDB.favor or 0) > 0 then
						for stat, value in pairs(item.stats) do
							if stat ~= "Corruption" then
								Fatebound:AddStat(stat, value)
							end
						end
					end

					Fatebound.Inventory.CursorItem = nil
					Fatebound:RefreshAll()

					-- Remove equipped item from inventory
					for i, invItem in ipairs(Fatebound.Items) do
						if invItem.name == item.name then
							table.remove(Fatebound.Items, i)
							break
						end
					end
				end

			-- RIGHT CLICK: Unequip with confirmation
			elseif button == "RightButton" and self.item then
				self.__treeDB = treeDB
				self.__slotIndex = index
				self.__allowElite = allowElite
				StaticPopup_Show("FATEBOUND_UNEQUIP_PERK", nil, nil, self)
			end
		end)
	end

	-- ==================================================
	-- CREATE PERK SLOTS FOR ALL TREES
	-- ==================================================
	local Trees = {
		{ key="CthunPerks", panel=chosenPath.panel1, positions={{46,-92},{230,-92}}, elitePos={138,0}, eliteKey="elite" },
		{ key="YoggPerks", panel=chosenPath.panel2, positions={{0,-69},{92,0},{138,-138},{184,0},{276,-69}}, elitePos={138,-69}, eliteKey="elite" },
		{ key="YshaarjPerks", panel=chosenPath.panel3, positions={{0,23},{46,-92},{138,-69},{138,-138},{230,-92},{276,23}}, elitePos={138,0}, eliteKey="elite" },
		{ key="GhuunPerks", panel=chosenPath.panel4, positions={{46,-46},{92,-138},{184,-138},{230,-46}}, elitePos={138,23}, eliteKey="elite" },
		{ key="NzothPerks", panel=chosenPath.panel5, positions={{0,-46},{46,23},{92,-92},{138,-46},{138,-138},{184,-92},{230,23},{276,-46}}, elitePos={138,23}, eliteKey="elite" },
	}

	for _, tree in ipairs(Trees) do
		FateboundDB[tree.key] = FateboundDB[tree.key] or {}
		local baseTreeName = tree.key:match("^(%w+)Perks$"):lower()
		FateboundDB[tree.key].__treeName = baseTreeName
		chosenPath[tree.key .. "_slots"] = {}

		-- Normal slots
		for i, pos in ipairs(tree.positions) do
			local slot = CreatePerkSlot(tree.panel, pos[1], pos[2], false)
			chosenPath[tree.key .. "_slots"][i] = slot

			local savedItem = FateboundDB[tree.key][i]
			if savedItem then
				slot.icon:SetTexture(savedItem.icon)
				slot.icon:SetSize(48, 48)
				slot.icon:SetPoint("CENTER")
				slot.icon:Show()
				slot.item = savedItem

			end

			AttachTooltip(slot)
			SetSlotScripts(slot, FateboundDB[tree.key], i, false, tree.key)
		end

		-- Elite slot
		local eliteSlot = CreatePerkSlot(tree.panel, tree.elitePos[1], tree.elitePos[2], true)
		chosenPath[tree.key .. "_elite"] = eliteSlot

		local savedElite = FateboundDB[tree.key][tree.eliteKey]
		if savedElite then
			eliteSlot.icon:SetTexture(savedElite.icon)
			eliteSlot.icon:SetSize(48, 48)
			eliteSlot.icon:SetPoint("CENTER")
			eliteSlot.icon:Show()
			eliteSlot.item = savedElite

		end

		AttachTooltip(eliteSlot)
		SetSlotScripts(eliteSlot, FateboundDB[tree.key], tree.eliteKey, true, tree.key)
	end
	
	-- ==================================================
	-- SHARED FRAMES
	-- ==================================================
	-- Parchment frame
	chosenPath.parchmentFrame = CreateFrame("Frame", nil, chosenPath)
	chosenPath.parchmentFrame:SetAllPoints()
	chosenPath.parchmentFrame:SetFrameLevel(chosenPath:GetFrameLevel() + 1)

	chosenPath.parchmentLeft = chosenPath.parchmentFrame:CreateTexture(nil, "ARTWORK")
	chosenPath.parchmentLeft:SetSize(300, 360)
	chosenPath.parchmentLeft:SetTexture("interface/questionframe/warboard")
	chosenPath.parchmentLeft:SetTexCoord(0.0009765625, 0.3427734375, 0.462890625, 0.9248046875)
	chosenPath.parchmentLeft:SetPoint("LEFT", 5, -50)
	chosenPath.parchmentLeft:SetDrawLayer("ARTWORK", 2)

	-- Text overlay
	chosenPath.textOverlay = CreateFrame("Frame", nil, chosenPath)
	chosenPath.textOverlay:SetAllPoints()
	chosenPath.textOverlay:SetFrameLevel(chosenPath.panel1:GetFrameLevel() + 1)

    -- Path title
    chosenPath.pathLabel = chosenPath.textOverlay:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    chosenPath.pathLabel:SetPoint("TOPLEFT", 40, -70)
    chosenPath.pathLabel:SetShadowColor(0, 0, 0, 1)
    chosenPath.pathLabel:SetShadowOffset(1, -1)

    -- Favour
    chosenPath.favorLabel = chosenPath.textOverlay:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    chosenPath.favorLabel:SetPoint("TOPRIGHT", -40, -70)
    chosenPath.favorLabel:SetShadowColor(0, 0, 0, 1)
    chosenPath.favorLabel:SetShadowOffset(1, -1)
	
	-- Tooltip hover frame for Favor
	chosenPath.favorTooltip = CreateFrame("Frame", nil, chosenPath)
	chosenPath.favorTooltip:SetPoint("TOPRIGHT", chosenPath.favorLabel, "TOPRIGHT")
	chosenPath.favorTooltip:SetSize(140, 24)
	chosenPath.favorTooltip:EnableMouse(true)

	chosenPath.favorTooltip:SetScript("OnEnter", function(self)
		GameTooltip:SetOwner(self, "ANCHOR_TOPRIGHT")
		GameTooltip:SetText("Favor", 1, 1, 1)
		GameTooltip:AddLine(" ")
		GameTooltip:AddLine(
			"Favor can be gained by completing everyday tasks such as quests and dungeons.\n\n" ..
			"Be wary. Favor fades if you neglect devotion to the gods.\n\n" ..
			"Perks require at least 1 Favor to activate.",
			1, 0.82, 0, true
		)

		GameTooltip:Show()
	end)

	chosenPath.favorTooltip:SetScript("OnLeave", function()
		GameTooltip:Hide()
	end)

	-- Inventory button
	local buttonInv = CreateFrame("Button", nil, chosenPath)
	buttonInv:SetSize(48, 67)
	buttonInv:SetPoint("RIGHT", chosenPath, "RIGHT", 20, -55)
	buttonInv:SetFrameStrata("HIGH")
	buttonInv:SetFrameLevel(100)

	local normalTex = buttonInv:CreateTexture()
	normalTex:SetTexture("interface/covenantrenown/covenantrenownui")
	normalTex:SetAllPoints(buttonInv)

	local highlightTex = buttonInv:CreateTexture()
	highlightTex:SetTexture("interface/covenantrenown/covenantrenownui")
	highlightTex:SetAllPoints(buttonInv)

	local pushedTex = buttonInv:CreateTexture()
	pushedTex:SetTexture("interface/covenantrenown/covenantrenownui")
	pushedTex:SetAllPoints(buttonInv)

	buttonInv:SetNormalTexture(normalTex)
	buttonInv:SetHighlightTexture(highlightTex)
	buttonInv:SetPushedTexture(pushedTex)

	-- Original coordinates
	local coords = {
		normal = {0.7568359375, 0.8037109375, 0.267578125, 0.3984375},
		highlight = {0.8056640625, 0.8525390625, 0.1328125, 0.263671875},
		pushed = {0.7568359375, 0.8037109375, 0.40234375, 0.533203125},
	}

	-- Flipped coordinates
	local flippedCoords = {
		normal = {0.8037109375, 0.7568359375, 0.3984375, 0.267578125},
		highlight = {0.8525390625, 0.8056640625, 0.263671875, 0.1328125},
		pushed = {0.8037109375, 0.7568359375, 0.533203125, 0.40234375},
	}

	normalTex:SetTexCoord(unpack(flippedCoords.normal))
	highlightTex:SetTexCoord(unpack(flippedCoords.highlight))
	pushedTex:SetTexCoord(unpack(flippedCoords.pushed))

	buttonInv:SetScript("OnClick", function()
		Fatebound.Inventory:ShowInventory(chosenPath)

		-- Flip textures back if inventory is shown, otherwise flip again
		local invFrame = Fatebound.Inventory.frame
		if invFrame and invFrame:IsShown() then
			normalTex:SetTexCoord(unpack(coords.normal))
			highlightTex:SetTexCoord(unpack(coords.highlight))
			pushedTex:SetTexCoord(unpack(coords.pushed))
		else
			normalTex:SetTexCoord(unpack(flippedCoords.normal))
			highlightTex:SetTexCoord(unpack(flippedCoords.highlight))
			pushedTex:SetTexCoord(unpack(flippedCoords.pushed))
		end
	end)

	-- ================================
	-- Devotion Button & Timer
	-- ================================
	chosenPath.devotionButton = CreateFrame("Button", nil, chosenPath, "UIPanelButtonTemplate")
	chosenPath.devotionButton:SetSize(140, 32)
	chosenPath.devotionButton:SetPoint("BOTTOMRIGHT", -160, 20)
	chosenPath.devotionButton:SetText("Offer Devotion")
	chosenPath.devotionButton:SetFrameLevel(chosenPath.panel1:GetFrameLevel() + 10)

	chosenPath.devotionButton:SetScript("OnClick", function()
		if not IsResting() then
			UIErrorsFrame:AddMessage("You must be in a sanctuary of rest to offer devotion.", 1, 0, 0)
			return
		end	

		if FateboundDB.devoted then return end
		FateboundDB.devoted = true
		Fatebound:UpdateExpBar()

		local now = time()
		local obsession = FateboundDB.stats.Obsession or 0
		local dynamicDuration = BASE_PAUSE_DURATION + (obsession * 15)
		FateboundDB.drainPausedUntil = now + dynamicDuration

		-- Reset tier ramp so drain starts after devotion ends
		FateboundDB.drainStartTime = now
		FateboundDB.lastDrainTier = 0

		chosenPath.devotionButton:SetText("Devotion Active")
		chosenPath.devotionButton:Disable()
		chosenPath.devotionTimerText:Show()

		ns.ShowCenterMessage("Your allegiance echoes through the void… the chaos of the Old Gods consumes you.", FateboundDB.path)
		ns.UpdateChosenPathFrame()
	end)

	-- Timer text
	chosenPath.devotionTimerText = chosenPath.textOverlay:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	chosenPath.devotionTimerText:SetPoint("LEFT", chosenPath.devotionButton, "RIGHT", 10, 0)
	chosenPath.devotionTimerText:SetText("")
	chosenPath.devotionTimerText:Hide()

	-- Restore UI on login
	if FateboundDB.devoted and (FateboundDB.drainPausedUntil or 0) > time() then
		chosenPath.devotionButton:SetText("Devotion Active")
		chosenPath.devotionButton:Disable()
		chosenPath.devotionTimerText:Show()
	end

	-- Devotion timer OnUpdate
	local devotionTimerFrame = CreateFrame("Frame")
	local devotionTimerElapsed = 0
	devotionTimerFrame:Show()
	devotionTimerFrame:SetScript("OnUpdate", function(_, dt)
		devotionTimerElapsed = devotionTimerElapsed + dt
		if devotionTimerElapsed < 1 then return end
		devotionTimerElapsed = 0

		local remaining = (FateboundDB.drainPausedUntil or 0) - time()

		-- If devotion ended
		if remaining <= 0 then
			if FateboundDB.devoted then
				-- Reset devotion state
				FateboundDB.devoted = false
				Fatebound:UpdateExpBar()
				FateboundDB.drainStartTime = time() -- start tier ramp
				FateboundDB.lastDrainTier = 0
				FateboundDB.drainPausedUntil = 0

				if chosenPath.devotionButton then
					chosenPath.devotionButton:SetText("Offer Devotion")
					chosenPath.devotionButton:Enable()
				end
				if chosenPath.devotionTimerText then
					chosenPath.devotionTimerText:SetText("")
					chosenPath.devotionTimerText:Hide()
				end

				-- Immediately resume drain
				DrainExp()
				ns.drainFrame:Show()
			end
			return
		end

		-- Update timer text while devotion is active
		if chosenPath.devotionTimerText then
			local hours   = math.floor(remaining / 3600)
			local minutes = math.floor((remaining % 3600) / 60)
			local seconds = math.floor(remaining % 60)
			chosenPath.devotionTimerText:SetText(string.format("%02d:%02d:%02d", hours, minutes, seconds))
		end
	end)

	-- ================================
	-- EXPERIENCE / FAVOR BAR
	-- ================================
	-- Setup chosenPath expBar
	chosenPath.expBar = CreateFrame("Frame", nil, chosenPath)
	chosenPath.expBar:SetSize(190, 18)
	chosenPath.expBar:SetPoint("TOP", 0, -70)
	chosenPath.expBar:SetFrameLevel(chosenPath.panel1:GetFrameLevel() + 10)

	-- Text label (hidden by default, shows on hover)
	chosenPath.expBar.text = chosenPath.expBar:CreateFontString(nil, "OVERLAY", "GameFontNormal")
	chosenPath.expBar.text:SetPoint("BOTTOM", chosenPath.expBar, "TOP", 0, -14)
	chosenPath.expBar.text:SetJustifyH("CENTER")
	chosenPath.expBar.text:Hide()

	-- Enable mouse events for hover
	chosenPath.expBar:EnableMouse(true)

	-- Background texture
	chosenPath.expBar.bg = chosenPath.expBar:CreateTexture(nil, "BACKGROUND")
	chosenPath.expBar.bg:SetAllPoints(chosenPath.expBar)
	chosenPath.expBar.bg:SetTexture("Interface/QuestFrame/BonusObjectives")
	chosenPath.expBar.bg:SetTexCoord(0.474609375, 0.8515625, 0.6796875, 0.71484375)
	chosenPath.expBar.bg:SetVertexColor(0.15, 0.15, 0.15, 1)

	-- Fill texture
	chosenPath.expBar.fill = chosenPath.expBar:CreateTexture(nil, "ARTWORK")
	chosenPath.expBar.fill:SetHeight(16)
	chosenPath.expBar.fill:SetPoint("LEFT", chosenPath.expBar, "LEFT", 0, 0)
	--chosenPath.expBar.fill:SetTexture("Interface/QuestFrame/BonusObjectives")
	--chosenPath.expBar.fill:SetTexCoord(0.505859375, 0.5078125, 0.298828125, 0.333984375)
	--chosenPath.expBar.fill:SetTexture("interface/playerframe/priestinsanitybar")
	--chosenPath.expBar.fill:SetTexCoord(0, 0.5, 0.00390625, 0.04296875)
	chosenPath.expBar.fill:SetTexture("interface/hud/uicooldownmanager2x")
	chosenPath.expBar.fill:SetTexCoord(0.341796875, 0.826171875, 0.16015625, 0.23828125)

	-- Border
	chosenPath.expBar.border = chosenPath.expBar:CreateTexture(nil, "OVERLAY")
	chosenPath.expBar.border:SetPoint("CENTER", chosenPath.expBar, "CENTER", 18, 0)
	chosenPath.expBar.border:SetSize(240, 51)
	chosenPath.expBar.border:SetTexture("Interface/QuestFrame/BonusObjectives")
	chosenPath.expBar.border:SetTexCoord(0.001953125, 0.470703125, 0.615234375, 0.71484375)

	-- Fill padding
	chosenPath.expBar.fillPadding = 0

	-- ================================
	-- Hover text
	-- ================================
	chosenPath.expBar:SetScript("OnEnter", function(self)
		if (FateboundDB.favor or 0) >= ns.MAX_FAVOR then
			self.text:SetText("MAXED")
		else
			self.text:SetText((FateboundDB.exp or 0) .. " / " .. (ns.MAX_EXP or 100))
		end
		self.text:Show()
	end)

	chosenPath.expBar:SetScript("OnLeave", function(self)
		self.text:Hide()
	end)

	-- Path panel title bg
	chosenPath.panelTitleBG = chosenPath:CreateTexture(nil, "OVERLAY")
	chosenPath.panelTitleBG:SetPoint("CENTER", 138, 75)
	chosenPath.panelTitleBG:SetSize(272, 12)
	chosenPath.panelTitleBG:SetTexture("interface/professions/craftingorders")
    chosenPath.panelTitleBG:SetTexCoord(0.15625, 0.421875, 0.44921875, 0.47265625)
	chosenPath.panelTitleBG:SetDrawLayer("ARTWORK", 2)

	-- Path panel title
	chosenPath.panelTitle = chosenPath.textOverlay:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
	chosenPath.panelTitle:SetPoint("CENTER", 138, 90)
	chosenPath.panelTitle:SetShadowColor(0,0,0,1)
	chosenPath.panelTitle:SetShadowOffset(1,-1)
	chosenPath.panelTitle:SetText("")

	-- ================================
	-- Tabs & Panel Titles
	-- ================================

	-- Create table for tabs
	chosenPath.tabs = {}

	-- Tab labels per path
	chosenPath.tabLabels = {
		[1] = {OLD_GODS = "C'Thun",    PANTHEON = "Titan1", LOA = "Beast1"},
		[2] = {OLD_GODS = "Yogg-Saron",      PANTHEON = "Titan2", LOA = "Beast2"},
		[3] = {OLD_GODS = "Y'Shaarj",      PANTHEON = "Titan3", LOA = "Beast3"},
		[4] = {OLD_GODS = "G'huun",  PANTHEON = "Titan4", LOA = "Beast4"},
		[5] = {OLD_GODS = "N'Zoth",      PANTHEON = "Titan5", LOA = "Beast5"},
		[6] = {OLD_GODS = "Forge",   PANTHEON = "Forge", LOA = "Forge"}, -- Forge tab
	}

	-- Panel titles per path
	local PANEL_TITLES = {
		OLD_GODS = {
			"Oracle of Madness",
			"Scion of the Thousand-Faced God",
			"Herald of the Seven",
			"Avatar of the Rotted Core",
			"Warden of the Deep",
		},
		PANTHEON = {
			"Titan1TitleText",
			"Titan2TitleText",
			"Titan3TitleText",
			"Titan4TitleText",
			"Titan5TitleText",
		},
		LOA = {
			"Beast1TitleText",
			"Beast2TitleText",
			"Beast3TitleText",
			"Beast4TitleText",
			"Beast5TitleText",
		},
	}

	-- ================================
	-- Helper: Return cursor item safely
	-- ================================
	local function OnTabSwitch()
		local cursor = Fatebound.Inventory.CursorItem
		if cursor and cursor.item then
			Fatebound.Inventory:AddItem(cursor.item)
			Fatebound.Inventory.CursorItem = nil

			-- Safely hide cursor frame
			local cf = Fatebound.Inventory.cursorFrame
			if cf then
				cf:Hide()
				cf.texture:SetTexture(nil)
			end
		end
	end

	-- Create 5 main tabs
	for i = 1, 5 do
		local tab = CreateFateboundTab(chosenPath)
		chosenPath.tabs[i] = tab
		if i == 1 then
			tab:SetPoint("BOTTOMLEFT", 20, -28)
		else
			tab:SetPoint("LEFT", chosenPath.tabs[i-1], "RIGHT", -5, 0)
		end
	end

	-- Create Forge tab
	chosenPath.tabForge = CreateFateboundTab(chosenPath)
	chosenPath.tabForge:SetPoint("BOTTOMRIGHT", -20, -28)

	-- Function to update tab labels based on current path
	local function UpdateTabLabels()
		local pathID = FateboundDB.path
		if not pathID then return end

		for i, tab in ipairs(chosenPath.tabs) do
			local label = chosenPath.tabLabels[i] and chosenPath.tabLabels[i][pathID] or ""
			tab:SetLabel(label)
		end

		-- Forge tab
		chosenPath.tabForge:SetLabel(chosenPath.tabLabels[6] and chosenPath.tabLabels[6][pathID] or "Voidforge")
	end

	-- Call once to set initial labels
	UpdateTabLabels()

	-- Function to update the panel title based on selected tab
	local function UpdatePanelTitle(selectedTab)
		local pathID = FateboundDB.path
		if not pathID then return end

		local index
		for i, tab in ipairs(chosenPath.tabs) do
			if tab == selectedTab then index = i end
		end
		if selectedTab == chosenPath.tabForge then index = 6 end

		local title = PANEL_TITLES[pathID] and PANEL_TITLES[pathID][index] or ""
		chosenPath.panelTitle:SetText(title)
		chosenPath.panelTitle:Show()
	end

	-- Function to select a tab and handle Forge/devotion
	local function SelectTab(selectedTab)
		OnTabSwitch()

		-- Normal tabs
		for i, tab in ipairs(chosenPath.tabs) do
			local isSelected = (tab == selectedTab)
			tab:SetSelected(isSelected)
			chosenPath["panel"..i]:SetShown(isSelected)
		end

		-- Forge tab
		local isForge = (selectedTab == chosenPath.tabForge)
		chosenPath.tabForge:SetSelected(isForge)
		chosenPath.panelForge:SetShown(isForge)

		if isForge then
			Fatebound:UpdateCooldownList()
		end

		-- Hide devotion UI if Forge is selected
		SetDevotionUIVisible(not isForge)

		-- Hide parchment frame and panel title background if Forge is selected
		if chosenPath.parchmentFrame then
			chosenPath.parchmentFrame:SetShown(not isForge)
		end
		if chosenPath.panelTitleBG then
			chosenPath.panelTitleBG:SetShown(not isForge)
		end
		
		-- Hide description toggle checkbox on Forge panel
		if showDescCheckbox then
			showDescCheckbox:SetShown(not isForge)
		end

		-- Update panel title
		UpdatePanelTitle(selectedTab)
	end

	-- Assign OnClick scripts to main tabs
	for i, tab in ipairs(chosenPath.tabs) do
		tab:SetScript("OnClick", function() SelectTab(tab) end)
	end

	-- Assign OnClick script to Forge tab
	chosenPath.tabForge:SetScript("OnClick", function()
		SelectTab(chosenPath.tabForge)
	end)

	-- Initial selection: first normal tab
	SelectTab(chosenPath.tabs[1])

	-- Ensure frame can be closed with ESC
	tinsert(UISpecialFrames, "FateboundChosenPathFrame")
	FateboundChosenPathFrame = chosenPath

	-- Update frame visuals
	Fatebound:RefreshAll()
	ns.UpdateChosenPathFrame()
end

-- ================================
-- FRAME TOGGLE
-- ================================
function ToggleChosenPathFrame()
    if not FateboundChosenPathFrame then
        CreateChosenPathFrame()
        ns.UpdateChosenPathFrame()
        FateboundChosenPathFrame:Show()
        return
    end

    if FateboundChosenPathFrame:IsShown() then
        FateboundChosenPathFrame:Hide()
    else
        ns.UpdateChosenPathFrame()
        FateboundChosenPathFrame:Show()
    end
end

-- ================================
-- QUEST TURN-IN EXPERIENCE HANDLER
-- ================================
local expFrame = CreateFrame("Frame")
expFrame:RegisterEvent("QUEST_TURNED_IN")
expFrame:SetScript("OnEvent", function(_, questID, xpReward)
    -- Path required
    if not FateboundDB.path then return end

    -- Devotion required
    if not FateboundDB.devoted then
        if not FateboundDB.disableDevotionPrint then
            print("|cffff4d4dFatebound: You are not devoted. Offer Devotion to gain favor.|r")
        end
        return
    end

    -- Add EXP from quest
    FateboundDB.exp = (FateboundDB.exp or 0) + ns.EXP_PER_QUEST
    FateboundDB.favor = FateboundDB.favor or 0

    while FateboundDB.exp >= ns.MAX_EXP do
        FateboundDB.exp = FateboundDB.exp - ns.MAX_EXP

        if FateboundDB.favor < ns.MAX_FAVOR then
            FateboundDB.favor = FateboundDB.favor + 1

			Fatebound:RefreshPerkIcons()
            Fatebound:UpdateFavorLabel()
            Fatebound:RefreshAll()

            if not FateboundDB.disableFavorPrint then
                print("|cff6f6fffFatebound: You gained 1 favor! Total Favor: " .. FateboundDB.favor .. ".|r")
            end
        else
            FateboundDB.exp = ns.MAX_EXP
            break
        end
    end

    if Fatebound.UpdateExpBar then
        Fatebound:UpdateExpBar()
    end
    if FateboundChosenPathFrame and FateboundChosenPathFrame.expBar then
        local bar = FateboundChosenPathFrame.expBar
        if bar:IsMouseOver() and bar.text then
            if FateboundDB.favor >= ns.MAX_FAVOR then
                bar.text:SetText("MAXED")
            else
                bar.text:SetText(FateboundDB.exp .. " / " .. ns.MAX_EXP)
            end
        end
    end

    ns.UpdateChosenPathFrame()
end)

-- ================================
-- SLASH COMMAND: Gain EXP / Favor
-- ================================
SLASH_GAINEXP1 = "/gainexp"

SlashCmdList["GAINEXP"] = function(msg)
    local amount = tonumber(msg)
    if not amount or amount <= 0 then
        print("|cffff4d4dFatebound: Invalid amount. Usage: /gainexp <amount>|r")
        return
    end

    if not FateboundDB.exp then FateboundDB.exp = 0 end
    if not FateboundDB.favor then FateboundDB.favor = 0 end

    FateboundDB.exp = FateboundDB.exp + amount

    -- Handle leveling: every ns.MAX_EXP EXP grants 1 favor
    while FateboundDB.exp >= ns.MAX_EXP do
        FateboundDB.exp = FateboundDB.exp - ns.MAX_EXP

        if FateboundDB.favor < ns.MAX_FAVOR then
            FateboundDB.favor = FateboundDB.favor + 1
            print("|cff6f6fffFatebound: Gained 1 favor! Total Favor: " .. FateboundDB.favor .. ".|r")
        else
            -- If favor is maxed, cap EXP at MAX_EXP
            FateboundDB.exp = ns.MAX_EXP
            break
        end
    end

    -- Update favor label immediately
    if Fatebound.UpdateFavorLabel then
		Fatebound:RefreshPerkIcons()
        Fatebound:UpdateFavorLabel()
		Fatebound:RefreshAll()
    end

    -- Update the experience bar
    if Fatebound.UpdateExpBar then
        Fatebound:UpdateExpBar()
    end

    -- Update hover text immediately if mouse is over
    if FateboundChosenPathFrame and FateboundChosenPathFrame.expBar then
        local bar = FateboundChosenPathFrame.expBar
        if bar:IsMouseOver() and bar.text then
            if (FateboundDB.favor or 0) >= ns.MAX_FAVOR then
                bar.text:SetText("MAXED")
            else
                bar.text:SetText((FateboundDB.exp or 0) .. " / " .. ns.MAX_EXP)
            end
        end
    end

    print("|cff6f6fffFatebound: Gained " .. amount .. " EXP.|r")
end
