local addonName = ...

-- ============================================================
--  Localisation  (L["key"] for every user-facing string)
-- ============================================================
local L = {}
do
    local locale = GetLocale()

    -- ── English (default) ────────────────────────────────────────────────────
    L["Custom Font Loader"]        = "Custom Font Loader"
    L["Author: Exxos"]             = "Author: Exxos"
    L["Instructions"]              = "Instructions"
    L["Font Manager"]              = "Font Manager"
    L["Utilities"]                 = "Utilities"
    L["How to add fonts to CustomFontLoader"] = "How to add fonts to CustomFontLoader"
    L["Fonts registered with LibSharedMedia-3.0"] = "Fonts registered with LibSharedMedia-3.0"
    L["HUD display options"]       = "HUD display options"
    -- Instructions pane
    L["INSTR_TITLE"]               = "How to add a new font"
    L["INSTR_STEP1_HDR"]           = "Step 1 — Place your font file"
    L["INSTR_STEP1_BODY"]          = "Copy your .ttf font file(s) into WoW's shared Fonts folder (never touched by addon updates):"
    L["INSTR_STEP1_NOTE"]          = "Avoid spaces in filenames — use hyphens instead (e.g. My-Font-Bold.ttf)."
    L["INSTR_STEP2_HDR"]           = "Step 2 — Reload"
    L["INSTR_STEP2_BODY"]          = "WoW must load the files before they can be registered. Type  /reload  or use the button below:"
    L["Reload UI"]                 = "Reload UI"
    L["INSTR_STEP2_NOTE"]          = "If you use the button above, this addon will reopen automatically after the reload."
    L["INSTR_STEP3_HDR"]           = "Step 3 — Register in the Font Manager"
    L["INSTR_STEP3_BODY"]          = "Switch to the Font Manager tab, type the filename without the extension, then click Add Font."
    L["INSTR_STEP3_NOTE"]          = "The font is immediately available in EllesmereUI, Plater, BigWigs, and any other SharedMedia addon."
    L["Notes"]                     = "Notes"
    L["NOTE1"]                     = "   -  Put your fonts in Interface\\Fonts\\ — this folder is shared across all addons and never touched by CurseForge."
    L["NOTE2"]                     = "   -  Only .ttf files are supported — .otf files will not load in WoW."
    L["NOTE3"]                     = "   -  If you delete a font file, the game will not recognize the removal until a full restart."
    L["NOTE4"]                     = "   -  After a full restart, missing fonts are automatically cleaned up on login."
    -- Font Manager pane
    L["Registered Fonts"]          = "Registered Fonts"
    L["FONT_COUNT"]                = "%d font(s) registered"
    L["Add New Font"]              = "Add New Font"
    L["ADDFONT_INSTR"]             = L["ADDFONT_INSTR"]
    L["Font Filename"]             = "Font Filename"
    L["Add Font"]                  = "Add Font"
    L["file not found"]            = "file not found"
    L["No fonts registered yet.  Add one below."] = "No fonts registered yet.  Add one below."
    L["ERR_LSM"]                   = "LibSharedMedia-3.0 not found."
    L["ERR_EMPTY"]                 = "Enter a font filename."
    L["ERR_EXT"] = [["%s" is not a valid format for WoW. Only .ttf files are supported.]]
    L["ERR_MISSING"] = [["%s" not found. Place the .ttf file in Interface\\Fonts\\ or the addon Fonts folder, then try again.]]
    L["STATUS_ADDED"] = [["%s" added.]]
    L["STATUS_REMOVED"]            = "  Removed %d missing font(s)."
    L["PRINT_REGISTERED"]          = "|cFFAA44FFCustomFontLoader:|r Registered font: %s"
    L["PRINT_CLEANED"]             = "|cFFAA44FFCustomFontLoader:|r Removed %d font(s) with missing files.  %d font(s) active."
    -- Utilities / HUD
    L["HUD"]                       = "HUD"
    L["HUD_DESC"]                  = "A compact top-of-screen bar with live stats, clock, and quick-action buttons."
    L["Show HUD"]                  = "Show HUD"
    L["Show:"]                     = "Show:"
    L["Currency"]                  = "Currency"
    L["Friends"]                   = "Friends"
    L["Clock"]                     = "Clock"
    L["FPS"]                       = "FPS"
    L["MS"]                        = "MS"
    L["Durability"]                = "Durability"
    L["Party"]                     = "Party"
    L["Screenshot"]                = "Screenshot"
    L["Hero DC"]                   = "Hero DC"
    L["Myth DC"]                   = "Myth DC"
    L["Voidcore"]                  = "Voidcore"
    L["Hearthstone"]               = "Hearthstone"
    L["Text Style"]                = "Text Style"
    L["TEXT_STYLE_DESC"]           = "Customize font, size, and color for each HUD element."
    L["Currency_lbl"]              = "Currency"
    L["Friends count"]             = "Friends count"
    L["Party count"]               = "Party count"
    L["Clock_lbl"]                 = "Clock"
    L["Stats (FPS / MS / Dur)"]    = "Stats (FPS / MS / Dur)"
    L["Hero Dawncrest"]            = "Hero Dawncrest"
    L["Myth Dawncrest"]            = "Myth Dawncrest"
    L["Nebulous Voidcore"]         = "Nebulous Voidcore"
    L["Hearthstone Button"]        = "Hearthstone Button"
    L["HS_BUTTON_DESC"]            = "Assign a spell or toy to each mouse button on the Hearthstone HUD button."
    L["Left-click:"]               = "Left-click:"
    L["Right-click:"]              = "Right-click:"
    L["Middle-click:"]             = "Middle-click:"
    L["Clock Format"]              = "Clock Format"
    L["Format:"]                   = "Format:"
    L["Source:"]                   = "Source:"
    L["Server"]                    = "Server"
    L["Local"]                     = "Local"
    L["Position & Layout"]         = "Position & Layout"
    L["Reset HUD Position"]        = "Reset HUD Position"
    L["Lock Position"]             = "Lock Position"
    L["HUD Scale"]                 = "HUD Scale"
    L["Font:"]                     = "Font:"
    L["Size:"]                     = "Size:"
    L["Color:"]                    = "Color:"
    L["Nothing (disabled)"]        = "Nothing (disabled)"
    L["Gold"]                      = "Gold"
    L["Icon Order"]                = "Icon Order"
    L["ICON_ORDER_DESC"]           = "Drag an item to reorder it. Left/Right picks which side of the clock it sits on — switching sides moves it to the front of that side."
    L["Left"]                      = "Left"
    L["Right"]                     = "Right"
    L["HUD_NUDGE_HINT"]            = "Nudge 1px (Shift-click = 10px)"
    L["Nudge Position"]            = "Nudge:"
    L["HUD_SELECT_HINT"]           = "Arrow keys move 1px (Shift = 10px) — Esc or Save to finish"
    L["Save Position"]             = "Save Position"
    -- Tooltips
    L["TT_GOLD"]                   = "Gold"
    L["TT_FRIENDS"]                = "Friends Online: %s"
    L["TT_FRIENDS_INWOW"]          = "In WoW:"
    L["TT_FRIENDS_NONE"]           = "No friends in WoW right now"
    L["TT_RAID_SIZE"]              = "Raid Size"
    L["TT_PARTY_SIZE"]             = "Party Size"
    L["TT_SCREENSHOT"]             = "Screenshot (silent)"
    L["TT_CLOCK"]                  = "Click to toggle 12h/24h"
    L["TT_HEARTHSTONE"]            = "Hearthstone / Teleport"
    L["TT_MINIMAP"]                = "Custom Font Loader"
    L["TT_MINIMAP_CLICK"]          = "Left-click: Toggle UI"
    L["TT_MINIMAP_DRAG"]           = "Drag: Reposition"
    L["Left-click: "]              = "Left-click: "
    L["Right-click: "]             = "Right-click: "
    L["Middle-click: "]            = "Middle-click: "

    -- ── Simplified Chinese (zhCN) ─────────────────────────────────────────────
    if locale == "zhCN" then
        L["Custom Font Loader"]        = "自定义字体加载器"
        L["Author: Exxos"]             = "作者: Exxos"
        L["Instructions"]              = "说明"
        L["Font Manager"]              = "字体管理器"
        L["Utilities"]                 = "实用工具"
        L["How to add fonts to CustomFontLoader"] = "如何向自定义字体加载器添加字体"
        L["Fonts registered with LibSharedMedia-3.0"] = "已通过 LibSharedMedia-3.0 注册的字体"
        L["HUD display options"]       = "HUD 显示选项"
        L["INSTR_TITLE"]               = "如何添加新字体"
        L["INSTR_STEP1_HDR"]           = "第一步 — 放置字体文件"
        L["INSTR_STEP1_BODY"]          = "将 .ttf 字体文件复制到插件的 Fonts 文件夹："
        L["INSTR_STEP1_NOTE"]          = "文件名中避免使用空格，请改用连字符（例如 My-Font-Bold.ttf）。"
        L["INSTR_STEP2_HDR"]           = "第二步 — 重新加载"
        L["INSTR_STEP2_BODY"]          = "WoW 需要加载文件后才能注册。输入 /reload 或使用下方按钮："
        L["Reload UI"]                 = "重载界面"
        L["INSTR_STEP2_NOTE"]          = "如果使用上方按钮，重载后此插件将自动重新打开。"
        L["INSTR_STEP3_HDR"]           = "第三步 — 在字体管理器中注册"
        L["INSTR_STEP3_BODY"]          = "切换到字体管理器选项卡，输入不带扩展名的文件名，然后点击【添加字体】。"
        L["INSTR_STEP3_NOTE"]          = "字体立即可在 EllesmereUI、Plater、BigWigs 及任何其他 SharedMedia 插件中使用。"
        L["Notes"]                     = "注意事项"
        L["NOTE1"]                     = "   -  字体会自动保存，每次登录时重新注册。"
        L["NOTE2"]                     = "   -  仅支持 .ttf 文件，.otf 文件无法在 WoW 中加载。"
        L["NOTE3"]                     = "   -  如果删除字体文件，游戏在完全重启前不会识别该删除操作。"
        L["NOTE4"]                     = "   -  完全重启后，缺失的字体将在登录时自动清理。"
        L["Registered Fonts"]          = "已注册字体"
        L["FONT_COUNT"]                = "已注册 %d 个字体"
        L["Add New Font"]              = "添加新字体"
        L["ADDFONT_INSTR"]             = "将 .ttf 文件放入 Fonts 文件夹，在下方输入文件名，然后点击【添加字体】。"
        L["Font Filename"]             = "字体文件名"
        L["Add Font"]                  = "添加字体"
        L["file not found"]            = "文件未找到"
        L["No fonts registered yet.  Add one below."] = "尚未注册字体，请在下方添加。"
        L["ERR_LSM"]                   = "未找到 LibSharedMedia-3.0。"
        L["ERR_EMPTY"]                 = "请输入字体文件名。"
        L["ERR_EXT"] = [["%s" 是无效格式，WoW 仅支持 .ttf 文件。]]
        L["ERR_MISSING"] = [["%s" 未在 Fonts 文件夹中找到，请检查文件名后重试。]]
        L["STATUS_ADDED"] = [["%s" 已添加。]]
        L["STATUS_REMOVED"]            = "  已移除 %d 个缺失字体。"
        L["HUD"]                       = "HUD"
        L["HUD_DESC"]                  = "屏幕顶部的紧凑状态栏，显示实时数据、时钟和快捷操作按钮。"
        L["Show HUD"]                  = "显示 HUD"
        L["Show:"]                     = "显示："
        L["Currency"]                  = "货币"
        L["Friends"]                   = "好友"
        L["Clock"]                     = "时钟"
        L["Durability"]                = "耐久"
        L["Party"]                     = "队伍"
        L["Screenshot"]                = "截图"
        L["Text Style"]                = "文字样式"
        L["TEXT_STYLE_DESC"]           = "自定义每个 HUD 元素的字体、大小和颜色。"
        L["Friends count"]             = "在线好友数"
        L["Party count"]               = "队伍人数"
        L["Stats (FPS / MS / Dur)"]    = "统计（FPS / 延迟 / 耐久）"
        L["Hero Dawncrest"]            = "英雄黎明纹章"
        L["Myth Dawncrest"]            = "神话黎明纹章"
        L["Nebulous Voidcore"]         = "星云虚空核心"
        L["Hearthstone Button"]        = "炉石按钮"
        L["HS_BUTTON_DESC"]            = "为 HUD 炉石按钮的每个鼠标键分配法术或玩具。"
        L["Left-click:"]               = "左键："
        L["Right-click:"]              = "右键："
        L["Middle-click:"]             = "中键："
        L["Clock Format"]              = "时钟格式"
        L["Format:"]                   = "格式："
        L["Source:"]                   = "来源："
        L["Server"]                    = "服务器"
        L["Local"]                     = "本地"
        L["Position & Layout"]         = "位置与布局"
        L["Reset HUD Position"]        = "重置 HUD 位置"
        L["Lock Position"]             = "锁定位置"
        L["HUD Scale"]                 = "HUD 缩放"
        L["Font:"]                     = "字体："
        L["Size:"]                     = "大小："
        L["Color:"]                    = "颜色："
        L["Nothing (disabled)"]        = "无（禁用）"
        L["TT_GOLD"]                   = "金币"
        L["TT_FRIENDS"]                = "在线好友：%s"
        L["TT_FRIENDS_INWOW"]          = "正在玩魔兽："
        L["TT_FRIENDS_NONE"]           = "当前没有好友在线玩魔兽"
        L["TT_RAID_SIZE"]              = "团队人数"
        L["TT_PARTY_SIZE"]             = "队伍人数"
        L["TT_SCREENSHOT"]             = "截图（静默）"
        L["TT_CLOCK"]                  = "点击切换 12/24 小时制"
        L["TT_HEARTHSTONE"]            = "炉石 / 传送"
        L["TT_MINIMAP"]                = "自定义字体加载器"
        L["TT_MINIMAP_CLICK"]          = "左键：切换界面"
        L["TT_MINIMAP_DRAG"]           = "拖动：重新定位"
        L["Left-click: "]              = "左键："
        L["Right-click: "]             = "右键："
        L["Middle-click: "]            = "中键："

    -- ── Traditional Chinese (zhTW) ────────────────────────────────────────────
    elseif locale == "zhTW" then
        L["Custom Font Loader"]        = "自訂字型載入器"
        L["Author: Exxos"]             = "作者: Exxos"
        L["Instructions"]              = "說明"
        L["Font Manager"]              = "字型管理員"
        L["Utilities"]                 = "實用工具"
        L["How to add fonts to CustomFontLoader"] = "如何新增字型至自訂字型載入器"
        L["Fonts registered with LibSharedMedia-3.0"] = "已透過 LibSharedMedia-3.0 登錄的字型"
        L["HUD display options"]       = "HUD 顯示選項"
        L["INSTR_TITLE"]               = "如何新增字型"
        L["INSTR_STEP1_HDR"]           = "步驟一 — 放置字型檔案"
        L["INSTR_STEP1_BODY"]          = "將 .ttf 字型檔案複製到插件的 Fonts 資料夾："
        L["INSTR_STEP1_NOTE"]          = "檔名中避免使用空格，請改用連字符（例如 My-Font-Bold.ttf）。"
        L["INSTR_STEP2_HDR"]           = "步驟二 — 重新載入"
        L["INSTR_STEP2_BODY"]          = "WoW 需要載入檔案後才能登錄。輸入 /reload 或使用下方按鈕："
        L["Reload UI"]                 = "重載介面"
        L["INSTR_STEP2_NOTE"]          = "若使用上方按鈕，重載後此插件將自動重新開啟。"
        L["INSTR_STEP3_HDR"]           = "步驟三 — 在字型管理員中登錄"
        L["INSTR_STEP3_BODY"]          = "切換至字型管理員頁籤，輸入不含副檔名的檔名，然後點選「新增字型」。"
        L["INSTR_STEP3_NOTE"]          = "字型可立即在 EllesmereUI、Plater、BigWigs 及任何其他 SharedMedia 插件中使用。"
        L["Notes"]                     = "注意事項"
        L["NOTE1"]                     = "   -  字型會自動儲存，每次登入時重新登錄。"
        L["NOTE2"]                     = "   -  僅支援 .ttf 檔案，.otf 檔案無法在 WoW 中載入。"
        L["NOTE3"]                     = "   -  刪除字型檔案後，遊戲在完全重啟前不會識別該刪除動作。"
        L["NOTE4"]                     = "   -  完全重啟後，缺失的字型將在登入時自動清理。"
        L["Registered Fonts"]          = "已登錄字型"
        L["FONT_COUNT"]                = "已登錄 %d 個字型"
        L["Add New Font"]              = "新增字型"
        L["ADDFONT_INSTR"]             = "將 .ttf 檔案放入 Fonts 資料夾，在下方輸入檔名，然後點選「新增字型」。"
        L["Font Filename"]             = "字型檔名"
        L["Add Font"]                  = "新增字型"
        L["file not found"]            = "找不到檔案"
        L["No fonts registered yet.  Add one below."] = "尚未登錄字型，請在下方新增。"
        L["ERR_LSM"]                   = "找不到 LibSharedMedia-3.0。"
        L["ERR_EMPTY"]                 = "請輸入字型檔名。"
        L["ERR_EXT"] = [["%s" 為無效格式，WoW 僅支援 .ttf 檔案。]]
        L["ERR_MISSING"] = [["%s" 未在 Fonts 資料夾中找到，請確認檔名後重試。]]
        L["STATUS_ADDED"] = [["%s" 已新增。]]
        L["STATUS_REMOVED"]            = "  已移除 %d 個缺失字型。"
        L["HUD"]                       = "HUD"
        L["HUD_DESC"]                  = "螢幕頂部的精簡狀態列，顯示即時數據、時鐘與快速操作按鈕。"
        L["Show HUD"]                  = "顯示 HUD"
        L["Show:"]                     = "顯示："
        L["Currency"]                  = "貨幣"
        L["Friends"]                   = "好友"
        L["Clock"]                     = "時鐘"
        L["Durability"]                = "耐久"
        L["Party"]                     = "隊伍"
        L["Screenshot"]                = "截圖"
        L["Text Style"]                = "文字樣式"
        L["TEXT_STYLE_DESC"]           = "自訂每個 HUD 元素的字型、大小和顏色。"
        L["Friends count"]             = "線上好友數"
        L["Party count"]               = "隊伍人數"
        L["Stats (FPS / MS / Dur)"]    = "統計（FPS / 延遲 / 耐久）"
        L["Hero Dawncrest"]            = "英雄黎明紋章"
        L["Myth Dawncrest"]            = "神話黎明紋章"
        L["Nebulous Voidcore"]         = "星雲虛空核心"
        L["Hearthstone Button"]        = "爐石按鈕"
        L["HS_BUTTON_DESC"]            = "為 HUD 爐石按鈕的每個滑鼠鍵指定法術或玩具。"
        L["Left-click:"]               = "左鍵："
        L["Right-click:"]              = "右鍵："
        L["Middle-click:"]             = "中鍵："
        L["Clock Format"]              = "時鐘格式"
        L["Format:"]                   = "格式："
        L["Source:"]                   = "來源："
        L["Server"]                    = "伺服器"
        L["Local"]                     = "本地"
        L["Position & Layout"]         = "位置與版面"
        L["Reset HUD Position"]        = "重置 HUD 位置"
        L["Lock Position"]             = "鎖定位置"
        L["HUD Scale"]                 = "HUD 縮放"
        L["Font:"]                     = "字型："
        L["Size:"]                     = "大小："
        L["Color:"]                    = "顏色："
        L["Nothing (disabled)"]        = "無（停用）"
        L["TT_GOLD"]                   = "金幣"
        L["TT_FRIENDS"]                = "線上好友：%s"
        L["TT_FRIENDS_INWOW"]          = "正在玩魔獸："
        L["TT_FRIENDS_NONE"]           = "目前沒有好友在線上玩魔獸"
        L["TT_RAID_SIZE"]              = "團隊人數"
        L["TT_PARTY_SIZE"]             = "隊伍人數"
        L["TT_SCREENSHOT"]             = "截圖（靜音）"
        L["TT_CLOCK"]                  = "點擊切換 12/24 小時制"
        L["TT_HEARTHSTONE"]            = "爐石 / 傳送"
        L["TT_MINIMAP"]                = "自訂字型載入器"
        L["TT_MINIMAP_CLICK"]          = "左鍵：切換介面"
        L["TT_MINIMAP_DRAG"]           = "拖曳：重新定位"
        L["Left-click: "]              = "左鍵："
        L["Right-click: "]             = "右鍵："
        L["Middle-click: "]            = "中鍵："

    -- ── Korean (koKR) ─────────────────────────────────────────────────────────
    elseif locale == "koKR" then
        L["Custom Font Loader"]        = "커스텀 폰트 로더"
        L["Author: Exxos"]             = "제작자: Exxos"
        L["Instructions"]              = "사용 방법"
        L["Font Manager"]              = "폰트 관리자"
        L["Utilities"]                 = "유틸리티"
        L["How to add fonts to CustomFontLoader"] = "커스텀 폰트 로더에 폰트 추가하는 방법"
        L["Fonts registered with LibSharedMedia-3.0"] = "LibSharedMedia-3.0에 등록된 폰트"
        L["HUD display options"]       = "HUD 표시 옵션"
        L["INSTR_TITLE"]               = "새 폰트 추가 방법"
        L["INSTR_STEP1_HDR"]           = "1단계 — 폰트 파일 배치"
        L["INSTR_STEP1_BODY"]          = ".ttf 폰트 파일을 애드온 Fonts 폴더에 복사하세요:"
        L["INSTR_STEP1_NOTE"]          = "파일명에 공백 사용 금지 — 하이픈 사용 권장 (예: My-Font-Bold.ttf)."
        L["INSTR_STEP2_HDR"]           = "2단계 — 리로드"
        L["INSTR_STEP2_BODY"]          = "등록 전에 WoW가 파일을 로드해야 합니다. /reload를 입력하거나 아래 버튼을 사용하세요:"
        L["Reload UI"]                 = "UI 다시 로드"
        L["INSTR_STEP2_NOTE"]          = "위 버튼을 사용하면 리로드 후 이 애드온이 자동으로 다시 열립니다."
        L["INSTR_STEP3_HDR"]           = "3단계 — 폰트 관리자에서 등록"
        L["INSTR_STEP3_BODY"]          = "폰트 관리자 탭으로 전환하고, 확장자 없이 파일명을 입력한 후 폰트 추가를 클릭하세요."
        L["INSTR_STEP3_NOTE"]          = "폰트는 EllesmereUI, Plater, BigWigs 및 다른 SharedMedia 애드온에서 즉시 사용할 수 있습니다."
        L["Notes"]                     = "참고"
        L["NOTE1"]                     = "   -  폰트는 자동으로 저장되며 로그인할 때마다 재등록됩니다."
        L["NOTE2"]                     = "   -  .ttf 파일만 지원됩니다 — .otf 파일은 WoW에서 로드되지 않습니다."
        L["NOTE3"]                     = "   -  폰트 파일을 삭제해도 게임은 완전히 재시작하기 전까지 이를 인식하지 못합니다."
        L["NOTE4"]                     = "   -  완전히 재시작하면 누락된 폰트가 로그인 시 자동으로 정리됩니다."
        L["Registered Fonts"]          = "등록된 폰트"
        L["FONT_COUNT"]                = "%d개 폰트 등록됨"
        L["Add New Font"]              = "새 폰트 추가"
        L["ADDFONT_INSTR"]             = ".ttf 파일을 Fonts 폴더에 넣고, 아래에 파일명을 입력한 후 폰트 추가를 클릭하세요."
        L["Font Filename"]             = "폰트 파일명"
        L["Add Font"]                  = "폰트 추가"
        L["file not found"]            = "파일 없음"
        L["No fonts registered yet.  Add one below."] = "등록된 폰트가 없습니다. 아래에서 추가하세요."
        L["ERR_LSM"]                   = "LibSharedMedia-3.0을 찾을 수 없습니다."
        L["ERR_EMPTY"]                 = "폰트 파일명을 입력하세요."
        L["ERR_EXT"] = [["%s"은(는) 유효하지 않은 형식입니다. .ttf 파일만 지원됩니다.]]
        L["ERR_MISSING"] = [["%s"을(를) Fonts 폴더에서 찾을 수 없습니다. 파일명을 확인하고 다시 시도하세요.]]
        L["STATUS_ADDED"] = [["%s" 추가됨.]]
        L["STATUS_REMOVED"]            = "  누락된 폰트 %d개 제거됨."
        L["HUD"]                       = "HUD"
        L["HUD_DESC"]                  = "실시간 통계, 시계, 빠른 실행 버튼이 있는 화면 상단의 간결한 표시줄."
        L["Show HUD"]                  = "HUD 표시"
        L["Show:"]                     = "표시:"
        L["Currency"]                  = "화폐"
        L["Friends"]                   = "친구"
        L["Clock"]                     = "시계"
        L["Durability"]                = "내구도"
        L["Party"]                     = "파티"
        L["Screenshot"]                = "스크린샷"
        L["Text Style"]                = "텍스트 스타일"
        L["TEXT_STYLE_DESC"]           = "각 HUD 요소의 폰트, 크기, 색상을 사용자 지정하세요."
        L["Friends count"]             = "온라인 친구 수"
        L["Party count"]               = "파티 인원"
        L["Stats (FPS / MS / Dur)"]    = "통계 (FPS / 지연 / 내구도)"
        L["Hero Dawncrest"]            = "영웅 새벽문장"
        L["Myth Dawncrest"]            = "신화 새벽문장"
        L["Nebulous Voidcore"]         = "성운 공허핵"
        L["Hearthstone Button"]        = "귀환석 버튼"
        L["HS_BUTTON_DESC"]            = "HUD 귀환석 버튼의 각 마우스 버튼에 주문 또는 장난감을 할당하세요."
        L["Left-click:"]               = "좌클릭:"
        L["Right-click:"]              = "우클릭:"
        L["Middle-click:"]             = "휠클릭:"
        L["Clock Format"]              = "시계 형식"
        L["Format:"]                   = "형식:"
        L["Source:"]                   = "소스:"
        L["Server"]                    = "서버"
        L["Local"]                     = "현지"
        L["Position & Layout"]         = "위치 및 레이아웃"
        L["Reset HUD Position"]        = "HUD 위치 초기화"
        L["Lock Position"]             = "위치 잠금"
        L["HUD Scale"]                 = "HUD 크기"
        L["Font:"]                     = "폰트:"
        L["Size:"]                     = "크기:"
        L["Color:"]                    = "색상:"
        L["Nothing (disabled)"]        = "없음 (비활성화)"
        L["TT_GOLD"]                   = "골드"
        L["TT_FRIENDS"]                = "온라인 친구: %s"
        L["TT_FRIENDS_INWOW"]          = "WoW 플레이 중:"
        L["TT_FRIENDS_NONE"]           = "현재 WoW를 플레이 중인 친구가 없습니다"
        L["TT_RAID_SIZE"]              = "공격대 인원"
        L["TT_PARTY_SIZE"]             = "파티 인원"
        L["TT_SCREENSHOT"]             = "스크린샷 (무음)"
        L["TT_CLOCK"]                  = "클릭하여 12시간/24시간제 전환"
        L["TT_HEARTHSTONE"]            = "귀환석 / 텔레포트"
        L["TT_MINIMAP"]                = "커스텀 폰트 로더"
        L["TT_MINIMAP_CLICK"]          = "좌클릭: UI 전환"
        L["TT_MINIMAP_DRAG"]           = "드래그: 위치 변경"
        L["Left-click: "]              = "좌클릭: "
        L["Right-click: "]             = "우클릭: "
        L["Middle-click: "]            = "휠클릭: "

    -- ── French (frFR) ─────────────────────────────────────────────────────────
    elseif locale == "frFR" then
        L["Custom Font Loader"]        = "Chargeur de Polices"
        L["Author: Exxos"]             = "Auteur : Exxos"
        L["Instructions"]              = "Instructions"
        L["Font Manager"]              = "Gestionnaire de Polices"
        L["Utilities"]                 = "Utilitaires"
        L["How to add fonts to CustomFontLoader"] = "Comment ajouter des polices"
        L["Fonts registered with LibSharedMedia-3.0"] = "Polices enregistrées avec LibSharedMedia-3.0"
        L["HUD display options"]       = "Options d'affichage HUD"
        L["INSTR_TITLE"]               = "Comment ajouter une nouvelle police"
        L["INSTR_STEP1_HDR"]           = "Étape 1 — Placer le fichier de police"
        L["INSTR_STEP1_BODY"]          = "Copiez vos fichiers .ttf dans le dossier Fonts de l'addon :"
        L["INSTR_STEP1_NOTE"]          = "Évitez les espaces dans les noms de fichiers — utilisez des tirets (ex. Ma-Police-Gras.ttf)."
        L["INSTR_STEP2_HDR"]           = "Étape 2 — Recharger"
        L["INSTR_STEP2_BODY"]          = "WoW doit charger les fichiers avant de les enregistrer. Tapez /reload ou utilisez le bouton ci-dessous :"
        L["Reload UI"]                 = "Recharger l'UI"
        L["INSTR_STEP2_NOTE"]          = "Si vous utilisez le bouton ci-dessus, cet addon se rouvrira automatiquement après le rechargement."
        L["INSTR_STEP3_HDR"]           = "Étape 3 — Enregistrer dans le Gestionnaire de Polices"
        L["INSTR_STEP3_BODY"]          = "Passez à l'onglet Gestionnaire de Polices, saisissez le nom de fichier sans extension, puis cliquez sur Ajouter Police."
        L["INSTR_STEP3_NOTE"]          = "La police est immédiatement disponible dans EllesmereUI, Plater, BigWigs et tout autre addon SharedMedia."
        L["Notes"]                     = "Notes"
        L["NOTE1"]                     = "   -  Les polices sont sauvegardées automatiquement et ré-enregistrées à chaque connexion."
        L["NOTE2"]                     = "   -  Seuls les fichiers .ttf sont pris en charge — les fichiers .otf ne se chargent pas dans WoW."
        L["NOTE3"]                     = "   -  Si vous supprimez un fichier de police, le jeu ne le reconnaîtra qu'après un redémarrage complet."
        L["NOTE4"]                     = "   -  Après un redémarrage complet, les polices manquantes sont nettoyées automatiquement à la connexion."
        L["Registered Fonts"]          = "Polices enregistrées"
        L["FONT_COUNT"]                = "%d police(s) enregistrée(s)"
        L["Add New Font"]              = "Ajouter une police"
        L["ADDFONT_INSTR"]             = "Placez le fichier .ttf dans le dossier Fonts, saisissez le nom ci-dessous, puis cliquez sur Ajouter Police."
        L["Font Filename"]             = "Nom du fichier de police"
        L["Add Font"]                  = "Ajouter Police"
        L["file not found"]            = "fichier introuvable"
        L["No fonts registered yet.  Add one below."] = "Aucune police enregistrée. Ajoutez-en une ci-dessous."
        L["ERR_LSM"]                   = "LibSharedMedia-3.0 introuvable."
        L["ERR_EMPTY"]                 = "Saisissez un nom de fichier de police."
        L["ERR_EXT"] = [["%s" n'est pas un format valide pour WoW. Seuls les fichiers .ttf sont pris en charge.]]
        L["ERR_MISSING"] = [["%s" introuvable dans le dossier Fonts. Vérifiez le nom et réessayez.]]
        L["STATUS_ADDED"] = [["%s" ajoutée.]]
        L["STATUS_REMOVED"]            = "  %d police(s) manquante(s) supprimée(s)."
        L["HUD"]                       = "HUD"
        L["HUD_DESC"]                  = "Barre compacte en haut de l'écran avec statistiques en direct, horloge et boutons d'action rapide."
        L["Show HUD"]                  = "Afficher HUD"
        L["Show:"]                     = "Afficher :"
        L["Currency"]                  = "Monnaie"
        L["Friends"]                   = "Amis"
        L["Clock"]                     = "Horloge"
        L["Durability"]                = "Durabilité"
        L["Party"]                     = "Groupe"
        L["Screenshot"]                = "Capture d'écran"
        L["Text Style"]                = "Style de texte"
        L["TEXT_STYLE_DESC"]           = "Personnalisez la police, la taille et la couleur de chaque élément HUD."
        L["Friends count"]             = "Amis en ligne"
        L["Party count"]               = "Taille du groupe"
        L["Stats (FPS / MS / Dur)"]    = "Stats (FPS / MS / Dur)"
        L["Hero Dawncrest"]            = "Crête de l'Aube Héroïque"
        L["Myth Dawncrest"]            = "Crête de l'Aube Mythique"
        L["Nebulous Voidcore"]         = "Noyau du Vide Nébuleux"
        L["Hearthstone Button"]        = "Bouton Pierre de Foyer"
        L["HS_BUTTON_DESC"]            = "Assignez un sort ou un jouet à chaque bouton de la souris sur le bouton HUD Pierre de Foyer."
        L["Left-click:"]               = "Clic gauche :"
        L["Right-click:"]              = "Clic droit :"
        L["Middle-click:"]             = "Clic molette :"
        L["Clock Format"]              = "Format de l'horloge"
        L["Format:"]                   = "Format :"
        L["Source:"]                   = "Source :"
        L["Server"]                    = "Serveur"
        L["Local"]                     = "Local"
        L["Position & Layout"]         = "Position et disposition"
        L["Reset HUD Position"]        = "Réinitialiser la position HUD"
        L["Lock Position"]             = "Verrouiller la position"
        L["HUD Scale"]                 = "Échelle HUD"
        L["Font:"]                     = "Police :"
        L["Size:"]                     = "Taille :"
        L["Color:"]                    = "Couleur :"
        L["Nothing (disabled)"]        = "Rien (désactivé)"
        L["TT_GOLD"]                   = "Or"
        L["TT_FRIENDS"]                = "Amis en ligne : %s"
        L["TT_FRIENDS_INWOW"]          = "Dans WoW :"
        L["TT_FRIENDS_NONE"]           = "Aucun ami en ligne sur WoW"
        L["TT_RAID_SIZE"]              = "Taille du raid"
        L["TT_PARTY_SIZE"]             = "Taille du groupe"
        L["TT_SCREENSHOT"]             = "Capture d'écran (silencieuse)"
        L["TT_CLOCK"]                  = "Cliquer pour basculer 12h/24h"
        L["TT_HEARTHSTONE"]            = "Pierre de Foyer / Téléportation"
        L["TT_MINIMAP"]                = "Chargeur de Polices"
        L["TT_MINIMAP_CLICK"]          = "Clic gauche : Basculer l'UI"
        L["TT_MINIMAP_DRAG"]           = "Glisser : Repositionner"
        L["Left-click: "]              = "Clic gauche : "
        L["Right-click: "]             = "Clic droit : "
        L["Middle-click: "]            = "Clic molette : "

    -- ── German (deDE) ─────────────────────────────────────────────────────────
    elseif locale == "deDE" then
        L["Custom Font Loader"]        = "Schriftart-Lader"
        L["Author: Exxos"]             = "Autor: Exxos"
        L["Instructions"]              = "Anleitung"
        L["Font Manager"]              = "Schriftart-Manager"
        L["Utilities"]                 = "Hilfsmittel"
        L["How to add fonts to CustomFontLoader"] = "Schriftarten hinzufügen"
        L["Fonts registered with LibSharedMedia-3.0"] = "Mit LibSharedMedia-3.0 registrierte Schriftarten"
        L["HUD display options"]       = "HUD-Anzeigeoptionen"
        L["INSTR_TITLE"]               = "Neue Schriftart hinzufügen"
        L["INSTR_STEP1_HDR"]           = "Schritt 1 — Schriftartdatei ablegen"
        L["INSTR_STEP1_BODY"]          = "Kopiere deine .ttf-Dateien in den Fonts-Ordner des AddOns:"
        L["INSTR_STEP1_NOTE"]          = "Vermeide Leerzeichen in Dateinamen — verwende Bindestriche (z.B. Meine-Schrift-Fett.ttf)."
        L["INSTR_STEP2_HDR"]           = "Schritt 2 — Neu laden"
        L["INSTR_STEP2_BODY"]          = "WoW muss die Dateien laden, bevor sie registriert werden können. Tippe /reload oder benutze die Schaltfläche:"
        L["Reload UI"]                 = "UI neu laden"
        L["INSTR_STEP2_NOTE"]          = "Wenn du die Schaltfläche oben benutzt, öffnet sich dieses AddOn nach dem Neuladen automatisch wieder."
        L["INSTR_STEP3_HDR"]           = "Schritt 3 — Im Schriftart-Manager registrieren"
        L["INSTR_STEP3_BODY"]          = "Wechsle zum Schriftart-Manager, gib den Dateinamen ohne Erweiterung ein und klicke auf Schriftart hinzufügen."
        L["INSTR_STEP3_NOTE"]          = "Die Schriftart ist sofort in EllesmereUI, Plater, BigWigs und anderen SharedMedia-AddOns verfügbar."
        L["Notes"]                     = "Hinweise"
        L["NOTE1"]                     = "   -  Schriftarten werden automatisch gespeichert und bei jedem Login erneut registriert."
        L["NOTE2"]                     = "   -  Nur .ttf-Dateien werden unterstützt — .otf-Dateien funktionieren in WoW nicht."
        L["NOTE3"]                     = "   -  Wenn du eine Schriftartdatei löschst, erkennt das Spiel dies erst nach einem vollständigen Neustart."
        L["NOTE4"]                     = "   -  Nach einem vollständigen Neustart werden fehlende Schriftarten beim Login automatisch bereinigt."
        L["Registered Fonts"]          = "Registrierte Schriftarten"
        L["FONT_COUNT"]                = "%d Schriftart(en) registriert"
        L["Add New Font"]              = "Schriftart hinzufügen"
        L["ADDFONT_INSTR"]             = "Lege die .ttf-Datei in den Fonts-Ordner, gib den Dateinamen unten ein und klicke auf Schriftart hinzufügen."
        L["Font Filename"]             = "Dateiname der Schriftart"
        L["Add Font"]                  = "Schriftart hinzufügen"
        L["file not found"]            = "Datei nicht gefunden"
        L["No fonts registered yet.  Add one below."] = "Noch keine Schriftarten registriert. Füge eine unten hinzu."
        L["ERR_LSM"]                   = "LibSharedMedia-3.0 nicht gefunden."
        L["ERR_EMPTY"]                 = "Gib einen Dateinamen für die Schriftart ein."
        L["ERR_EXT"] = [["%s" ist kein gültiges Format für WoW. Nur .ttf-Dateien werden unterstützt.]]
        L["ERR_MISSING"] = [["%s" wurde im Fonts-Ordner nicht gefunden. Überprüfe den Dateinamen und versuche es erneut.]]
        L["STATUS_ADDED"] = [["%s" hinzugefügt.]]
        L["STATUS_REMOVED"]            = "  %d fehlende Schriftart(en) entfernt."
        L["HUD"]                       = "HUD"
        L["HUD_DESC"]                  = "Kompakte Leiste am oberen Bildschirmrand mit Live-Statistiken, Uhr und Schnellzugriff-Schaltflächen."
        L["Show HUD"]                  = "HUD anzeigen"
        L["Show:"]                     = "Anzeigen:"
        L["Currency"]                  = "Währung"
        L["Friends"]                   = "Freunde"
        L["Clock"]                     = "Uhr"
        L["Durability"]                = "Haltbarkeit"
        L["Party"]                     = "Gruppe"
        L["Screenshot"]                = "Screenshot"
        L["Text Style"]                = "Textstil"
        L["TEXT_STYLE_DESC"]           = "Schriftart, Größe und Farbe für jedes HUD-Element anpassen."
        L["Friends count"]             = "Online-Freunde"
        L["Party count"]               = "Gruppengröße"
        L["Stats (FPS / MS / Dur)"]    = "Stats (FPS / MS / Halt.)"
        L["Hero Dawncrest"]            = "Helden-Morgenkamm"
        L["Myth Dawncrest"]            = "Mythos-Morgenkamm"
        L["Nebulous Voidcore"]         = "Nebulöser Leerekern"
        L["Hearthstone Button"]        = "Hearthstone-Schaltfläche"
        L["HS_BUTTON_DESC"]            = "Weise jeder Maustaste des HUD-Hearthstone-Buttons einen Zauber oder ein Spielzeug zu."
        L["Left-click:"]               = "Linksklick:"
        L["Right-click:"]              = "Rechtsklick:"
        L["Middle-click:"]             = "Mittelklick:"
        L["Clock Format"]              = "Uhrzeitformat"
        L["Format:"]                   = "Format:"
        L["Source:"]                   = "Quelle:"
        L["Server"]                    = "Server"
        L["Local"]                     = "Lokal"
        L["Position & Layout"]         = "Position & Layout"
        L["Reset HUD Position"]        = "HUD-Position zurücksetzen"
        L["Lock Position"]             = "Position sperren"
        L["HUD Scale"]                 = "HUD-Skalierung"
        L["Font:"]                     = "Schriftart:"
        L["Size:"]                     = "Größe:"
        L["Color:"]                    = "Farbe:"
        L["Nothing (disabled)"]        = "Nichts (deaktiviert)"
        L["TT_GOLD"]                   = "Gold"
        L["TT_FRIENDS"]                = "Freunde online: %s"
        L["TT_FRIENDS_INWOW"]          = "In WoW:"
        L["TT_FRIENDS_NONE"]           = "Keine Freunde gerade in WoW online"
        L["TT_RAID_SIZE"]              = "Schlachtzuggröße"
        L["TT_PARTY_SIZE"]             = "Gruppengröße"
        L["TT_SCREENSHOT"]             = "Screenshot (lautlos)"
        L["TT_CLOCK"]                  = "Klicken zum Umschalten 12h/24h"
        L["TT_HEARTHSTONE"]            = "Hearthstone / Teleport"
        L["TT_MINIMAP"]                = "Schriftart-Lader"
        L["TT_MINIMAP_CLICK"]          = "Linksklick: UI umschalten"
        L["TT_MINIMAP_DRAG"]           = "Ziehen: Neu positionieren"
        L["Left-click: "]              = "Linksklick: "
        L["Right-click: "]             = "Rechtsklick: "
        L["Middle-click: "]            = "Mittelklick: "

    -- ── Spanish / Latin America (esES / esMX) ────────────────────────────────
    elseif locale == "esES" or locale == "esMX" then
        L["Custom Font Loader"]        = "Cargador de Fuentes"
        L["Author: Exxos"]             = "Autor: Exxos"
        L["Instructions"]              = "Instrucciones"
        L["Font Manager"]              = "Gestor de Fuentes"
        L["Utilities"]                 = "Utilidades"
        L["How to add fonts to CustomFontLoader"] = "Cómo añadir fuentes"
        L["Fonts registered with LibSharedMedia-3.0"] = "Fuentes registradas con LibSharedMedia-3.0"
        L["HUD display options"]       = "Opciones de visualización HUD"
        L["INSTR_TITLE"]               = "Cómo añadir una nueva fuente"
        L["INSTR_STEP1_HDR"]           = "Paso 1 — Coloca el archivo de fuente"
        L["INSTR_STEP1_BODY"]          = "Copia tus archivos .ttf en la carpeta Fonts del addon:"
        L["INSTR_STEP1_NOTE"]          = "Evita espacios en los nombres — usa guiones (ej. Mi-Fuente-Negrita.ttf)."
        L["INSTR_STEP2_HDR"]           = "Paso 2 — Recargar"
        L["INSTR_STEP2_BODY"]          = "WoW debe cargar los archivos antes de registrarlos. Escribe /reload o usa el botón:"
        L["Reload UI"]                 = "Recargar UI"
        L["INSTR_STEP2_NOTE"]          = "Si usas el botón, este addon se reabrirá automáticamente tras la recarga."
        L["INSTR_STEP3_HDR"]           = "Paso 3 — Registrar en el Gestor de Fuentes"
        L["INSTR_STEP3_BODY"]          = "Cambia a la pestaña Gestor de Fuentes, escribe el nombre sin extensión y haz clic en Añadir Fuente."
        L["INSTR_STEP3_NOTE"]          = "La fuente estará disponible inmediatamente en EllesmereUI, Plater, BigWigs y otros addons SharedMedia."
        L["Notes"]                     = "Notas"
        L["NOTE1"]                     = "   -  Las fuentes se guardan automáticamente y se vuelven a registrar en cada inicio de sesión."
        L["NOTE2"]                     = "   -  Solo se admiten archivos .ttf — los archivos .otf no cargan en WoW."
        L["NOTE3"]                     = "   -  Si eliminas un archivo de fuente, el juego no lo reconocerá hasta reiniciar completamente."
        L["NOTE4"]                     = "   -  Tras reiniciar, las fuentes faltantes se limpian automáticamente al iniciar sesión."
        L["Registered Fonts"]          = "Fuentes registradas"
        L["FONT_COUNT"]                = "%d fuente(s) registrada(s)"
        L["Add New Font"]              = "Añadir fuente"
        L["ADDFONT_INSTR"]             = "Coloca el archivo .ttf en la carpeta Fonts, escribe el nombre abajo y haz clic en Añadir Fuente."
        L["Font Filename"]             = "Nombre del archivo de fuente"
        L["Add Font"]                  = "Añadir Fuente"
        L["file not found"]            = "archivo no encontrado"
        L["No fonts registered yet.  Add one below."] = "Aún no hay fuentes registradas. Añade una abajo."
        L["ERR_LSM"]                   = "LibSharedMedia-3.0 no encontrado."
        L["ERR_EMPTY"]                 = "Introduce un nombre de archivo de fuente."
        L["ERR_EXT"] = [["%s" no es un formato válido para WoW. Solo se admiten archivos .ttf.]]
        L["ERR_MISSING"] = [["%s" no encontrado en la carpeta Fonts. Verifica el nombre e inténtalo de nuevo.]]
        L["STATUS_ADDED"] = [["%s" añadida.]]
        L["STATUS_REMOVED"]            = "  %d fuente(s) faltante(s) eliminada(s)."
        L["HUD"]                       = "HUD"
        L["HUD_DESC"]                  = "Barra compacta en la parte superior con estadísticas en vivo, reloj y botones de acción rápida."
        L["Show HUD"]                  = "Mostrar HUD"
        L["Show:"]                     = "Mostrar:"
        L["Currency"]                  = "Moneda"
        L["Friends"]                   = "Amigos"
        L["Clock"]                     = "Reloj"
        L["Durability"]                = "Durabilidad"
        L["Party"]                     = "Grupo"
        L["Screenshot"]                = "Captura"
        L["Text Style"]                = "Estilo de texto"
        L["TEXT_STYLE_DESC"]           = "Personaliza la fuente, el tamaño y el color de cada elemento del HUD."
        L["Friends count"]             = "Amigos en línea"
        L["Party count"]               = "Tamaño del grupo"
        L["Stats (FPS / MS / Dur)"]    = "Stats (FPS / MS / Dur)"
        L["Hero Dawncrest"]            = "Cresta del Alba Heroica"
        L["Myth Dawncrest"]            = "Cresta del Alba Mítica"
        L["Nebulous Voidcore"]         = "Núcleo del Vacío Nebuloso"
        L["Hearthstone Button"]        = "Botón Piedra de Hogar"
        L["HS_BUTTON_DESC"]            = "Asigna un hechizo o juguete a cada botón del ratón en el botón HUD Piedra de Hogar."
        L["Left-click:"]               = "Clic izquierdo:"
        L["Right-click:"]              = "Clic derecho:"
        L["Middle-click:"]             = "Clic central:"
        L["Clock Format"]              = "Formato de reloj"
        L["Format:"]                   = "Formato:"
        L["Source:"]                   = "Fuente:"
        L["Server"]                    = "Servidor"
        L["Local"]                     = "Local"
        L["Position & Layout"]         = "Posición y disposición"
        L["Reset HUD Position"]        = "Restablecer posición HUD"
        L["Lock Position"]             = "Bloquear posición"
        L["HUD Scale"]                 = "Escala HUD"
        L["Font:"]                     = "Fuente:"
        L["Size:"]                     = "Tamaño:"
        L["Color:"]                    = "Color:"
        L["Nothing (disabled)"]        = "Nada (desactivado)"
        L["TT_GOLD"]                   = "Oro"
        L["TT_FRIENDS"]                = "Amigos en línea: %s"
        L["TT_FRIENDS_INWOW"]          = "En WoW:"
        L["TT_FRIENDS_NONE"]           = "No hay amigos en WoW ahora mismo"
        L["TT_RAID_SIZE"]              = "Tamaño de banda"
        L["TT_PARTY_SIZE"]             = "Tamaño del grupo"
        L["TT_SCREENSHOT"]             = "Captura (silenciosa)"
        L["TT_CLOCK"]                  = "Clic para alternar 12h/24h"
        L["TT_HEARTHSTONE"]            = "Piedra de Hogar / Teletransporte"
        L["TT_MINIMAP"]                = "Cargador de Fuentes"
        L["TT_MINIMAP_CLICK"]          = "Clic izquierdo: Alternar UI"
        L["TT_MINIMAP_DRAG"]           = "Arrastrar: Reposicionar"
        L["Left-click: "]              = "Clic izq.: "
        L["Right-click: "]             = "Clic der.: "
        L["Middle-click: "]            = "Clic cen.: "

    -- ── Portuguese Brazil (ptBR) ──────────────────────────────────────────────
    elseif locale == "ptBR" then
        L["Custom Font Loader"]        = "Carregador de Fontes"
        L["Author: Exxos"]             = "Autor: Exxos"
        L["Instructions"]              = "Instruções"
        L["Font Manager"]              = "Gerenciador de Fontes"
        L["Utilities"]                 = "Utilitários"
        L["How to add fonts to CustomFontLoader"] = "Como adicionar fontes"
        L["Fonts registered with LibSharedMedia-3.0"] = "Fontes registradas com LibSharedMedia-3.0"
        L["HUD display options"]       = "Opções de exibição HUD"
        L["INSTR_TITLE"]               = "Como adicionar uma nova fonte"
        L["INSTR_STEP1_HDR"]           = "Passo 1 — Coloque o arquivo de fonte"
        L["INSTR_STEP1_BODY"]          = "Copie seus arquivos .ttf para a pasta Fonts do addon:"
        L["INSTR_STEP1_NOTE"]          = "Evite espaços nos nomes — use hifens (ex. Minha-Fonte-Negrito.ttf)."
        L["INSTR_STEP2_HDR"]           = "Passo 2 — Recarregar"
        L["INSTR_STEP2_BODY"]          = "O WoW precisa carregar os arquivos antes de registrá-los. Digite /reload ou use o botão abaixo:"
        L["Reload UI"]                 = "Recarregar UI"
        L["INSTR_STEP2_NOTE"]          = "Se usar o botão acima, este addon reabrirá automaticamente após o recarregamento."
        L["INSTR_STEP3_HDR"]           = "Passo 3 — Registrar no Gerenciador de Fontes"
        L["INSTR_STEP3_BODY"]          = "Mude para a aba Gerenciador de Fontes, digite o nome do arquivo sem extensão e clique em Adicionar Fonte."
        L["INSTR_STEP3_NOTE"]          = "A fonte ficará disponível imediatamente no EllesmereUI, Plater, BigWigs e outros addons SharedMedia."
        L["Notes"]                     = "Notas"
        L["NOTE1"]                     = "   -  As fontes são salvas automaticamente e re-registradas a cada login."
        L["NOTE2"]                     = "   -  Apenas arquivos .ttf são suportados — arquivos .otf não carregam no WoW."
        L["NOTE3"]                     = "   -  Se você deletar um arquivo de fonte, o jogo não reconhecerá a remoção até uma reinicialização completa."
        L["NOTE4"]                     = "   -  Após reiniciar completamente, fontes faltantes são limpas automaticamente no login."
        L["Registered Fonts"]          = "Fontes registradas"
        L["FONT_COUNT"]                = "%d fonte(s) registrada(s)"
        L["Add New Font"]              = "Adicionar fonte"
        L["ADDFONT_INSTR"]             = "Coloque o arquivo .ttf na pasta Fonts, digite o nome abaixo e clique em Adicionar Fonte."
        L["Font Filename"]             = "Nome do arquivo de fonte"
        L["Add Font"]                  = "Adicionar Fonte"
        L["file not found"]            = "arquivo não encontrado"
        L["No fonts registered yet.  Add one below."] = "Nenhuma fonte registrada ainda. Adicione uma abaixo."
        L["ERR_LSM"]                   = "LibSharedMedia-3.0 não encontrado."
        L["ERR_EMPTY"]                 = "Insira um nome de arquivo de fonte."
        L["ERR_EXT"] = [["%s" não é um formato válido para WoW. Apenas arquivos .ttf são suportados.]]
        L["ERR_MISSING"] = [["%s" não encontrado na pasta Fonts. Verifique o nome e tente novamente.]]
        L["STATUS_ADDED"] = [["%s" adicionada.]]
        L["STATUS_REMOVED"]            = "  %d fonte(s) faltante(s) removida(s)."
        L["HUD"]                       = "HUD"
        L["HUD_DESC"]                  = "Barra compacta no topo da tela com estatísticas ao vivo, relógio e botões de ação rápida."
        L["Show HUD"]                  = "Mostrar HUD"
        L["Show:"]                     = "Mostrar:"
        L["Currency"]                  = "Moeda"
        L["Friends"]                   = "Amigos"
        L["Clock"]                     = "Relógio"
        L["Durability"]                = "Durabilidade"
        L["Party"]                     = "Grupo"
        L["Screenshot"]                = "Captura de tela"
        L["Text Style"]                = "Estilo de texto"
        L["TEXT_STYLE_DESC"]           = "Personalize fonte, tamanho e cor de cada elemento do HUD."
        L["Friends count"]             = "Amigos online"
        L["Party count"]               = "Tamanho do grupo"
        L["Stats (FPS / MS / Dur)"]    = "Stats (FPS / MS / Dur)"
        L["Hero Dawncrest"]            = "Crista da Aurora Heroica"
        L["Myth Dawncrest"]            = "Crista da Aurora Mítica"
        L["Nebulous Voidcore"]         = "Núcleo do Vazio Nebuloso"
        L["Hearthstone Button"]        = "Botão Pedra do Lar"
        L["HS_BUTTON_DESC"]            = "Atribua um feitiço ou brinquedo a cada botão do mouse no botão HUD Pedra do Lar."
        L["Left-click:"]               = "Clique esquerdo:"
        L["Right-click:"]              = "Clique direito:"
        L["Middle-click:"]             = "Clique do meio:"
        L["Clock Format"]              = "Formato do relógio"
        L["Format:"]                   = "Formato:"
        L["Source:"]                   = "Fonte:"
        L["Server"]                    = "Servidor"
        L["Local"]                     = "Local"
        L["Position & Layout"]         = "Posição e layout"
        L["Reset HUD Position"]        = "Redefinir posição HUD"
        L["Lock Position"]             = "Bloquear posição"
        L["HUD Scale"]                 = "Escala HUD"
        L["Font:"]                     = "Fonte:"
        L["Size:"]                     = "Tamanho:"
        L["Color:"]                    = "Cor:"
        L["Nothing (disabled)"]        = "Nada (desativado)"
        L["TT_GOLD"]                   = "Ouro"
        L["TT_FRIENDS"]                = "Amigos online: %s"
        L["TT_FRIENDS_INWOW"]          = "No WoW:"
        L["TT_FRIENDS_NONE"]           = "Nenhum amigo no WoW agora"
        L["TT_RAID"]                   = "Tamanho da raid"
        L["TT_PARTY_SIZE"]             = "Tamanho do grupo"
        L["TT_SCREENSHOT"]             = "Captura de tela (silenciosa)"
        L["TT_CLOCK"]                  = "Clique para alternar 12h/24h"
        L["TT_HEARTHSTONE"]            = "Pedra do Lar / Teleporte"
        L["TT_MINIMAP"]                = "Carregador de Fontes"
        L["TT_MINIMAP_CLICK"]          = "Clique esquerdo: Alternar UI"
        L["TT_MINIMAP_DRAG"]           = "Arrastar: Reposicionar"
        L["Left-click: "]              = "Clic esq.: "
        L["Right-click: "]             = "Clic dir.: "
        L["Middle-click: "]            = "Clic meio: "

    -- ── Russian (ruRU) ────────────────────────────────────────────────────────
    elseif locale == "ruRU" then
        L["Custom Font Loader"]        = "Загрузчик шрифтов"
        L["Author: Exxos"]             = "Автор: Exxos"
        L["Instructions"]              = "Инструкции"
        L["Font Manager"]              = "Менеджер шрифтов"
        L["Utilities"]                 = "Утилиты"
        L["How to add fonts to CustomFontLoader"] = "Как добавить шрифты"
        L["Fonts registered with LibSharedMedia-3.0"] = "Шрифты, зарегистрированные в LibSharedMedia-3.0"
        L["HUD display options"]       = "Настройки отображения HUD"
        L["INSTR_TITLE"]               = "Как добавить новый шрифт"
        L["INSTR_STEP1_HDR"]           = "Шаг 1 — Поместите файл шрифта"
        L["INSTR_STEP1_BODY"]          = "Скопируйте файлы .ttf в папку Fonts аддона:"
        L["INSTR_STEP1_NOTE"]          = "Избегайте пробелов в именах файлов — используйте дефисы (например, Мой-Шрифт-Жирный.ttf)."
        L["INSTR_STEP2_HDR"]           = "Шаг 2 — Перезагрузка"
        L["INSTR_STEP2_BODY"]          = "WoW должен загрузить файлы перед регистрацией. Введите /reload или используйте кнопку:"
        L["Reload UI"]                 = "Перезагрузить UI"
        L["INSTR_STEP2_NOTE"]          = "Если вы используете кнопку выше, аддон автоматически откроется после перезагрузки."
        L["INSTR_STEP3_HDR"]           = "Шаг 3 — Зарегистрировать в менеджере шрифтов"
        L["INSTR_STEP3_BODY"]          = "Перейдите на вкладку Менеджер шрифтов, введите имя файла без расширения и нажмите Добавить шрифт."
        L["INSTR_STEP3_NOTE"]          = "Шрифт сразу доступен в EllesmereUI, Plater, BigWigs и других аддонах SharedMedia."
        L["Notes"]                     = "Примечания"
        L["NOTE1"]                     = "   -  Шрифты сохраняются автоматически и перерегистрируются при каждом входе."
        L["NOTE2"]                     = "   -  Поддерживаются только файлы .ttf — файлы .otf не загружаются в WoW."
        L["NOTE3"]                     = "   -  Если вы удалите файл шрифта, игра не распознает это до полного перезапуска."
        L["NOTE4"]                     = "   -  После полного перезапуска отсутствующие шрифты автоматически очищаются при входе."
        L["Registered Fonts"]          = "Зарегистрированные шрифты"
        L["FONT_COUNT"]                = "Зарегистрировано %d шрифт(ов)"
        L["Add New Font"]              = "Добавить шрифт"
        L["ADDFONT_INSTR"]             = "Поместите файл .ttf в папку Fonts, введите имя ниже и нажмите Добавить шрифт."
        L["Font Filename"]             = "Имя файла шрифта"
        L["Add Font"]                  = "Добавить шрифт"
        L["file not found"]            = "файл не найден"
        L["No fonts registered yet.  Add one below."] = "Шрифты ещё не зарегистрированы. Добавьте ниже."
        L["ERR_LSM"]                   = "LibSharedMedia-3.0 не найден."
        L["ERR_EMPTY"]                 = "Введите имя файла шрифта."
        L["ERR_EXT"] = [["%s" — недопустимый формат для WoW. Поддерживаются только файлы .ttf.]]
        L["ERR_MISSING"] = [["%s" не найден в папке Fonts. Проверьте имя файла и попробуйте снова.]]
        L["STATUS_ADDED"] = [["%s" добавлен.]]
        L["STATUS_REMOVED"]            = "  Удалено %d отсутствующих шрифт(ов)."
        L["HUD"]                       = "HUD"
        L["HUD_DESC"]                  = "Компактная полоса вверху экрана с живыми статистиками, часами и кнопками быстрых действий."
        L["Show HUD"]                  = "Показать HUD"
        L["Show:"]                     = "Показать:"
        L["Currency"]                  = "Валюта"
        L["Friends"]                   = "Друзья"
        L["Clock"]                     = "Часы"
        L["Durability"]                = "Прочность"
        L["Party"]                     = "Группа"
        L["Screenshot"]                = "Скриншот"
        L["Text Style"]                = "Стиль текста"
        L["TEXT_STYLE_DESC"]           = "Настройте шрифт, размер и цвет для каждого элемента HUD."
        L["Friends count"]             = "Друзья онлайн"
        L["Party count"]               = "Размер группы"
        L["Stats (FPS / MS / Dur)"]    = "Статы (FPS / МС / Прочн.)"
        L["Hero Dawncrest"]            = "Герой Рассветный Герб"
        L["Myth Dawncrest"]            = "Мифический Рассветный Герб"
        L["Nebulous Voidcore"]         = "Туманное Ядро Пустоты"
        L["Hearthstone Button"]        = "Кнопка Камня Возврата"
        L["HS_BUTTON_DESC"]            = "Назначьте заклинание или игрушку каждой кнопке мыши на кнопке HUD Камня Возврата."
        L["Left-click:"]               = "Левая кнопка:"
        L["Right-click:"]              = "Правая кнопка:"
        L["Middle-click:"]             = "Средняя кнопка:"
        L["Clock Format"]              = "Формат часов"
        L["Format:"]                   = "Формат:"
        L["Source:"]                   = "Источник:"
        L["Server"]                    = "Сервер"
        L["Local"]                     = "Местное"
        L["Position & Layout"]         = "Положение и макет"
        L["Reset HUD Position"]        = "Сбросить положение HUD"
        L["Lock Position"]             = "Заблокировать положение"
        L["HUD Scale"]                 = "Масштаб HUD"
        L["Font:"]                     = "Шрифт:"
        L["Size:"]                     = "Размер:"
        L["Color:"]                    = "Цвет:"
        L["Nothing (disabled)"]        = "Ничего (отключено)"
        L["TT_GOLD"]                   = "Золото"
        L["TT_FRIENDS"]                = "Друзья онлайн: %s"
        L["TT_FRIENDS_INWOW"]          = "В WoW:"
        L["TT_FRIENDS_NONE"]           = "Нет друзей в WoW прямо сейчас"
        L["TT_RAID_SIZE"]              = "Размер рейда"
        L["TT_PARTY_SIZE"]             = "Размер группы"
        L["TT_SCREENSHOT"]             = "Скриншот (тихий)"
        L["TT_CLOCK"]                  = "Нажмите для переключения 12ч/24ч"
        L["TT_HEARTHSTONE"]            = "Камень Возврата / Телепорт"
        L["TT_MINIMAP"]                = "Загрузчик шрифтов"
        L["TT_MINIMAP_CLICK"]          = "ЛКМ: Переключить UI"
        L["TT_MINIMAP_DRAG"]           = "Перетащить: Переместить"
        L["Left-click: "]              = "ЛКМ: "
        L["Right-click: "]             = "ПКМ: "
        L["Middle-click: "]            = "СКМ: "

    -- ── Italian (itIT) ────────────────────────────────────────────────────────
    elseif locale == "itIT" then
        L["Custom Font Loader"]        = "Caricatore di Font"
        L["Author: Exxos"]             = "Autore: Exxos"
        L["Instructions"]              = "Istruzioni"
        L["Font Manager"]              = "Gestore Font"
        L["Utilities"]                 = "Utilità"
        L["How to add fonts to CustomFontLoader"] = "Come aggiungere font"
        L["Fonts registered with LibSharedMedia-3.0"] = "Font registrati con LibSharedMedia-3.0"
        L["HUD display options"]       = "Opzioni visualizzazione HUD"
        L["INSTR_TITLE"]               = "Come aggiungere un nuovo font"
        L["INSTR_STEP1_HDR"]           = "Passo 1 — Inserisci il file del font"
        L["INSTR_STEP1_BODY"]          = "Copia i tuoi file .ttf nella cartella Fonts dell'addon:"
        L["INSTR_STEP1_NOTE"]          = "Evita spazi nei nomi — usa trattini (es. Mio-Font-Grassetto.ttf)."
        L["INSTR_STEP2_HDR"]           = "Passo 2 — Ricarica"
        L["INSTR_STEP2_BODY"]          = "WoW deve caricare i file prima di registrarli. Digita /reload o usa il pulsante:"
        L["Reload UI"]                 = "Ricarica UI"
        L["INSTR_STEP2_NOTE"]          = "Se usi il pulsante sopra, questo addon si riaprirà automaticamente dopo il ricaricamento."
        L["INSTR_STEP3_HDR"]           = "Passo 3 — Registra nel Gestore Font"
        L["INSTR_STEP3_BODY"]          = "Passa alla scheda Gestore Font, inserisci il nome senza estensione e clicca Aggiungi Font."
        L["INSTR_STEP3_NOTE"]          = "Il font sarà immediatamente disponibile in EllesmereUI, Plater, BigWigs e altri addon SharedMedia."
        L["Notes"]                     = "Note"
        L["NOTE1"]                     = "   -  I font vengono salvati automaticamente e ri-registrati ad ogni accesso."
        L["NOTE2"]                     = "   -  Sono supportati solo i file .ttf — i file .otf non si caricano in WoW."
        L["NOTE3"]                     = "   -  Se elimini un file font, il gioco non lo riconoscerà fino al riavvio completo."
        L["NOTE4"]                     = "   -  Dopo il riavvio completo, i font mancanti vengono rimossi automaticamente all'accesso."
        L["Registered Fonts"]          = "Font registrati"
        L["FONT_COUNT"]                = "%d font registrati"
        L["Add New Font"]              = "Aggiungi font"
        L["ADDFONT_INSTR"]             = "Metti il file .ttf nella cartella Fonts, inserisci il nome qui sotto e clicca Aggiungi Font."
        L["Font Filename"]             = "Nome file font"
        L["Add Font"]                  = "Aggiungi Font"
        L["file not found"]            = "file non trovato"
        L["No fonts registered yet.  Add one below."] = "Nessun font registrato. Aggiungine uno qui sotto."
        L["ERR_LSM"]                   = "LibSharedMedia-3.0 non trovato."
        L["ERR_EMPTY"]                 = "Inserisci un nome file per il font."
        L["ERR_EXT"] = [["%s" non è un formato valido per WoW. Sono supportati solo file .ttf.]]
        L["ERR_MISSING"] = [["%s" non trovato nella cartella Fonts. Verifica il nome e riprova.]]
        L["STATUS_ADDED"] = [["%s" aggiunto.]]
        L["STATUS_REMOVED"]            = "  %d font mancanti rimossi."
        L["HUD"]                       = "HUD"
        L["HUD_DESC"]                  = "Barra compatta in cima allo schermo con statistiche in tempo reale, orologio e pulsanti di azione rapida."
        L["Show HUD"]                  = "Mostra HUD"
        L["Show:"]                     = "Mostra:"
        L["Currency"]                  = "Valuta"
        L["Friends"]                   = "Amici"
        L["Clock"]                     = "Orologio"
        L["Durability"]                = "Durabilità"
        L["Party"]                     = "Gruppo"
        L["Screenshot"]                = "Screenshot"
        L["Text Style"]                = "Stile testo"
        L["TEXT_STYLE_DESC"]           = "Personalizza font, dimensione e colore per ogni elemento HUD."
        L["Friends count"]             = "Amici online"
        L["Party count"]               = "Dimensione gruppo"
        L["Stats (FPS / MS / Dur)"]    = "Stats (FPS / MS / Dur)"
        L["Hero Dawncrest"]            = "Cresta dell'Alba Eroica"
        L["Myth Dawncrest"]            = "Cresta dell'Alba Mitica"
        L["Nebulous Voidcore"]         = "Nucleo del Vuoto Nebuloso"
        L["Hearthstone Button"]        = "Pulsante Pietra del Focolare"
        L["HS_BUTTON_DESC"]            = "Assegna un incantesimo o giocattolo a ogni pulsante del mouse sul pulsante HUD Pietra del Focolare."
        L["Left-click:"]               = "Clic sinistro:"
        L["Right-click:"]              = "Clic destro:"
        L["Middle-click:"]             = "Clic centrale:"
        L["Clock Format"]              = "Formato orologio"
        L["Format:"]                   = "Formato:"
        L["Source:"]                   = "Sorgente:"
        L["Server"]                    = "Server"
        L["Local"]                     = "Locale"
        L["Position & Layout"]         = "Posizione e layout"
        L["Reset HUD Position"]        = "Ripristina posizione HUD"
        L["Lock Position"]             = "Blocca posizione"
        L["HUD Scale"]                 = "Scala HUD"
        L["Font:"]                     = "Font:"
        L["Size:"]                     = "Dimensione:"
        L["Color:"]                    = "Colore:"
        L["Nothing (disabled)"]        = "Niente (disabilitato)"
        L["TT_GOLD"]                   = "Oro"
        L["TT_FRIENDS"]                = "Amici online: %s"
        L["TT_FRIENDS_INWOW"]          = "In WoW:"
        L["TT_FRIENDS_NONE"]           = "Nessun amico in WoW al momento"
        L["TT_RAID_SIZE"]              = "Dimensione raid"
        L["TT_PARTY_SIZE"]             = "Dimensione gruppo"
        L["TT_SCREENSHOT"]             = "Screenshot (silenzioso)"
        L["TT_CLOCK"]                  = "Clicca per alternare 12h/24h"
        L["TT_HEARTHSTONE"]            = "Pietra del Focolare / Teletrasporto"
        L["TT_MINIMAP"]                = "Caricatore di Font"
        L["TT_MINIMAP_CLICK"]          = "Clic sinistro: Attiva/Disattiva UI"
        L["TT_MINIMAP_DRAG"]           = "Trascina: Riposiziona"
        L["Left-click: "]              = "Clic sin.: "
        L["Right-click: "]             = "Clic des.: "
        L["Middle-click: "]            = "Clic cen.: "
    end
end


-- ============================================================
--  Colours
-- ============================================================
local C = {
    bg         = {0.05, 0.04, 0.07, 0.98},
    sidebar    = {0.03, 0.02, 0.05, 1.00},
    accent     = {0.65, 0.30, 1.00, 1.00},
    accentDim  = {0.40, 0.15, 0.65, 1.00},
    header     = {0.95, 0.92, 1.00, 1.00},
    sub        = {0.48, 0.42, 0.58, 1.00},
    text       = {0.82, 0.78, 0.90, 1.00},
    inputBg    = {0.08, 0.06, 0.12, 1.00},
    border     = {0.20, 0.14, 0.28, 1.00},
    row1       = {0.10, 0.07, 0.15, 0.70},
}

local FRAME_W   = 1040
local FRAME_H   = 680
local SIDEBAR_W = 240
local PAD       = 20

-- ============================================================
--  Helpers
-- ============================================================
local function Backdrop(f, bg, border)
    f:SetBackdrop({
        bgFile   = "Interface\\Buttons\\WHITE8X8",
        edgeFile = "Interface\\Buttons\\WHITE8X8",
        edgeSize = 1,
    })
    f:SetBackdropColor(bg[1], bg[2], bg[3], bg[4] or 1)
    if border then
        f:SetBackdropBorderColor(border[1], border[2], border[3], border[4] or 1)
    end
end

local function Tex(parent, r, g, b, a)
    local t = parent:CreateTexture(nil, "ARTWORK")
    t:SetColorTexture(r, g, b, a)
    return t
end

local function Label(parent, fontObj, text, r, g, b, a)
    local fs = parent:CreateFontString(nil, "OVERLAY", fontObj)
    fs:SetText(text or "")
    if r then fs:SetTextColor(r, g, b, a or 1) end
    fs:SetJustifyH("LEFT")
    return fs
end

-- ============================================================
--  Main window
-- ============================================================
-- Forward declarations for the only names from this section that anything
-- past it needs to see. Everything else below is wrapped in a do-block —
-- purely to stay under Lua 5.1's 200-top-level-local ceiling, since this
-- whole section is otherwise self-contained (tabs, panes, font manager UI).
local Main
local ContentFrame
local HS_DEFAULT_LEFT, HS_DEFAULT_RIGHT, HS_DEFAULT_MIDDLE
local UtilFrame

do
Main = CreateFrame("Frame", "CFLMainFrame", UIParent, "BackdropTemplate")
Main:SetSize(FRAME_W, FRAME_H)
Main:SetPoint("CENTER")
Main:SetFrameStrata("DIALOG")
Main:SetMovable(true)
Main:EnableMouse(true)
Main:RegisterForDrag("LeftButton")
Main:SetScript("OnDragStart", Main.StartMoving)
Main:SetScript("OnDragStop",  Main.StopMovingOrSizing)

table.insert(UISpecialFrames, "CFLMainFrame")
Main:Hide()
Main:SetBackdrop({
    bgFile   = "Interface\\Buttons\\WHITE8X8",
    edgeFile = "Interface\\Buttons\\WHITE8X8",
    edgeSize = 1,
})
Main:SetBackdropColor(C.bg[1], C.bg[2], C.bg[3], C.bg[4] or 1)
Main:SetBackdropBorderColor(0, 0, 0, 0)

local function AddBorderLine(side)
    local t = Main:CreateTexture(nil, "OVERLAY", nil, 7)
    t:SetColorTexture(C.accent[1], C.accent[2], C.accent[3], 0.9)
    if side == "top" then
        t:SetHeight(2); t:SetPoint("TOPLEFT", Main, "TOPLEFT", 0, 0); t:SetPoint("TOPRIGHT", Main, "TOPRIGHT", 0, 0)
    elseif side == "bottom" then
        t:SetHeight(2); t:SetPoint("BOTTOMLEFT", Main, "BOTTOMLEFT", 0, 0); t:SetPoint("BOTTOMRIGHT", Main, "BOTTOMRIGHT", 0, 0)
    elseif side == "left" then
        t:SetWidth(2); t:SetPoint("TOPLEFT", Main, "TOPLEFT", 0, 0); t:SetPoint("BOTTOMLEFT", Main, "BOTTOMLEFT", 0, 0)
    elseif side == "right" then
        t:SetWidth(2); t:SetPoint("TOPRIGHT", Main, "TOPRIGHT", 0, 0); t:SetPoint("BOTTOMRIGHT", Main, "BOTTOMRIGHT", 0, 0)
    end
end
AddBorderLine("top")
AddBorderLine("bottom")
AddBorderLine("left")
AddBorderLine("right")

local CloseBtn = CreateFrame("Button", nil, Main, "BackdropTemplate")
CloseBtn:SetSize(32, 32)
CloseBtn:SetPoint("TOPRIGHT", Main, "TOPRIGHT", -8, -8)
CloseBtn:SetFrameLevel(Main:GetFrameLevel() + 10)
Backdrop(CloseBtn, {0.15, 0.08, 0.04, 1}, C.border)
local closeFS = CloseBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
closeFS:SetText("x")
closeFS:SetTextColor(C.sub[1], C.sub[2], C.sub[3])
closeFS:SetAllPoints(CloseBtn)
closeFS:SetJustifyH("CENTER")
CloseBtn:SetScript("OnEnter", function() closeFS:SetTextColor(1, 0.30, 0.20) end)
CloseBtn:SetScript("OnLeave", function() closeFS:SetTextColor(C.sub[1], C.sub[2], C.sub[3]) end)
CloseBtn:SetScript("OnClick", function() Main:Hide() end)

local Sidebar = CreateFrame("Frame", nil, Main, "BackdropTemplate")
Sidebar:SetWidth(SIDEBAR_W)
Sidebar:SetPoint("TOPLEFT",    Main, "TOPLEFT",    2, -2)
Sidebar:SetPoint("BOTTOMLEFT", Main, "BOTTOMLEFT", 2,  2)
Backdrop(Sidebar, C.sidebar, C.border)

local sideDiv = Tex(Sidebar, C.accent[1], C.accent[2], C.accent[3], 0.35)
sideDiv:SetWidth(1)
sideDiv:SetPoint("TOPRIGHT",    Sidebar, "TOPRIGHT",    0,  0)
sideDiv:SetPoint("BOTTOMRIGHT", Sidebar, "BOTTOMRIGHT", 0,  0)

local LOGO_H = 80
local logoGlow = Tex(Sidebar, C.accent[1], C.accent[2], C.accent[3], 0.08)
logoGlow:SetPoint("TOPLEFT",  Sidebar, "TOPLEFT",  0, 0)
logoGlow:SetPoint("TOPRIGHT", Sidebar, "TOPRIGHT", 0, 0)
logoGlow:SetHeight(LOGO_H)

local logoTitle = Label(Sidebar, "GameFontNormalHuge", L["Custom Font Loader"], C.accent[1], C.accent[2], C.accent[3])
logoTitle:SetPoint("TOPLEFT", Sidebar, "TOPLEFT", PAD, -14)
logoTitle:SetWidth(SIDEBAR_W - PAD * 2)
logoTitle:SetWordWrap(true)

local logoAuth = Label(Sidebar, "GameFontNormal", L["Author: Exxos"], C.sub[1], C.sub[2], C.sub[3])
logoAuth:SetPoint("TOPLEFT", logoTitle, "BOTTOMLEFT", 0, -4)

local logoDivLine = Tex(Sidebar, C.accent[1], C.accent[2], C.accent[3], 0.25)
logoDivLine:SetHeight(1)
logoDivLine:SetPoint("TOPLEFT",  Sidebar, "TOPLEFT",  0, -LOGO_H)
logoDivLine:SetPoint("TOPRIGHT", Sidebar, "TOPRIGHT", 0, -LOGO_H)

local verLabel = Sidebar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
verLabel:SetPoint("BOTTOMLEFT", Sidebar, "BOTTOMLEFT", 8, 6)
verLabel:SetTextColor(C.sub[1], C.sub[2], C.sub[3])
verLabel:SetJustifyH("LEFT")
verLabel:SetText("v" .. (C_AddOns.GetAddOnMetadata(addonName, "Version") or "?"))

local NAV_Y   = LOGO_H + 40
local NAV_H   = 42
local activePane = nil

local function MakeNavBtn(label, yOffset)
    local btn = CreateFrame("Button", nil, Sidebar, "BackdropTemplate")
    btn:SetHeight(NAV_H)
    btn:SetPoint("TOPLEFT",  Sidebar, "TOPLEFT",  0, -(NAV_Y + yOffset))
    btn:SetPoint("TOPRIGHT", Sidebar, "TOPRIGHT", 0, -(NAV_Y + yOffset))

    local selBG = Tex(btn, C.accent[1], C.accent[2], C.accent[3], 0.00)
    selBG:SetAllPoints(btn)
    btn._selBG = selBG

    local selBar = Tex(btn, C.accent[1], C.accent[2], C.accent[3], 0.00)
    selBar:SetWidth(3)
    selBar:SetPoint("TOPLEFT",    btn, "TOPLEFT",    0, 0)
    selBar:SetPoint("BOTTOMLEFT", btn, "BOTTOMLEFT", 0, 0)
    btn._selBar = selBar

    local hl = Tex(btn, 1, 1, 1, 0.00)
    hl:SetAllPoints(btn)
    btn:SetScript("OnEnter", function(self)
        if activePane ~= self then hl:SetColorTexture(1, 1, 1, 0.05) end
    end)
    btn:SetScript("OnLeave", function() hl:SetColorTexture(1, 1, 1, 0.00) end)

    local lbl = Label(btn, "GameFontNormalLarge", label, C.sub[1], C.sub[2], C.sub[3])
    lbl:SetPoint("LEFT", btn, "LEFT", PAD + 10, 0)
    btn._lbl = lbl

    return btn
end

local NavInstructions = MakeNavBtn(L["Instructions"], 0)
local NavFonts        = MakeNavBtn(L["Font Manager"],  NAV_H)
local NavUtilities    = MakeNavBtn(L["Utilities"],     NAV_H * 2)

local Pane = CreateFrame("Frame", nil, Main)
Pane:SetPoint("TOPLEFT",     Main, "TOPLEFT",     SIDEBAR_W + 1, -2)
Pane:SetPoint("BOTTOMRIGHT", Main, "BOTTOMRIGHT", -2,              2)

local HEADER_H = 66
local PaneHdr = CreateFrame("Frame", nil, Pane, "BackdropTemplate")
PaneHdr:SetHeight(HEADER_H)
PaneHdr:SetPoint("TOPLEFT",  Pane, "TOPLEFT",  0, 0)
PaneHdr:SetPoint("TOPRIGHT", Pane, "TOPRIGHT", 0, 0)
Backdrop(PaneHdr, {0.07, 0.05, 0.03, 1}, C.border)

local paneTitle = Label(PaneHdr, "GameFontNormalHuge", "", C.header[1], C.header[2], C.header[3])
paneTitle:SetPoint("BOTTOMLEFT", PaneHdr, "BOTTOMLEFT", PAD, 22)

local paneSub = Label(PaneHdr, "GameFontNormal", "", C.sub[1], C.sub[2], C.sub[3])
paneSub:SetPoint("BOTTOMLEFT", PaneHdr, "BOTTOMLEFT", PAD, 7)

local hdrDiv = Tex(Pane, C.border[1], C.border[2], C.border[3], 1)
hdrDiv:SetHeight(1)
hdrDiv:SetPoint("TOPLEFT",  Pane, "TOPLEFT",  0, -HEADER_H)
hdrDiv:SetPoint("TOPRIGHT", Pane, "TOPRIGHT", 0, -HEADER_H)

local CONTENT_TOP = HEADER_H + 1

function ContentFrame()
    local f = CreateFrame("Frame", nil, Pane)
    f:SetPoint("TOPLEFT",     Pane, "TOPLEFT",     0, -CONTENT_TOP)
    f:SetPoint("BOTTOMRIGHT", Pane, "BOTTOMRIGHT", 0,  1)
    f:Hide()
    return f
end

local InstrFrame = ContentFrame()
local PANE_TXT_W = FRAME_W - SIDEBAR_W - PAD * 2

local function ILine(text, yOff, font, r, g, b)
    font = font or "GameFontNormal"
    r, g, b = r or C.text[1], g or C.text[2], b or C.text[3]
    local fs = InstrFrame:CreateFontString(nil, "OVERLAY", font)
    fs:SetText(text)
    fs:SetTextColor(r, g, b)
    fs:SetJustifyH("LEFT")
    fs:SetWidth(PANE_TXT_W)
    fs:SetPoint("TOPLEFT", InstrFrame, "TOPLEFT", PAD, -yOff)
    return fs
end

local function ISep(yOff, alpha)
    local t = Tex(InstrFrame, C.border[1], C.border[2], C.border[3], alpha or 0.8)
    t:SetHeight(1)
    t:SetPoint("TOPLEFT",  InstrFrame, "TOPLEFT",  PAD,    -yOff)
    t:SetPoint("TOPRIGHT", InstrFrame, "TOPRIGHT", -PAD,   -yOff)
    return t
end

ILine(L["INSTR_TITLE"], 18, "GameFontNormalLarge", C.accent[1], C.accent[2], C.accent[3])
ISep(46, 0.3)

ILine(L["INSTR_STEP1_HDR"], 58, "GameFontNormalLarge", C.header[1], C.header[2], C.header[3])
ILine(L["INSTR_STEP1_BODY"], 84)
ILine("    World of Warcraft\\_retail_\\Interface\\Fonts\\", 106,
    "GameFontNormal", C.accent[1], C.accent[2] * 0.85, C.accent[3] * 0.4)
ILine(L["INSTR_STEP1_NOTE"], 132)
ISep(156)

ILine(L["INSTR_STEP2_HDR"], 172, "GameFontNormalLarge", C.header[1], C.header[2], C.header[3])
ILine(L["INSTR_STEP2_BODY"], 198)

local reloadBtn = CreateFrame("Button", nil, InstrFrame, "BackdropTemplate")
reloadBtn:SetSize(110, 26)
reloadBtn:SetPoint("TOPLEFT", InstrFrame, "TOPLEFT", PAD, -216)
Backdrop(reloadBtn, {0.30, 0.10, 0.55, 1.00}, {0.50, 0.20, 0.80, 1.00})
local reloadLbl = reloadBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
reloadLbl:SetText(L["Reload UI"])
reloadLbl:SetTextColor(0.90, 0.80, 1.00)
reloadLbl:SetAllPoints(reloadBtn)
reloadLbl:SetJustifyH("CENTER")
reloadBtn:SetScript("OnEnter", function(self) self:SetBackdropColor(C.accent[1], C.accent[2], C.accent[3], 1) end)
reloadBtn:SetScript("OnLeave", function(self) self:SetBackdropColor(0.30, 0.10, 0.55, 1.00) end)
reloadBtn:SetScript("OnClick", function()
    CFLOpenOnLoad = true
    ReloadUI()
end)

ILine(L["INSTR_STEP2_NOTE"], 272)
ISep(292)

ILine(L["INSTR_STEP3_HDR"], 308, "GameFontNormalLarge", C.header[1], C.header[2], C.header[3])
ILine(L["INSTR_STEP3_BODY"], 334)
ILine(L["INSTR_STEP3_NOTE"], 360)
ISep(386)

ILine(L["Notes"], 402, "GameFontNormalLarge", C.header[1], C.header[2], C.header[3])
ILine(L["NOTE1"], 428)
ILine(L["NOTE2"], 452)
ILine(L["NOTE3"], 476)
ILine(L["NOTE4"], 500)

local FontFrame = ContentFrame()
local PANE_INNER = FRAME_W - SIDEBAR_W - 1
local ROW_H      = 30

local ListHeader = CreateFrame("Frame", nil, FontFrame)
ListHeader:SetHeight(44)
ListHeader:SetPoint("TOPLEFT",  FontFrame, "TOPLEFT",  0, 0)
ListHeader:SetPoint("TOPRIGHT", FontFrame, "TOPRIGHT", 0, 0)

local listLabel = Label(ListHeader, "GameFontNormalLarge", L["Registered Fonts"], C.text[1], C.text[2], C.text[3])
listLabel:SetPoint("LEFT", ListHeader, "LEFT", PAD, 0)

local countLabel = Label(ListHeader, "GameFontNormalSmall", "", C.sub[1], C.sub[2], C.sub[3])
countLabel:SetPoint("RIGHT", ListHeader, "RIGHT", -PAD, 0)
countLabel:SetJustifyH("RIGHT")

local listDivTop = Tex(FontFrame, C.border[1], C.border[2], C.border[3], 0.8)
listDivTop:SetHeight(1)
listDivTop:SetPoint("TOPLEFT",  FontFrame, "TOPLEFT",  PAD,  -44)
listDivTop:SetPoint("TOPRIGHT", FontFrame, "TOPRIGHT", -PAD, -44)

local ADD_PANEL_H = 170
local AddPanel = CreateFrame("Frame", nil, FontFrame, "BackdropTemplate")
AddPanel:SetHeight(ADD_PANEL_H)
AddPanel:SetPoint("BOTTOMLEFT",  FontFrame, "BOTTOMLEFT",  0, 1)
AddPanel:SetPoint("BOTTOMRIGHT", FontFrame, "BOTTOMRIGHT", 0, 1)
AddPanel:SetBackdrop({bgFile = "Interface\\Buttons\\WHITE8X8"})
AddPanel:SetBackdropColor(0.04, 0.03, 0.07, 1)

local addPanelDiv = Tex(FontFrame, C.accent[1], C.accent[2], C.accent[3], 0.25)
addPanelDiv:SetHeight(1)
addPanelDiv:SetPoint("BOTTOMLEFT",  FontFrame, "BOTTOMLEFT",  PAD,  ADD_PANEL_H)
addPanelDiv:SetPoint("BOTTOMRIGHT", FontFrame, "BOTTOMRIGHT", -PAD, ADD_PANEL_H)

local addTitle = Label(AddPanel, "GameFontNormalLarge", L["Add New Font"], C.header[1], C.header[2], C.header[3])
addTitle:SetPoint("TOPLEFT", AddPanel, "TOPLEFT", PAD, -14)

local addInstr = Label(AddPanel, "GameFontNormal",
    "Place the .ttf file in the Fonts folder, enter the filename below, then click Add Font.", C.sub[1], C.sub[2], C.sub[3])
addInstr:SetPoint("TOPLEFT", addTitle, "BOTTOMLEFT", 0, -6)
addInstr:SetWidth(PANE_INNER - PAD * 2)

local lblFile = Label(AddPanel, "GameFontNormal", L["Font Filename"], C.sub[1], C.sub[2], C.sub[3])
lblFile:SetPoint("TOPLEFT", addInstr, "BOTTOMLEFT", 0, -10)

local BTN_W        = 100
local FILE_INPUT_W = PANE_INNER - PAD * 2 - BTN_W - PAD - 8

local function MakeEditBox(parent, width, anchorPt, anchorTo, relPt, ox, oy)
    local eb = CreateFrame("EditBox", nil, parent, "BackdropTemplate")
    eb:SetHeight(32)
    eb:SetWidth(width)
    eb:SetPoint(anchorPt, anchorTo, relPt, ox, oy)
    Backdrop(eb, C.inputBg, C.border)
    eb:SetFontObject("GameFontNormal")
    eb:SetTextColor(C.text[1], C.text[2], C.text[3])
    eb:SetTextInsets(10, 10, 0, 0)
    eb:SetAutoFocus(false)
    eb:SetMaxLetters(256)
    eb:SetScript("OnEditFocusGained", function(self) self:SetBackdropBorderColor(C.accent[1], C.accent[2], C.accent[3], 0.9) end)
    eb:SetScript("OnEditFocusLost", function(self) self:SetBackdropBorderColor(C.border[1], C.border[2], C.border[3], 1) end)
    eb:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
    eb:SetScript("OnEnterPressed", function(self) self:ClearFocus() end)
    return eb
end

local fileBox = MakeEditBox(AddPanel, FILE_INPUT_W, "TOPLEFT", lblFile, "BOTTOMLEFT", 0, -4)

local AddBtn = CreateFrame("Button", nil, AddPanel, "BackdropTemplate")
AddBtn:SetSize(BTN_W, 32)
AddBtn:SetPoint("BOTTOMLEFT", fileBox, "BOTTOMRIGHT", 8, 0)
Backdrop(AddBtn, {0.30, 0.10, 0.55, 1.00}, {0.50, 0.20, 0.80, 1.00})
local addBtnLbl = AddBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
addBtnLbl:SetText(L["Add Font"])
addBtnLbl:SetTextColor(0.90, 0.80, 1.00)
addBtnLbl:SetAllPoints(AddBtn)
addBtnLbl:SetJustifyH("CENTER")
AddBtn:SetScript("OnEnter", function(self) self:SetBackdropColor(C.accent[1], C.accent[2], C.accent[3], 1) end)
AddBtn:SetScript("OnLeave", function(self) self:SetBackdropColor(0.30, 0.10, 0.55, 1.00) end)

local statusFS = Label(AddPanel, "GameFontNormal", "", C.sub[1], C.sub[2], C.sub[3])
statusFS:SetPoint("TOPLEFT", fileBox, "BOTTOMLEFT", 0, -6)
statusFS:SetWidth(PANE_INNER - PAD * 2)

local function SetStatus(msg, ok)
    statusFS:SetText(msg)
    if ok then
        statusFS:SetTextColor(C.accent[1], C.accent[2], C.accent[3])
    else
        statusFS:SetTextColor(1, 0.35, 0.20)
    end
end

local scrollFrame = CreateFrame("ScrollFrame", nil, FontFrame, "UIPanelScrollFrameTemplate")
scrollFrame:SetPoint("TOPLEFT",     FontFrame, "TOPLEFT",     PAD,       -46)
scrollFrame:SetPoint("BOTTOMRIGHT", FontFrame, "BOTTOMRIGHT", -(PAD + 22), ADD_PANEL_H + 2)

local scrollChild = CreateFrame("Frame", nil, scrollFrame)
scrollChild:SetWidth(1)
scrollChild:SetHeight(1)
scrollFrame:SetScrollChild(scrollChild)

local probeString = nil
local function GetProbe()
    if not probeString then
        probeString = UIParent:CreateFontString(nil, "ARTWORK")
        probeString:Hide()
    end
    return probeString
end

local function FontFileExists(path)
    local probe = GetProbe()
    local ok = pcall(function() probe:SetFont(path, 12) end)
    return ok
end

-- Font search paths in priority order.
-- Interface\Fonts\ is WoW's own shared font directory — completely outside
-- any addon folder, so CurseForge will never touch it. Safest place for
-- users to store their personal fonts. Falls back to the bundled Fonts folder.
local FONT_PATHS = {
    "Interface\\Fonts\\",
    "Interface\\AddOns\\CustomFontLoader\\Fonts\\",
}

-- Given a bare filename, return the first full path where the file loads,
-- or nil if not found. Reuses the shared probe FontString.
local function FindFontFile(fileName)
    local probe = GetProbe()
    for _, base in ipairs(FONT_PATHS) do
        local fullPath = base .. fileName
        if pcall(function() probe:SetFont(fullPath, 12) end) then
            return fullPath
        end
    end
    return nil
end


local function SyncFonts()
    local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
    if not LSM then return 0, 0 end
    if not CFLPendingFonts then CFLPendingFonts = {} end

    local removed = 0
    for i = #CFLPendingFonts, 1, -1 do
        local entry = CFLPendingFonts[i]
        if FontFileExists(entry.path) then
            LSM:Register("font", entry.name, entry.path)
        else
            local hash = LSM:HashTable("font")
            if hash[entry.name] == entry.path then
                hash[entry.name] = nil
            end
            table.remove(CFLPendingFonts, i)
            removed = removed + 1
        end
    end

    local total = 0
    for _ in pairs(LSM:HashTable("font")) do total = total + 1 end
    return total, removed
end

local function PopulateFontList()
    for _, c in ipairs({scrollChild:GetChildren()}) do
        c:Hide(); c:SetParent(nil)
    end
    for _, r in ipairs({scrollChild:GetRegions()}) do r:Hide() end

    local LSM   = LibStub and LibStub("LibSharedMedia-3.0", true)
    local fonts = LSM and LSM:HashTable("font") or {}

    local sorted = {}
    for name in pairs(fonts) do sorted[#sorted + 1] = name end
    table.sort(sorted)

    countLabel:SetText(string.format(L["FONT_COUNT"], #sorted))

    local vw = scrollFrame:GetWidth()
    scrollChild:SetWidth(vw)
    scrollChild:SetHeight(math.max(#sorted * ROW_H, 1))

    for i, name in ipairs(sorted) do
        local path    = fonts[name]
        local missing = path and not FontFileExists(path)

        local row = CreateFrame("Frame", nil, scrollChild, "BackdropTemplate")
        row:SetHeight(ROW_H)
        row:SetPoint("TOPLEFT",  scrollChild, "TOPLEFT",  0, -(i - 1) * ROW_H)
        row:SetPoint("TOPRIGHT", scrollChild, "TOPRIGHT", 0, -(i - 1) * ROW_H)
        if i % 2 == 1 then Backdrop(row, C.row1, {0, 0, 0, 0}) end

        local dot = row:CreateTexture(nil, "ARTWORK")
        dot:SetSize(5, 5)
        if missing then dot:SetColorTexture(0.8, 0.2, 0.2, 0.8) else dot:SetColorTexture(C.accent[1], C.accent[2], C.accent[3], 0.8) end
        dot:SetPoint("LEFT", row, "LEFT", 10, 0)

        local nameLbl = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        nameLbl:SetText(name)
        if missing then nameLbl:SetTextColor(0.6, 0.2, 0.2) else nameLbl:SetTextColor(C.text[1], C.text[2], C.text[3]) end
        nameLbl:SetPoint("LEFT", dot, "RIGHT", 6, 0)
        nameLbl:SetJustifyH("LEFT")

        if missing then
            local missingLbl = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            missingLbl:SetText(L["file not found"])
            missingLbl:SetTextColor(0.6, 0.2, 0.2)
            missingLbl:SetPoint("RIGHT", row, "RIGHT", -8, 0)
            missingLbl:SetJustifyH("RIGHT")
        elseif path then
            local prev = row:CreateFontString(nil, "OVERLAY")
            local ok = pcall(function() prev:SetFont(path, 12) end)
            if ok then
                prev:SetText("Aa Bb Cc 123")
                prev:SetTextColor(C.sub[1], C.sub[2], C.sub[3])
                prev:SetPoint("RIGHT", row, "RIGHT", -8, 0)
                prev:SetJustifyH("RIGHT")
            else
                prev:Hide()
            end
        end
    end

    if #sorted == 0 then
        local empty = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        empty:SetText(L["No fonts registered yet.  Add one below."])
        empty:SetTextColor(C.sub[1], C.sub[2], C.sub[3])
        empty:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 8, -10)
    end
end

AddBtn:SetScript("OnClick", function()
    local LSM  = LibStub and LibStub("LibSharedMedia-3.0", true)
    local file = fileBox:GetText():match("^%s*(.-)%s*$")

    if not LSM then SetStatus(L["ERR_LSM"], false); return end
    if file == "" then SetStatus(L["ERR_EMPTY"], false); return end

    local ext = file:match("%.([^%.]+)$")
    if ext and ext:lower() ~= "ttf" then
        SetStatus(string.format(L["ERR_EXT"], '.' .. ext), false)
        return
    end

    file = file:gsub("%.ttf$", ""):gsub("%.TTF$", "")
    local fileName   = file .. ".ttf"
    local displayName = file

    -- Search Interface\Fonts\ first, then the bundled Fonts folder
    local fullPath = FindFontFile(fileName)
    if not fullPath then
        SetStatus(string.format(L["ERR_MISSING"], fileName), false)
        return
    end

    if not CFLPendingFonts then CFLPendingFonts = {} end

    local exists = false
    for _, e in ipairs(CFLPendingFonts) do
        if e.name == displayName then e.path = fullPath; exists = true; break end
    end
    if not exists then table.insert(CFLPendingFonts, {name = displayName, path = fullPath}) end

    LSM:Register("font", displayName, fullPath)

    local total, removed = SyncFonts()
    fileBox:SetText("")
    PopulateFontList()

    local msg = '"' .. displayName .. '" added.'
    if removed > 0 then msg = msg .. "  Removed " .. removed .. " missing font(s)." end
    SetStatus(msg, true)
    print(string.format(L["PRINT_REGISTERED"], displayName))
end)

local function SelectTab(navBtn, contentFrame, title, sub)
    InstrFrame:Hide()
    FontFrame:Hide()
    if UtilFrame then UtilFrame:Hide() end

    for _, btn in ipairs({NavInstructions, NavFonts, NavUtilities}) do
        btn._selBG:SetColorTexture(C.accent[1], C.accent[2], C.accent[3], 0.00)
        btn._selBar:SetColorTexture(C.accent[1], C.accent[2], C.accent[3], 0.00)
        btn._lbl:SetTextColor(C.sub[1], C.sub[2], C.sub[3])
    end

    navBtn._selBG:SetColorTexture(C.accent[1], C.accent[2], C.accent[3], 0.12)
    navBtn._selBar:SetColorTexture(C.accent[1], C.accent[2], C.accent[3], 1.00)
    navBtn._lbl:SetTextColor(C.accent[1], C.accent[2], C.accent[3])
    activePane = navBtn

    paneTitle:SetText(title)
    paneSub:SetText(sub)
    contentFrame:Show()
end

NavInstructions:SetScript("OnClick", function() SelectTab(NavInstructions, InstrFrame, L["Instructions"], L["How to add fonts to CustomFontLoader"]) end)
NavFonts:SetScript("OnClick", function() SelectTab(NavFonts, FontFrame, L["Font Manager"], L["Fonts registered with LibSharedMedia-3.0"]); PopulateFontList() end)
NavUtilities:SetScript("OnClick", function() SelectTab(NavUtilities, UtilFrame, L["Utilities"], L["HUD display options"]) end)
Main:SetScript("OnShow", function() SelectTab(NavInstructions, InstrFrame, "Instructions", "How to add fonts to CustomFontLoader") end)

-- Canonical default bindings for the Hearthstone button
HS_DEFAULT_LEFT   = "Hearthstone"
HS_DEFAULT_RIGHT  = "Dalaran Hearthstone"
HS_DEFAULT_MIDDLE = "Personal Key to the Arcantina"

local loader = CreateFrame("Frame")
loader:RegisterEvent("ADDON_LOADED")
loader:RegisterEvent("PLAYER_LOGIN")
loader:SetScript("OnEvent", function(self, event, addon)
    if event == "ADDON_LOADED" then
        if addon ~= addonName then return end
        local total, removed = SyncFonts()
        if removed > 0 then print(string.format(L["PRINT_CLEANED"], removed, total)) end

        if not CFLUtilSettings then CFLUtilSettings = {} end
        local D = CFLUtilSettings
        if D.ibShow      == nil then D.ibShow      = false end
        if D.ibGold      == nil then D.ibGold      = true  end
        if D.ibFriends   == nil then D.ibFriends   = true  end
        if D.ibClock     == nil then D.ibClock     = true  end
        if D.ibFPS       == nil then D.ibFPS       = true  end
        if D.ibMS        == nil then D.ibMS        = true  end
        if D.ibDur       == nil then D.ibDur       = true  end
        if D.ibParty     == nil then D.ibParty     = true  end
        if D.ibScreenshot== nil then D.ibScreenshot= true  end
        if D.ibHeroDC    == nil then D.ibHeroDC    = true  end
        if D.ibMythDC    == nil then D.ibMythDC    = true  end
        if D.ibVoidcore  == nil then D.ibVoidcore  = true  end
        if D.ibHearthstone == nil then D.ibHearthstone = true end
        if D.ib24h       == nil then D.ib24h       = false end  -- false = 12h
        if D.ibLocalTime == nil then D.ibLocalTime = true  end  -- true = local time
        if D.hudLocked   == nil then D.hudLocked   = false end
        if D.hudX        == nil then D.hudX        = 0     end
        if D.hudY        == nil then D.hudY        = 30    end
        if D.hudScale    == nil then D.hudScale    = 1.0   end
        if D.hudPY       == nil then D.hudPY       = 30    end
        -- D.hudPX intentionally left nil on first install → centres the HUD on first load
        if D.hudSide     == nil then D.hudSide     = {}    end
        -- D.hudOrder intentionally left nil on first install → CFL_NormalizedOrder() supplies the default order

        local defText  = {font="Expressway", size=14, r=0.82, g=0.78, b=0.90, outline="OUTLINE"}
        local defAccnt = {font="Expressway", size=25, r=0.65, g=0.30, b=1.00, outline="OUTLINE"}
        if not D.ibGoldCfg      then D.ibGoldCfg      = {font=defText.font, size=14, r=1.00, g=0.82, b=0.00, outline="OUTLINE"} end
        if not D.ibFriendsCfg   then D.ibFriendsCfg   = {font=defText.font, size=12, r=defText.r, g=defText.g, b=defText.b, outline=""} end
        if not D.ibPartyCfg     then D.ibPartyCfg     = {font=defText.font, size=12, r=defText.r, g=defText.g, b=defText.b, outline=""} end
        if not D.ibClockCfg     then D.ibClockCfg     = {font=defAccnt.font, size=25, r=defAccnt.r, g=defAccnt.g, b=defAccnt.b, outline="OUTLINE"} end
        if not D.ibFPScfg       then D.ibFPScfg       = {font=defText.font, size=14, r=defText.r, g=defText.g, b=defText.b, outline="OUTLINE"} end
        if not D.ibHeroDCcfg    then D.ibHeroDCcfg    = {font=defText.font, size=14, r=defText.r, g=defText.g, b=defText.b, outline="OUTLINE"} end
        if not D.ibMythDCcfg    then D.ibMythDCcfg    = {font=defText.font, size=14, r=defText.r, g=defText.g, b=defText.b, outline="OUTLINE"} end
        if not D.ibVoidcoreCfg  then D.ibVoidcoreCfg  = {font=defText.font, size=14, r=defText.r, g=defText.g, b=defText.b, outline="OUTLINE"} end
        if not D.ibHsCfg then D.ibHsCfg = { left=HS_DEFAULT_LEFT, right=HS_DEFAULT_RIGHT, middle=HS_DEFAULT_MIDDLE } end

        CFL_RefreshUtilUI()

        if CFLOpenOnLoad then
            CFLOpenOnLoad = nil
            C_Timer.After(0.5, function() Main:Show() end)
        end

    elseif event == "PLAYER_LOGIN" then
        if CFLUtilSettings then CFL_ApplyHUDState() end
        -- Belt-and-suspenders: guarantee the HUD is never left in "armed for
        -- keyboard nudge" state coming out of a login or /reload, no matter
        -- what could have set it. This is exactly what clicking Lock Position
        -- does today (which is why toggling it "fixes" a stuck keyboard) —
        -- doing it here means that reset happens automatically instead of
        -- requiring you to find and click Lock first.
        if CFL_SetHudSelected then CFL_SetHudSelected(false) end
    end
end)
end -- close do-block opened after the Main-window forward declarations

-- ============================================================
--  UTILITIES — HUD
-- ============================================================

local function ResolveFont(fontName)
    local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
    if LSM then
        local path = LSM:Fetch("font", fontName or "")
        if path then return path end
    end
    return fontName or ""
end

local OUTLINE_OPTIONS = {
    { value = "",                           label = "None" },
    { value = "OUTLINE",                    label = "Outline" },
    { value = "THICKOUTLINE",               label = "Thick Outline" },
    { value = "MONOCHROME",                 label = "Monochrome" },
    { value = "OUTLINE, MONOCHROME",       label = "Outline + Monochrome" },
    { value = "THICKOUTLINE, MONOCHROME",  label = "Thick Outline + Monochrome" },
}

local function OutlineLabelFor(value)
    for _, opt in ipairs(OUTLINE_OPTIONS) do
        if opt.value == (value or "") then return opt.label end
    end
    return "None"
end

local RefreshClkBtns

-- Currency IDs — Midnight Season 1
local CURRENCY_HERO_DAWNCREST = 3345   -- Hero Dawncrest
local CURRENCY_MYTH_DAWNCREST = 3347   -- Myth Dawncrest
local CURRENCY_VOIDCORE       = 3418   -- Nebulous Voidcore

-- ============================================================
--  Hearthstone / Teleport Button Data
-- ============================================================
-- Each entry: { label, use }
--   Everything goes through /use — works for hearthstones, toys, and spells by name.
--   Empty use = disabled slot.
local HS_ACTIONS = {
    -- ── Core Hearthstones (unique cooldowns / destinations) ──────────────────
    { label = "Hearthstone",                          use = "Hearthstone" },
    { label = "Garrison Hearthstone",                 use = "Garrison Hearthstone" },
    { label = "Dalaran Hearthstone",                  use = "Dalaran Hearthstone" },
    -- ── Shared-cooldown reskins — A-Z ────────────────────────────────────────
    { label = "Brewfest Reveler's Hearthstone",       use = "Brewfest Reveler's Hearthstone" },
    { label = "Broker Translocation Matrix",          use = "Broker Translocation Matrix" },
    { label = "Corewarden's Hearthstone",             use = "Corewarden's Hearthstone" },
    { label = "Cosmic Hearthstone",                   use = "Cosmic Hearthstone" },
    { label = "Dark Portal",                          use = "Dark Portal" },
    { label = "Death Knight's Hearthstone",           use = "Death Knight's Hearthstone" },
    { label = "Deepdweller's Earthen Hearthstone",   use = "Deepdweller's Earthen Hearthstone" },
    { label = "Dominated Hearthstone",                use = "Dominated Hearthstone" },
    { label = "Enlightened Hearthstone",              use = "Enlightened Hearthstone" },
    { label = "Eternal Traveler's Hearthstone",       use = "Eternal Traveler's Hearthstone" },
    { label = "Ethereal Portal",                      use = "Ethereal Portal" },
    { label = "Explosive Hearthstone",                use = "Explosive Hearthstone" },
    { label = "Fire Eater's Hearthstone",             use = "Fire Eater's Hearthstone" },
    { label = "Greatfather Winter's Hearthstone",     use = "Greatfather Winter's Hearthstone" },
    { label = "Headless Horseman's Hearthstone",      use = "Headless Horseman's Hearthstone" },
    { label = "Hearthstone of the Flame",             use = "Hearthstone of the Flame" },
    { label = "Holographic Digitalization HS",        use = "Holographic Digitalization Hearthstone" },
    { label = "Innkeeper's Daughter",                 use = "Innkeeper's Daughter" },
    { label = "Kor'kron Hearthstone",                 use = "Kor'kron Hearthstone" },
    { label = "Kyrian Hearthstone",                   use = "Kyrian Hearthstone" },
    { label = "Lightcalled Hearthstone",              use = "Lightcalled Hearthstone" },
    { label = "Lunar Elder's Hearthstone",            use = "Lunar Elder's Hearthstone" },
    { label = "Necrolord Hearthstone",                use = "Necrolord Hearthstone" },
    { label = "Night Fae Hearthstone",                use = "Night Fae Hearthstone" },
    { label = "Noble Gardener's Hearthstone",         use = "Noble Gardener's Hearthstone" },
    { label = "Ohn'ir Windsage's Hearthstone",        use = "Ohn'ir Windsage's Hearthstone" },
    { label = "P.O.S.T. Master's Express Hearthstone",use = "P.O.S.T. Master's Express Hearthstone" },
    { label = "Peddlefeet's Lovely Hearthstone",      use = "Peddlefeet's Lovely Hearthstone" },
    { label = "Stone of the Hearth",                  use = "Stone of the Hearth" },
    { label = "Timepiece of the Bronze Flight",       use = "Timepiece of the Bronze Flight" },
    { label = "Timewalker's Hearthstone",             use = "Timewalker's Hearthstone" },
    { label = "Tome of Town Portal",                  use = "Tome of Town Portal" },
    { label = "Venthyr Sinstone",                     use = "Venthyr Sinstone" },
    -- ── Special / class ──────────────────────────────────────────────────────
    { label = "Personal Key to the Arcantina",        use = "Personal Key to the Arcantina" },
    { label = "Astral Recall",                        use = "Astral Recall",           macro = true },
    -- ── Disabled ─────────────────────────────────────────────────────────────
    { label = "Nothing (disabled)",                   use = "" },
}



local ICON_FRIENDS    = "Interface\\Icons\\achievement_guildperk_everybodysfriend"
local ICON_PARTY      = "Interface\\Icons\\ui_greenflag"
local ICON_SCREENSHOT = "Interface\\Icons\\Inv_misc_selfiecamera_red_camera"

-- ============================================================
--  HUD icon order / side configuration
-- ============================================================
-- Every left/right-wing element (everything except the centred clock block)
-- has a stable key. D.hudOrder is a saved array of these keys defining the
-- left-to-right sequence; D.hudSide[key] = "left"/"right" picks the wing.
-- Both default to the addon's original layout.
local DEFAULT_HUD_ORDER = { "gold", "friends", "party", "screenshot", "hearthstone", "herodc", "mythdc", "voidcore" }
local DEFAULT_HUD_SIDE  = {
    gold = "left",  friends = "left",  party = "left",  screenshot = "left",
    hearthstone = "right", herodc = "right", mythdc = "right", voidcore = "right",
}

-- Returns a full ordering array containing every known key exactly once:
-- saved order first (minus unknown/stale keys), then any missing keys
-- appended in their default position. This keeps old SavedVariables safe
-- even if new orderable elements are added in a future version.
local function CFL_NormalizedOrder()
    local D = CFLUtilSettings
    local result, seen = {}, {}
    if D and D.hudOrder then
        for _, k in ipairs(D.hudOrder) do
            if DEFAULT_HUD_SIDE[k] and not seen[k] then
                result[#result+1] = k
                seen[k] = true
            end
        end
    end
    for _, k in ipairs(DEFAULT_HUD_ORDER) do
        if not seen[k] then
            result[#result+1] = k
            seen[k] = true
        end
    end
    return result
end

-- Forward declaration: assigned once the Icon Order settings rows are built.
local RelayoutOrderRows

-- Moves `key` to absolute position `newIndex` (1-based) in the saved order,
-- shifting everything else to make room. Used by the drag-to-reorder rows.
-- Returns true if the order actually changed.
local function CFL_ReorderTo(key, newIndex)
    local D = CFLUtilSettings
    if not D then return false end
    local order = CFL_NormalizedOrder()
    local idx
    for i, k in ipairs(order) do if k == key then idx = i break end end
    if not idx then return false end
    newIndex = math.max(1, math.min(newIndex, #order))
    if newIndex == idx then return false end
    table.remove(order, idx)
    table.insert(order, newIndex, key)
    D.hudOrder = order
    return true
end

-- Switches `key` to the given side ("left"/"right") and repositions it to be
-- the first (closest-to-clock) element of that side — e.g. moving Gold from
-- left to right drops it in right before Hearthstone, becoming the new
-- first right-side icon, exactly like reading position on screen.
local function CFL_SetItemSide(key, side)
    local D = CFLUtilSettings
    if not D then return end
    D.hudSide = D.hudSide or {}
    if D.hudSide[key] == side then return end
    D.hudSide[key] = side

    local order = CFL_NormalizedOrder()
    local idx
    for i, k in ipairs(order) do if k == key then idx = i break end end
    if idx then
        table.remove(order, idx)
        local insertAt = #order + 1
        for i, k in ipairs(order) do
            local kSide = (D.hudSide and D.hudSide[k]) or DEFAULT_HUD_SIDE[k]
            if kSide == side then insertAt = i; break end
        end
        table.insert(order, insertAt, key)
        D.hudOrder = order
    end

    if CFL_ApplyHUDState then CFL_ApplyHUDState() end
    if RelayoutOrderRows then RelayoutOrderRows() end
end

-- Shared sizing constants — also used by CFL_ApplyHUDState's real layout
-- pass and by CFL_ComputeMaxHudWidth's what-if calculation, so the two
-- can never drift out of sync with each other.
local HUD_ICON_SIZE = 20
local HUD_ITEM_GAP  = 20  -- gap between elements within each wing
local HUD_WING_PAD  = 10  -- extra padding between each wing's outermost element and the clock

local HUD = CreateFrame("Frame", "CFLHudFrame", UIParent)
HUD:SetSize(300, 24)
HUD:SetPoint("TOP", UIParent, "TOP", 0, -30)
HUD:SetFrameStrata("MEDIUM")
HUD:SetMovable(true)
HUD:EnableMouse(true)
HUD:SetClampedToScreen(true)
HUD:Hide()

function CFL_NudgeHud(dx, dy)
    if not CFLUtilSettings or CFLUtilSettings.hudLocked then return end
    local left   = math.floor((HUD:GetLeft()   or 0) + dx + 0.5)
    local bottom = math.floor((HUD:GetBottom() or 0) + dy + 0.5)
    HUD:ClearAllPoints()
    HUD:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
    CFLUtilSettings.hudPX = left
    CFLUtilSettings.hudPY = bottom
end

-- ============================================================
--  Click-to-arm arrow-key nudge
-- ============================================================
-- Rules, deliberately narrow:
--  - Only reachable by an explicit left-click on the HUD while it's unlocked.
--  - Being unlocked alone does nothing — no click, no key capture, ever.
--  - A visible border + a "Save Position" button appear anchored to the HUD
--    itself so it's obvious you're in this mode and exactly how to leave it.
--  - Escape, clicking Save, clicking Lock, hiding the HUD, clicking out in
--    the world, or entering combat all release it immediately.
--
-- IMPORTANT: this uses SetOverrideBinding, not EnableKeyboard. Override
-- bindings only intercept the *exact* key strings you register — LEFT,
-- RIGHT, UP, DOWN, SHIFT-LEFT etc., and ESCAPE — and nothing else. Movement
-- keys, spell keybinds, chat, everything else keeps working normally the
-- entire time you're nudging the HUD. An earlier version used EnableKeyboard
-- (a blanket keyboard-capture toggle) and that caused real, repeated
-- keyboard-lockup bugs; override bindings are the correct, narrowly-scoped
-- tool for "add a couple of temporary hotkeys" and don't have that failure
-- mode by construction.
local hudSelected = false

-- Invisible helper buttons that the override bindings click. Named/global so
-- SetOverrideBinding's "CLICK name:LeftButton" action string can find them.
local function MakeNudgeClickTarget(name, onClick)
    local btn = CreateFrame("Button", name, UIParent)
    btn:Hide()  -- never actually shown; exists only as a click target
    btn:SetScript("OnClick", onClick)
    return btn
end

local hudNudgeLeftBtn  = MakeNudgeClickTarget("CFLHudNudgeLeftBtn",  function() CFL_NudgeHud(-(IsShiftKeyDown() and 10 or 1), 0) end)
local hudNudgeRightBtn = MakeNudgeClickTarget("CFLHudNudgeRightBtn", function() CFL_NudgeHud( (IsShiftKeyDown() and 10 or 1), 0) end)
local hudNudgeUpBtn    = MakeNudgeClickTarget("CFLHudNudgeUpBtn",    function() CFL_NudgeHud(0,  (IsShiftKeyDown() and 10 or 1)) end)
local hudNudgeDownBtn  = MakeNudgeClickTarget("CFLHudNudgeDownBtn",  function() CFL_NudgeHud(0, -(IsShiftKeyDown() and 10 or 1)) end)
local hudNudgeExitBtn  = MakeNudgeClickTarget("CFLHudNudgeExitBtn",  function() CFL_SetHudSelected(false) end)

-- Computes the widest the HUD could ever be with the *current* font/size
-- settings, as if every optional element (gold, friends, party, screenshot,
-- hearthstone, all three currencies, clock, and all three stats) were
-- turned on — regardless of what's actually toggled on right now. This is
-- what the select-mode border is sized to, so positioning the HUD with only
-- a few elements enabled still shows exactly how much room it will need
-- once everything is turned on.
function CFL_ComputeMaxHudWidth()
    local D = CFLUtilSettings or {}
    local ICON, GAP, PAD_WING = HUD_ICON_SIZE, HUD_ITEM_GAP, HUD_WING_PAD

    -- Left wing: Gold, Friends, Party, Screenshot
    local gSize = (D.ibGoldCfg or {}).size or 14
    local goldW = math.max(gSize * 5, 55)
    local leftW = goldW + GAP + ICON + GAP + ICON + GAP + ICON

    -- Right wing: Hearthstone, Hero DC, Myth DC, Voidcore
    local rightW = ICON + GAP + ICON + GAP + ICON + GAP + ICON

    -- Centre: clock stacked over all three stats, at their configured sizes
    local clkFontSize = (D.ibClockCfg or {}).size or 25
    local stFontSize  = (D.ibFPScfg or {}).size or 14
    local clkTextW = math.max(clkFontSize * 3.5, 70)
    local stTextW  = math.max(stFontSize * 4.5 * 3, 50)
    local clockW = math.max(clkTextW, stTextW, 60)

    return math.floor(leftW + PAD_WING + clockW + PAD_WING + rightW + 0.5)
end

local hudSelectBorder = CreateFrame("Frame", nil, HUD, "BackdropTemplate")
hudSelectBorder:SetFrameLevel(math.max(HUD:GetFrameLevel() - 1, 0))
hudSelectBorder:SetBackdrop({
    edgeFile = "Interface\\Buttons\\WHITE8X8",
    edgeSize = 3,
})
hudSelectBorder:SetBackdropBorderColor(0.65, 0.30, 1.00, 1)
hudSelectBorder:Hide()

-- Resizes/repositions the border to the current max-possible width, anchored
-- to the HUD's own BOTTOMLEFT — the one point that never moves as elements
-- are toggled on/off (everything else shifts around it), so the box stays
-- correctly aligned with whatever's currently on screen.
local HUD_BORDER_PAD = 16
function CFL_ResizeSelectBorder()
    local maxW = CFL_ComputeMaxHudWidth()
    hudSelectBorder:ClearAllPoints()
    hudSelectBorder:SetPoint("BOTTOMLEFT", HUD, "BOTTOMLEFT", -HUD_BORDER_PAD, -HUD_BORDER_PAD)
    hudSelectBorder:SetSize(maxW + HUD_BORDER_PAD * 2, HUD:GetHeight() + HUD_BORDER_PAD * 2)
end

-- Save Position button + hint share one row at the border's top-right.
local hudSaveBtn = CreateFrame("Button", "CFLHudSaveBtn", hudSelectBorder, "BackdropTemplate")
hudSaveBtn:SetSize(120, 22)
hudSaveBtn:SetPoint("BOTTOMRIGHT", hudSelectBorder, "TOPRIGHT", 0, 4)
Backdrop(hudSaveBtn, {0.20, 0.10, 0.32, 1}, {0.65, 0.30, 1.00, 1})
local hudSaveLbl = hudSaveBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
hudSaveLbl:SetAllPoints(hudSaveBtn); hudSaveLbl:SetJustifyH("CENTER")
hudSaveLbl:SetText(L["Save Position"])
hudSaveLbl:SetTextColor(0.90, 0.80, 1.00, 1)
hudSaveBtn:SetScript("OnEnter", function(self) self:SetBackdropColor(0.30, 0.15, 0.46, 1) end)
hudSaveBtn:SetScript("OnLeave", function(self) self:SetBackdropColor(0.20, 0.10, 0.32, 1) end)
hudSaveBtn:Hide()

local hudSelectHint = hudSelectBorder:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
hudSelectHint:SetPoint("RIGHT", hudSaveBtn, "LEFT", -10, 0)
hudSelectHint:SetTextColor(0.65, 0.30, 1.00, 1)
hudSelectHint:SetText(L["HUD_SELECT_HINT"])

function CFL_SetHudSelected(state)
    hudSelected = state and true or false
    if hudSelected then
        CFL_ResizeSelectBorder()
        hudSelectBorder:Show()
        hudSaveBtn:Show()
        ClearOverrideBindings(HUD)
        SetOverrideBinding(HUD, false, "LEFT",        "CLICK CFLHudNudgeLeftBtn:LeftButton")
        SetOverrideBinding(HUD, false, "SHIFT-LEFT",  "CLICK CFLHudNudgeLeftBtn:LeftButton")
        SetOverrideBinding(HUD, false, "RIGHT",       "CLICK CFLHudNudgeRightBtn:LeftButton")
        SetOverrideBinding(HUD, false, "SHIFT-RIGHT", "CLICK CFLHudNudgeRightBtn:LeftButton")
        SetOverrideBinding(HUD, false, "UP",          "CLICK CFLHudNudgeUpBtn:LeftButton")
        SetOverrideBinding(HUD, false, "SHIFT-UP",    "CLICK CFLHudNudgeUpBtn:LeftButton")
        SetOverrideBinding(HUD, false, "DOWN",        "CLICK CFLHudNudgeDownBtn:LeftButton")
        SetOverrideBinding(HUD, false, "SHIFT-DOWN",  "CLICK CFLHudNudgeDownBtn:LeftButton")
        SetOverrideBinding(HUD, false, "ESCAPE",      "CLICK CFLHudNudgeExitBtn:LeftButton")
    else
        hudSelectBorder:Hide()
        hudSaveBtn:Hide()
        ClearOverrideBindings(HUD)
    end
end

hudSaveBtn:SetScript("OnClick", function() CFL_SetHudSelected(false) end)

HUD:HookScript("OnHide", function() CFL_SetHudSelected(false) end)

WorldFrame:HookScript("OnMouseDown", function()
    if hudSelected then CFL_SetHudSelected(false) end
end)

-- Hard safety net: never allow keyboard capture to survive into combat,
-- no matter how it was triggered.
local hudSelectSafety = CreateFrame("Frame")
hudSelectSafety:RegisterEvent("PLAYER_REGEN_DISABLED")
hudSelectSafety:SetScript("OnEvent", function() CFL_SetHudSelected(false) end)

-- CFL_ApplyHUDState skips repositioning the secure hearthstone button while
-- in combat (protected call). Re-apply once combat ends so it snaps to
-- wherever it should have moved to in the meantime.
local hsRegenCatchup = CreateFrame("Frame")
hsRegenCatchup:RegisterEvent("PLAYER_REGEN_ENABLED")
hsRegenCatchup:SetScript("OnEvent", function()
    if CFL_ApplyHUDState then CFL_ApplyHUDState() end
end)

local function HandleDrag(btn)
    btn:RegisterForDrag("LeftButton")
    btn:SetScript("OnMouseDown", function(_, mouseButton)
        if mouseButton ~= "LeftButton" then return end
        -- The one and only trigger: an explicit click on the HUD while it's
        -- unlocked. Being unlocked by itself does nothing.
        if not (CFLUtilSettings and not CFLUtilSettings.hudLocked) then return end
        if InCombatLockdown() then return end
        CFL_SetHudSelected(true)
    end)
    btn:SetScript("OnDragStart", function()
        if CFLUtilSettings and CFLUtilSettings.hudLocked then return end
        HUD:StartMoving()
    end)
    btn:SetScript("OnDragStop", function()
        HUD:StopMovingOrSizing()
        if CFLUtilSettings then
            -- GetLeft() and GetBottom() are absolute screen coords (origin = screen bottom-left).
            -- No UIParent dimension math at all — immune to any GetWidth/GetHeight timing issues.
            -- BOTTOMLEFT anchor from UIParent BOTTOMLEFT (screen origin) is purely additive:
            -- frame bottom-left ends up at exactly (left, bottom) every single time.
            local left   = math.floor(HUD:GetLeft()   + 0.5)
            local bottom = math.floor(HUD:GetBottom() + 0.5)
            HUD:ClearAllPoints()
            HUD:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
            CFLUtilSettings.hudPX = left
            CFLUtilSettings.hudPY = bottom   -- bottom edge from screen bottom
        end
    end)
end
HandleDrag(HUD)

local ibEls     = {}
local ibTickers = {}

local function TakeCleanScreenshot()
    UIErrorsFrame:Hide()
    Screenshot()
    C_Timer.After(0.5, function() UIErrorsFrame:Show() end)
end

local function ClassColor()
    local _, cls = UnitClass("player")
    if cls and RAID_CLASS_COLORS and RAID_CLASS_COLORS[cls] then
        local c = RAID_CLASS_COLORS[cls]; return c.r, c.g, c.b
    end
    return C.text[1], C.text[2], C.text[3]
end

local function StopIBTickers()
    for _, t in ipairs(ibTickers) do t:Cancel() end
    ibTickers = {}
end

local function GetOrMakeIconBtn(key, iconID, size, template)
    size = size or 20
    if not ibEls[key] then
        local btn = CreateFrame("Button", nil, HUD, template)
        btn:SetSize(size, size)
        local tex = btn:CreateTexture(nil, "ARTWORK")
        tex:SetAllPoints(btn)
        tex:SetTexture(iconID)
        btn._tex = tex
        local hl = btn:CreateTexture(nil, "HIGHLIGHT")
        hl:SetAllPoints(btn)
        hl:SetTexture("Interface\\Buttons\\ButtonHilight-Square")
        hl:SetBlendMode("ADD")
        HandleDrag(btn)
        ibEls[key] = btn
    end
    return ibEls[key]
end

local function GetOrMakeFrame(key)
    if not ibEls[key] then
        local f = CreateFrame("Button", nil, HUD)
        f:EnableMouse(true)
        HandleDrag(f)
        ibEls[key] = f
    end
    return ibEls[key]
end

function CFL_ApplyHUDState()
    local D = CFLUtilSettings
    if not D then return end
    StopIBTickers()
    -- CFLHearthstoneBtn is a SecureActionButtonTemplate frame: ClearAllPoints/
    -- SetPoint/SetSize/Hide become protected calls on it during combat lockdown.
    -- Skip touching it while in combat; PLAYER_REGEN_ENABLED re-applies once
    -- combat ends so it catches up to whatever layout it missed.
    local inCombat = InCombatLockdown()
    local hsBtn = ibEls["hearthstone"]
    for _, el in pairs(ibEls) do
        if not (inCombat and el == hsBtn) then el:ClearAllPoints(); el:Hide() end
    end

    if not D.ibShow then HUD:Hide(); return end
    HUD:Show()
    HUD:SetScale(D.hudScale or 1.0)

    local cr, cg, cb = ClassColor()
    local pr, pg, pb = C.accent[1], C.accent[2], C.accent[3]
    local ICON     = HUD_ICON_SIZE
    local GAP      = HUD_ITEM_GAP   -- gap between elements within each wing
    local PAD_WING = HUD_WING_PAD   -- extra padding between each wing's outermost element and the clock

    -- Elements are collected into a pendingEls map first (keyed by the stable
    -- ids used by D.hudOrder / D.hudSide), then assigned to left/right lists
    -- in a later pass according to the user's saved order and side, and
    -- finally positioned so both wings are equidistant from the clock centre.
    local leftEls  = {}   -- { el, w, h }
    local clockEl  = nil; local clockW = 0; local clockH_val = 0
    local rightEls = {}   -- { el, w, h }
    local pendingEls = {} -- key -> { el, w, h }

    local function QLft(el, w, h) leftEls[#leftEls+1]  = { el=el, w=w, h=h or ICON } end
    local function QRgt(el, w, h) rightEls[#rightEls+1] = { el=el, w=w, h=h or ICON } end

    -- ── 1. Gold (far left) ───────────────────────────────────────────────────
    if D.ibGold then
        local blk = GetOrMakeFrame("gold")
        if not blk._lbl then
            blk._lbl = blk:CreateFontString(nil, "OVERLAY", "GameFontNormal")
            blk._lbl:SetJustifyH("LEFT")
            blk._lbl:SetPoint("CENTER", blk, "CENTER", 0, 0)
            blk:SetScript("OnEnter", function(s)
                GameTooltip:SetOwner(s, "ANCHOR_NONE")
                GameTooltip:SetPoint("TOPLEFT", s, "BOTTOMLEFT", 0, -24)
                GameTooltip:SetText(L["TT_GOLD"], 1, 1, 1)
                GameTooltip:Show()
            end)
            blk:SetScript("OnLeave", function() GameTooltip:Hide() end)
        end
        local gCfg = D.ibGoldCfg or {}
        pcall(function() blk._lbl:SetFont(ResolveFont(gCfg.font), gCfg.size or 14, "OUTLINE") end)
        blk._lbl:SetTextColor(1, 1, 1)  -- colors are embedded in format strings
        local function UpdGold()
            local copper = GetMoney() or 0
            local g = math.floor(copper / 10000)
            local s = math.floor((copper % 10000) / 100)
            local c = copper % 100
            local txt
            if g > 0 then
                txt = string.format("|cFFFFD700%dg|r", g)
                if s > 0 or c > 0 then txt = txt .. string.format(" |cFFC0C0C0%ds|r", s) end
            elseif s > 0 then
                txt = string.format("|cFFC0C0C0%ds|r", s)
                if c > 0 then txt = txt .. string.format(" |cFFCD7F32%dc|r", c) end
            else
                txt = string.format("|cFFCD7F32%dc|r", c)
            end
            blk._lbl:SetText(txt)
        end
        UpdGold()
        ibTickers[#ibTickers+1] = C_Timer.NewTicker(5, UpdGold)
        -- Use font-size based width; GetStringWidth() is 0 at load time
        local gSize = (D.ibGoldCfg or {}).size or 14
        local w = math.max(gSize * 5, 55)  -- enough for "9999g 99s"
        blk:SetSize(w, ICON)
        pendingEls["gold"] = { el = blk, w = w, h = ICON }
    end

    -- ── 2. Friends (left side) ───────────────────────────────────────────────
    if D.ibFriends then
        local btn = GetOrMakeIconBtn("friends", ICON_FRIENDS, ICON)
        if not btn._count then
            btn._count = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            btn._count:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", 6, -3)
        end
        -- Tooltip: show in-game BNet friends with class colors
        btn:SetScript("OnEnter", function(s)
            GameTooltip:SetOwner(s, "ANCHOR_NONE")
            GameTooltip:SetPoint("TOPLEFT", s, "BOTTOMLEFT", 0, -24)
            -- Collect BNet friends actually playing WoW right now
            local ingame = {}
            if BNGetNumFriends then
                local numTotal = select(1, BNGetNumFriends()) or 0
                for i = 1, numTotal do
                    local accountInfo = C_BattleNet.GetFriendAccountInfo(i)
                    if accountInfo and accountInfo.gameAccountInfo then
                        local ga = accountInfo.gameAccountInfo
                        if ga.isOnline and ga.clientProgram == BNET_CLIENT_WOW then
                            local btag = accountInfo.battleTag or accountInfo.accountName or "Unknown"
                            local char = ga.characterName or btag
                            local zone = ga.areaName or ""
                            -- Class color: ga.className is the English class name key
                            local classHex = "FFFFFF"
                            if ga.className and RAID_CLASS_COLORS then
                                -- RAID_CLASS_COLORS is keyed by uppercase English class name
                                local clsUpper = ga.className:upper():gsub(" ", "")
                                -- Try direct lookup first, then scan for match
                                local clr = RAID_CLASS_COLORS[clsUpper]
                                if not clr then
                                    for k, v in pairs(RAID_CLASS_COLORS) do
                                        if k:upper():gsub(" ", "") == clsUpper then clr = v; break end
                                    end
                                end
                                if clr then
                                    classHex = string.format("%02X%02X%02X",
                                        math.floor(clr.r * 255),
                                        math.floor(clr.g * 255),
                                        math.floor(clr.b * 255))
                                end
                            end
                            -- BNet blue: #1D8DBA; class-coloured character name
                            local btagStr = "|cFF1D8DBA" .. btag .. "|r"
                            local charStr = "|cFF" .. classHex .. char .. "|r"
                            local entry = btagStr .. " " .. charStr
                            if zone ~= "" then
                                entry = entry .. " |cFF888888- " .. zone .. "|r"
                            end
                            ingame[#ingame+1] = entry
                        end
                    end
                end
            end
            local total = btn._count:GetText() or "0"
            GameTooltip:SetText(string.format(L["TT_FRIENDS"], total), 1, 1, 1)
            if #ingame > 0 then
                GameTooltip:AddLine(L["TT_FRIENDS_INWOW"], 0.65, 0.30, 1.00)
                for _, line in ipairs(ingame) do
                    GameTooltip:AddLine(line, 1, 1, 1, true)
                end
            else
                GameTooltip:AddLine(L["TT_FRIENDS_NONE"], 0.6, 0.6, 0.6)
            end
            GameTooltip:Show()
        end)
        btn:SetScript("OnLeave", function() GameTooltip:Hide() end)
        btn:SetScript("OnClick", function()
            ToggleFriendsFrame(1)   -- 1 = Friends tab
        end)

        local fCfg = D.ibFriendsCfg or {}
        pcall(function() btn._count:SetFont(ResolveFont(fCfg.font), fCfg.size or 12, fCfg.outline or "") end)
        btn._count:SetTextColor(fCfg.r or cr, fCfg.g or cg, fCfg.b or cb)
        local function Upd()
            local n = 0
            for i = 1, C_FriendList.GetNumFriends() do
                local info = C_FriendList.GetFriendInfoByIndex(i)
                if info and info.connected then n = n + 1 end
            end
            btn._count:SetText(tostring(n + ((BNGetNumFriends and select(1,BNGetNumFriends())) or 0)))
        end
        Upd(); ibTickers[#ibTickers+1] = C_Timer.NewTicker(10, Upd)
        pendingEls["friends"] = { el = btn, w = ICON, h = ICON }
    end

    -- ── 3. Party (left side, closest to clock) ───────────────────────────────
    if D.ibParty then
        local btn = GetOrMakeIconBtn("party", ICON_PARTY, ICON)
        if not btn._count then
            btn._count = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            btn._count:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", 6, -3)
            btn:SetScript("OnEnter", function(s)
                local isRaid = IsInRaid()
                local tip = isRaid and L["TT_RAID_SIZE"] or L["TT_PARTY_SIZE"]
                GameTooltip:SetOwner(s, "ANCHOR_NONE")
                GameTooltip:SetPoint("TOPLEFT", s, "BOTTOMLEFT", 0, -24)
                GameTooltip:SetText(tip, 1, 1, 1)
                GameTooltip:Show()
            end)
            btn:SetScript("OnLeave", function() GameTooltip:Hide() end)
            btn:SetScript("OnClick", function()
                -- Open Social panel on the Group/Raid tab (tab 3)
                ToggleFriendsFrame(3)
            end)
        end
        local pCfg = D.ibPartyCfg or {}
        pcall(function() btn._count:SetFont(ResolveFont(pCfg.font), pCfg.size or 12, pCfg.outline or "") end)
        btn._count:SetTextColor(pCfg.r or cr, pCfg.g or cg, pCfg.b or cb)
        local function Upd()
            local n
            if IsInRaid() then
                n = GetNumGroupMembers()          -- full raid size (1-40)
            elseif IsInGroup() then
                n = GetNumGroupMembers()          -- party size includes player (2-5)
            else
                n = 1                             -- solo
            end
            btn._count:SetText(tostring(n))
        end
        Upd(); ibTickers[#ibTickers+1] = C_Timer.NewTicker(3, Upd)
        pendingEls["party"] = { el = btn, w = ICON, h = ICON }
    end

    -- ── 4. Screenshot (left side, closest to clock) ───────────────────────────
    if D.ibScreenshot then
        local btn = GetOrMakeIconBtn("screenshot", ICON_SCREENSHOT, ICON)
        if not btn._ready then
            btn:SetScript("OnClick", TakeCleanScreenshot)
            btn:SetScript("OnEnter", function(s) GameTooltip:SetOwner(s,"ANCHOR_NONE"); GameTooltip:SetPoint("TOPLEFT",s,"BOTTOMLEFT",0,-24); GameTooltip:SetText(L["TT_SCREENSHOT"],1,1,1); GameTooltip:Show() end)
            btn:SetScript("OnLeave", function() GameTooltip:Hide() end)
            btn._ready = true
        end
        pendingEls["screenshot"] = { el = btn, w = ICON, h = ICON }
    end

    -- ── 5. Clock / FPS / MS / Dur (centre) ───────────────────────────────────
    if D.ibClock or D.ibFPS or D.ibMS or D.ibDur then
        local blk = GetOrMakeFrame("clockblock")
        if not blk._clk then
            blk._clk   = blk:CreateFontString(nil, "OVERLAY", "GameFontNormal")
            blk._stats = blk:CreateFontString(nil, "OVERLAY", "GameFontNormal")
            blk._clk:SetJustifyH("CENTER")
            blk._clk:SetPoint("TOP",    blk, "TOP",    0, 0)
            blk._stats:SetJustifyH("CENTER")
            blk._stats:SetPoint("BOTTOM", blk, "BOTTOM", 0, 0)
            blk:SetScript("OnClick", function()
                if CFLUtilSettings and CFLUtilSettings.ibClock then
                    CFLUtilSettings.ib24h = not (CFLUtilSettings.ib24h or false)
                    if RefreshClkBtns then RefreshClkBtns() end
                    CFL_ApplyHUDState()
                end
            end)
            blk:SetScript("OnEnter", function()
                if CFLUtilSettings and CFLUtilSettings.ibClock then
                    GameTooltip:SetOwner(blk, "ANCHOR_NONE")
                    GameTooltip:SetPoint("TOPLEFT", blk, "BOTTOMLEFT", 0, -24)
                    GameTooltip:SetText(L["TT_CLOCK"], 1, 1, 1)
                    GameTooltip:Show()
                end
            end)
            blk:SetScript("OnLeave", function() GameTooltip:Hide() end)
        end

        local clkCfg = D.ibClockCfg or {}
        if D.ibClock then
            pcall(function() blk._clk:SetFont(ResolveFont(clkCfg.font), clkCfg.size or 25, clkCfg.outline or "OUTLINE") end)
            blk._clk:SetTextColor(clkCfg.r or pr, clkCfg.g or pg, clkCfg.b or pb)
            blk._clk:Show()
        else
            pcall(function() blk._clk:SetText("") end); blk._clk:Hide()
        end

        local stCfg = D.ibFPScfg or {}
        if D.ibFPS or D.ibMS or D.ibDur then
            pcall(function() blk._stats:SetFont(ResolveFont(stCfg.font), stCfg.size or 14, stCfg.outline or "OUTLINE") end)
            blk._stats:SetTextColor(pr, pg, pb); blk._stats:Show()
        else
            pcall(function() blk._stats:SetText("") end); blk._stats:Hide()
        end

        local function UpdClk()
            if not (CFLUtilSettings and CFLUtilSettings.ibClock) then return end
            local h, m
            if (CFLUtilSettings or {}).ibLocalTime then
                local t = date("*t"); h, m = t.hour, t.min
            else
                h, m = GetGameTime()
            end
            if (CFLUtilSettings or {}).ib24h then
                blk._clk:SetText(string.format("%02d:%02d", h, m))
            else
                local sfx = h >= 12 and "PM" or "AM"
                h = h % 12; if h == 0 then h = 12 end
                blk._clk:SetText(string.format("%d:%02d %s", h, m, sfx))
            end
        end
        local function UpdStats()
            local D2 = CFLUtilSettings or {}; local parts = {}
            if D2.ibFPS then parts[#parts+1] = string.format("%d fps", math.floor(GetFramerate())) end
            if D2.ibMS  then local _,_,ms=GetNetStats(); parts[#parts+1]=string.format("%d ms",ms or 0) end
            if D2.ibDur then
                local cur,mx=0,0
                for s=0,19 do local c2,m2=GetInventoryItemDurability(s); if c2 and m2 and m2>0 then cur=cur+c2;mx=mx+m2 end end
                if mx>0 then
                    local pct=math.floor((cur/mx)*100)
                    parts[#parts+1]=pct<=20 and string.format("|cFFFF4444%d%% dur|r",pct) or string.format("%d%% dur",pct)
                else parts[#parts+1]="-- dur" end
            end
            blk._stats:SetText(table.concat(parts,"  "))
        end
        UpdClk(); UpdStats()
        ibTickers[#ibTickers+1] = C_Timer.NewTicker(5, UpdClk)
        ibTickers[#ibTickers+1] = C_Timer.NewTicker(2, UpdStats)

        local clkH = D.ibClock and (clkCfg.size or 25)+2 or 0
        local stH  = (D.ibFPS or D.ibMS or D.ibDur) and (stCfg.size or 14)+2 or 0
        -- Use font-size based width instead of GetStringWidth() which returns 0 at load time.
        -- Clock: "00:00 AM" at size 25 needs ~75px; stats: "000 fps  000 ms  100% dur" at 14 needs ~130px
        local clkFontSize = clkCfg.size or 25
        local stFontSize  = stCfg.size or 14
        -- "12:00 AM" at typical sizes needs ~3.5x font size; "23:59" needs ~2.8x.
        -- Use 3.5 to safely accommodate 12h format and any font.
        local clkTextW = D.ibClock and math.max(clkFontSize * 3.5, 70) or 0
        local stParts = 0
        if D.ibFPS then stParts = stParts + 1 end
        if D.ibMS  then stParts = stParts + 1 end
        if D.ibDur then stParts = stParts + 1 end
        local stTextW = stParts > 0 and math.max(stFontSize * 4.5 * stParts, 50) or 0
        clockW = math.max(clkTextW, stTextW, 60)
        clockH_val = math.max(clkH+stH, ICON)
        blk:SetSize(clockW, clockH_val)
        clockEl = blk
    end

    -- ── Right wing: Hearthstone (closest to clock) → Hero DC → Myth DC → Voidcore ──

    -- Resolve a binding label to its HS_ACTIONS entry
    local function HsEntry(label)
        if not label or label == "" then return nil end
        for _, entry in ipairs(HS_ACTIONS) do
            if entry.label == label then return entry end
        end
        return nil
    end

    -- Set SecureActionButtonTemplate attributes for one numbered click slot (1=left,2=right,3=middle)
    -- type="item" for hearthstones/toys; type="macro" with /use macrotext for pure spells.
    local function HsSetSlot(btn, num, label)
        local entry = HsEntry(label)
        if not entry or not entry.use or entry.use == "" then
            btn:SetAttribute("type"      .. num, nil)
            btn:SetAttribute("item"      .. num, nil)
            btn:SetAttribute("macrotext" .. num, nil)
            return
        end
        if entry.macro then
            -- Pure spells (e.g. Astral Recall) must go through macro /use
            btn:SetAttribute("type"      .. num, "macro")
            btn:SetAttribute("macrotext" .. num, "/use " .. entry.use)
            btn:SetAttribute("item"      .. num, nil)
        else
            btn:SetAttribute("type"      .. num, "item")
            btn:SetAttribute("item"      .. num, entry.use)
            btn:SetAttribute("macrotext" .. num, nil)
        end
    end

    if D.ibHearthstone then
        local hsCfg = D.ibHsCfg or {}
        -- Must be a named global frame for SecureActionButtonTemplate to work correctly
        if not ibEls["hearthstone"] then
            local btn = CreateFrame("Button", "CFLHearthstoneBtn", HUD, "SecureActionButtonTemplate")
            btn:SetSize(ICON, ICON)
            local tex = btn:CreateTexture(nil, "ARTWORK")
            tex:SetAllPoints(btn)
            tex:SetTexture("Interface\\Icons\\inv_garrison_hearthstone")
            btn._tex = tex
            local hl = btn:CreateTexture(nil, "HIGHLIGHT")
            hl:SetAllPoints(btn)
            hl:SetTexture("Interface\\Buttons\\ButtonHilight-Square")
            hl:SetBlendMode("ADD")
            btn:RegisterForClicks("AnyUp", "AnyDown")
            btn:HookScript("OnEnter", function(s)
                local cfg = CFLUtilSettings and CFLUtilSettings.ibHsCfg or {}
                GameTooltip:SetOwner(s, "ANCHOR_NONE")
                GameTooltip:SetPoint("TOPLEFT", s, "BOTTOMLEFT", 0, -24)
                GameTooltip:SetText(L["TT_HEARTHSTONE"], 0.65, 0.30, 1.00)
                GameTooltip:AddLine(L["Left-click: "]   .. (cfg.left   or HS_DEFAULT_LEFT),   0.82, 0.78, 0.90)
                GameTooltip:AddLine(L["Right-click: "]  .. (cfg.right  or HS_DEFAULT_RIGHT),  0.82, 0.78, 0.90)
                GameTooltip:AddLine(L["Middle-click: "] .. (cfg.middle or HS_DEFAULT_MIDDLE), 0.82, 0.78, 0.90)
                GameTooltip:Show()
            end)
            btn:HookScript("OnLeave", function() GameTooltip:Hide() end)
            ibEls["hearthstone"] = btn
        end
        local btn = ibEls["hearthstone"]
        if not InCombatLockdown() then
            HsSetSlot(btn, 1, hsCfg.left   or HS_DEFAULT_LEFT)
            HsSetSlot(btn, 2, hsCfg.right  or HS_DEFAULT_RIGHT)
            HsSetSlot(btn, 3, hsCfg.middle or HS_DEFAULT_MIDDLE)
        end
        pendingEls["hearthstone"] = { el = btn, w = ICON, h = ICON }
    end

    -- ── Currencies: Hero DC → Myth DC → Voidcore ─────────────────────────────
    local CURR_LIST = {
        { key="herodc",   id=CURRENCY_HERO_DAWNCREST, tip="Hero Dawncrest"    },
        { key="mythdc",   id=CURRENCY_MYTH_DAWNCREST,  tip="Myth Dawncrest"    },
        { key="voidcore", id=CURRENCY_VOIDCORE,         tip="Nebulous Voidcore" },
    }
    local showKey = { herodc="ibHeroDC", mythdc="ibMythDC", voidcore="ibVoidcore" }
    local cfgKey  = { herodc="ibHeroDCcfg", mythdc="ibMythDCcfg", voidcore="ibVoidcoreCfg" }

    for _, cur in ipairs(CURR_LIST) do
        if D[showKey[cur.key]] then
            -- Use GetOrMakeIconBtn so the frame is ICON x ICON with a texture fill,
            -- matching the left-side icon buttons exactly. The count badge overhangs
            -- to the bottom-right just like Friends/Party counters.
            local btn = GetOrMakeIconBtn("curr_"..cur.key, 0, ICON)
            if not btn._currReady then
                -- Texture is set each frame (currency icon from API)
                btn._count = btn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
                btn._count:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", 6, -3)
                btn._count:SetJustifyH("RIGHT")
                local captCur = cur
                btn:SetScript("OnEnter", function(s)
                    GameTooltip:SetOwner(s, "ANCHOR_NONE")
                    GameTooltip:SetPoint("TOPLEFT", s, "BOTTOMLEFT", 0, -24)
                    local info = C_CurrencyInfo.GetCurrencyInfo(captCur.id)
                    GameTooltip:SetText(info and info.name or captCur.tip, 1, 1, 1)
                    if info and info.maximumQuantity and info.maximumQuantity > 0 then
                        GameTooltip:AddLine(string.format("%d / %d", info.quantity, info.maximumQuantity), 0.8, 0.8, 0.8)
                    end
                    GameTooltip:Show()
                end)
                btn:SetScript("OnLeave", function() GameTooltip:Hide() end)
                btn._currReady = true
            end
            local info = C_CurrencyInfo.GetCurrencyInfo(cur.id)
            if info and info.iconFileID then btn._tex:SetTexture(info.iconFileID) end
            local cCfg = D[cfgKey[cur.key]] or {}
            pcall(function() btn._count:SetFont(ResolveFont(cCfg.font), cCfg.size or 14, cCfg.outline or "OUTLINE") end)
            btn._count:SetTextColor(cCfg.r or C.text[1], cCfg.g or C.text[2], cCfg.b or C.text[3])
            local captID = cur.id; local captBtn = btn
            local function UpdCurr()
                local ci = C_CurrencyInfo.GetCurrencyInfo(captID)
                captBtn._count:SetText(tostring(ci and ci.quantity or 0))
            end
            UpdCurr()
            ibTickers[#ibTickers+1] = C_Timer.NewTicker(10, UpdCurr)
            pendingEls[cur.key] = { el = btn, w = ICON, h = ICON }
        end
    end

    -- ── Assign pending elements to a wing, in the user's saved order ─────────
    for _, key in ipairs(CFL_NormalizedOrder()) do
        local pe = pendingEls[key]
        if pe then
            local side = (D.hudSide and D.hudSide[key]) or DEFAULT_HUD_SIDE[key] or "left"
            if side == "right" then
                QRgt(pe.el, pe.w, pe.h)
            else
                QLft(pe.el, pe.w, pe.h)
            end
        end
    end

    -- ── Final layout pass ─────────────────────────────────────────────────────
    local function sideWidth(list)
        local w = 0
        for i, e in ipairs(list) do w = w + e.w + (i < #list and GAP or 0) end
        return w
    end

    local leftW  = sideWidth(leftEls)
    local rightW = sideWidth(rightEls)
    local hasLeft  = #leftEls  > 0
    local hasRight = #rightEls > 0
    local hasClock = clockEl ~= nil

    -- Only add PAD_WING where there is actually content on both sides of the gap.
    -- This prevents phantom padding when wings are empty.
    local totalW = leftW
    if hasLeft  and hasClock then totalW = totalW + PAD_WING end
    if hasClock             then totalW = totalW + clockW    end
    if hasClock and hasRight then totalW = totalW + PAD_WING end
    if not hasClock and hasLeft and hasRight then totalW = totalW + PAD_WING end
    totalW = totalW + rightW

    -- Round finalW to whole pixel.
    local finalW = math.floor(math.max(totalW, 40) + 0.5)

    local uiW = UIParent:GetWidth()
    local uiH = UIParent:GetHeight()
    if uiW < 1 then uiW = 1280 end
    if uiH < 1 then uiH = 720  end

    HUD:SetWidth(finalW)
    HUD:ClearAllPoints()

    if D.hudPX ~= nil then
        -- Saved position: BOTTOMLEFT with absolute screen coords.
        -- UIParent BOTTOMLEFT is at screen origin (0, 0), so offset = exact pixel position.
        -- No UIParent width or height involved — pixel-perfect at every screen position.
        local left   = math.floor(D.hudPX + 0.5)
        local bottom = math.floor(D.hudPY + 0.5)
        HUD:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
    else
        -- No saved position yet (first install or after Reset) — centre near the top.
        --
        -- IMPORTANT: we centre on the CLOCK, not on the HUD frame as a whole.
        -- The clock sits inside the frame at offset (leftW + PAD_WING), so centring the
        -- frame itself only puts the clock at true screen-centre when the left wing
        -- (friends/party/screenshot) and right wing (hearthstone/currencies) happen to
        -- be exactly the same width. In practice they usually aren't, which used to
        -- push the clock visibly off-centre after a reset. Solving for the clock's
        -- own midpoint instead makes it land dead-centre regardless of wing widths.
        local clockOffsetInHUD
        if hasClock then
            clockOffsetInHUD = (hasLeft and (leftW + PAD_WING) or 0) + (clockW / 2)
        else
            clockOffsetInHUD = finalW / 2  -- no clock enabled: fall back to centring the frame
        end
        local left   = math.floor((uiW / 2) - clockOffsetInHUD + 0.5)
        local bottom = math.floor(uiH - 30 - HUD:GetHeight() + 0.5)
        HUD:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
        -- Persist so reload uses the same pixel anchor instead of recalculating.
        CFLUtilSettings.hudPX = left
        CFLUtilSettings.hudPY = bottom
    end

    -- Compute x offsets for clock and right wing based on what's present.
    local clockX
    if hasLeft and hasClock then
        clockX = leftW + PAD_WING          -- left wing then gap then clock
    else
        clockX = leftW                     -- no gap needed (no left wing, or no clock)
    end

    local rightX
    if hasClock and hasRight then
        rightX = clockX + clockW + PAD_WING    -- clock then gap then right wing
    elseif hasLeft and hasRight then
        rightX = leftW + PAD_WING              -- left wing then gap then right wing (no clock)
    else
        rightX = leftW + clockW                -- right wing starts immediately after whatever exists
    end

    -- Left wing
    local xL = 0
    for i, e in ipairs(leftEls) do
        if not (inCombat and e.el == hsBtn) then
            e.el:SetSize(e.w, e.h)
            e.el:ClearAllPoints()
            e.el:SetPoint("LEFT", HUD, "LEFT", xL, 0)
            e.el:Show()
        end
        xL = xL + e.w + (i < #leftEls and GAP or 0)
    end

    -- Clock
    if clockEl then
        clockEl:ClearAllPoints()
        clockEl:SetPoint("LEFT", HUD, "LEFT", clockX, 0)
        clockEl:SetSize(clockW, clockH_val)
        clockEl:Show()
    end

    -- Right wing
    local xR = rightX
    for i, e in ipairs(rightEls) do
        if not (inCombat and e.el == hsBtn) then
            e.el:SetSize(e.w, e.h)
            e.el:ClearAllPoints()
            e.el:SetPoint("LEFT", HUD, "LEFT", xR, 0)
            e.el:Show()
        end
        xR = xR + e.w + (i < #rightEls and GAP or 0)
    end

    -- If the pixel-nudge border is currently showing, keep it sized to the
    -- (possibly just-changed) max width rather than waiting for the next
    -- click-to-arm.
    if hudSelected and CFL_ResizeSelectBorder then CFL_ResizeSelectBorder() end
end
-- ============================================================
--  UTILITIES — Settings UI
-- ============================================================
UtilFrame = ContentFrame()
local UP_U         = PAD
local statWidgets = {}   

local function RefreshWidgetDisplay(cfgKey)
    local D = CFLUtilSettings
    if not D then return end
    local w   = statWidgets[cfgKey]
    local cfg = D[cfgKey]
    if not (w and cfg) then return end
    if w.ddBtn  and w.ddBtn._lbl  then w.ddBtn._lbl:SetText(cfg.font or "Expressway") end
    if w.sizeEB then w.sizeEB:SetTextSafe(tostring(cfg.size or 12)) end
    if w.swatch and w.swatch._tex then
        w.swatch._tex:SetColorTexture(cfg.r or C.text[1], cfg.g or C.text[2], cfg.b or C.text[3], 1)
    end
    if w.hexBox then
        w.hexBox:SetText(string.format("%02X%02X%02X",
            math.floor((cfg.r or C.text[1])*255),
            math.floor((cfg.g or C.text[2])*255),
            math.floor((cfg.b or C.text[3])*255)))
    end
    if w.outlineDD and w.outlineDD._lbl then
        w.outlineDD._lbl:SetText(OutlineLabelFor(cfg.outline))
    end
end

local sharedDD = CreateFrame("Frame", "CFLFontDropdown", UIParent, "BackdropTemplate")
sharedDD:SetFrameStrata("TOOLTIP")
sharedDD:SetWidth(200)
sharedDD:Hide()
sharedDD:EnableMouse(true)
Backdrop(sharedDD, C.sidebar, C.border)

local ddCloseBtn = CreateFrame("Button", nil, UIParent)
ddCloseBtn:SetAllPoints(UIParent)
ddCloseBtn:SetFrameStrata("HIGH")
ddCloseBtn:EnableMouse(true)
ddCloseBtn:Hide()
ddCloseBtn:SetScript("OnClick", function() sharedDD:Hide(); ddCloseBtn:Hide() end)

local ddSF = CreateFrame("ScrollFrame", nil, sharedDD, "UIPanelScrollFrameTemplate")
ddSF:SetPoint("TOPLEFT",     sharedDD, "TOPLEFT",     2, -2)
ddSF:SetPoint("BOTTOMRIGHT", sharedDD, "BOTTOMRIGHT", -20, 2)
local ddSC = CreateFrame("Frame", nil, ddSF)
ddSC:SetWidth(1); ddSC:SetHeight(1)
ddSF:SetScrollChild(ddSC)
sharedDD:SetFrameLevel(ddCloseBtn:GetFrameLevel() + 1)

local function MakeUtilEditBox(parent, width, anchorPt, anchorTo, relPt, ox, oy)
    local eb = CreateFrame("EditBox", nil, parent, "BackdropTemplate")
    eb:SetHeight(32)
    eb:SetWidth(width)
    eb:SetPoint(anchorPt, anchorTo, relPt, ox, oy)
    Backdrop(eb, C.inputBg, C.border)
    eb:SetFontObject("GameFontNormal")
    eb:SetTextColor(C.text[1], C.text[2], C.text[3])
    eb:SetTextInsets(10, 10, 0, 0)
    eb:SetAutoFocus(false)
    eb:SetMaxLetters(256)
    eb:SetScript("OnEditFocusGained", function(self) self:SetBackdropBorderColor(C.accent[1], C.accent[2], C.accent[3], 0.9) end)
    eb:SetScript("OnEditFocusLost", function(self) self:SetBackdropBorderColor(C.border[1], C.border[2], C.border[3], 1) end)
    eb:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
    eb:SetScript("OnEnterPressed",  function(self) self:ClearFocus() end)
    return eb
end

local function MakeFontDropdown(parent, cfgKey, onApply, anchorFrame, ax, ay)
    local btn = CreateFrame("Button", nil, parent, "BackdropTemplate")
    btn:SetSize(160, 24)
    btn:SetPoint("LEFT", anchorFrame, "RIGHT", ax or 8, ay or 0)
    Backdrop(btn, C.inputBg, C.border)
    local btnLbl = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    btnLbl:SetAllPoints(btn); btnLbl:SetJustifyH("CENTER")
    btnLbl:SetTextColor(C.text[1], C.text[2], C.text[3])
    btnLbl:SetText("Expressway")
    btn._lbl = btnLbl
    btn:SetScript("OnClick", function(self)
        if sharedDD:IsShown() and sharedDD._owner == self then
            sharedDD:Hide(); ddCloseBtn:Hide(); return
        end
        local LSM = LibStub and LibStub("LibSharedMedia-3.0", true)
        local fonts = {}; local seen = {}
        for _, n in ipairs({"GameFontNormal","GameFontNormalLarge","GameFontNormalSmall"}) do
            if not seen[n] then seen[n]=true; fonts[#fonts+1]=n end
        end
        if LSM then
            for n in pairs(LSM:HashTable("font")) do
                if not seen[n] then seen[n]=true; fonts[#fonts+1]=n end
            end
        end
        table.sort(fonts)
        for _, c in ipairs({ddSC:GetChildren()}) do c:Hide(); c:SetParent(nil) end
        local ITEM_H = 22
        sharedDD:SetHeight(math.min(#fonts,10)*ITEM_H+4)
        ddSC:SetWidth(sharedDD:GetWidth()-20)
        ddSC:SetHeight(#fonts*ITEM_H)
        local D = CFLUtilSettings
        local currentFont = (D and D[cfgKey] and D[cfgKey].font) or ""
        for i, name in ipairs(fonts) do
            local row = CreateFrame("Button", nil, ddSC)
            row:SetHeight(ITEM_H)
            row:SetPoint("TOPLEFT",  ddSC, "TOPLEFT",  0, -(i-1)*ITEM_H)
            row:SetPoint("TOPRIGHT", ddSC, "TOPRIGHT", 0, -(i-1)*ITEM_H)
            local rl = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            rl:SetText(name); rl:SetPoint("LEFT", row, "LEFT", 6, 0)
            local isC = (name == currentFont)
            rl:SetTextColor(isC and C.accent[1] or C.text[1], isC and C.accent[2] or C.text[2], isC and C.accent[3] or C.text[3])
            row:SetScript("OnEnter", function() rl:SetTextColor(C.accent[1],C.accent[2],C.accent[3]) end)
            row:SetScript("OnLeave", function()
                local d2 = CFLUtilSettings and CFLUtilSettings[cfgKey]
                local c2 = d2 and (name == d2.font)
                rl:SetTextColor(c2 and C.accent[1] or C.text[1], c2 and C.accent[2] or C.text[2], c2 and C.accent[3] or C.text[3])
            end)
            row:SetScript("OnClick", function()
                if CFLUtilSettings and CFLUtilSettings[cfgKey] then CFLUtilSettings[cfgKey].font = name end
                btnLbl:SetText(name)
                if onApply then onApply() end
                sharedDD:Hide(); ddCloseBtn:Hide()
            end)
        end
        sharedDD:ClearAllPoints()
        sharedDD:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, -2)
        sharedDD._owner = self; sharedDD:Show(); ddCloseBtn:Show()
        ddSF:SetVerticalScroll(0)
    end)
    return btn
end

local function MakeOutlineDropdown(parent, cfgKey, onApply, anchorFrame, ax, ay)
    local btn = CreateFrame("Button", nil, parent, "BackdropTemplate")
    btn:SetSize(120, 24)
    btn:SetPoint("LEFT", anchorFrame, "RIGHT", ax or 8, ay or 0)
    Backdrop(btn, C.inputBg, C.border)
    local btnLbl = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    btnLbl:SetAllPoints(btn); btnLbl:SetJustifyH("CENTER")
    btnLbl:SetTextColor(C.text[1], C.text[2], C.text[3])
    btnLbl:SetText("None")
    btn._lbl = btnLbl
    btn:SetScript("OnClick", function(self)
        if sharedDD:IsShown() and sharedDD._owner == self then
            sharedDD:Hide(); ddCloseBtn:Hide(); return
        end
        for _, c in ipairs({ddSC:GetChildren()}) do c:Hide(); c:SetParent(nil) end
        local ITEM_H = 22; local count = #OUTLINE_OPTIONS
        sharedDD:SetHeight(math.min(count,10)*ITEM_H+4)
        ddSC:SetWidth(sharedDD:GetWidth()-20); ddSC:SetHeight(count*ITEM_H)
        local D = CFLUtilSettings
        local currentVal = (D and D[cfgKey] and D[cfgKey].outline) or ""
        for i, opt in ipairs(OUTLINE_OPTIONS) do
            local row = CreateFrame("Button", nil, ddSC)
            row:SetHeight(ITEM_H)
            row:SetPoint("TOPLEFT",  ddSC, "TOPLEFT",  0, -(i-1)*ITEM_H)
            row:SetPoint("TOPRIGHT", ddSC, "TOPRIGHT", 0, -(i-1)*ITEM_H)
            local rl = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            rl:SetText(opt.label); rl:SetPoint("LEFT", row, "LEFT", 6, 0)
            local isC = (opt.value == currentVal)
            rl:SetTextColor(isC and C.accent[1] or C.text[1], isC and C.accent[2] or C.text[2], isC and C.accent[3] or C.text[3])
            row:SetScript("OnEnter", function() rl:SetTextColor(C.accent[1],C.accent[2],C.accent[3]) end)
            row:SetScript("OnLeave", function()
                local d2 = CFLUtilSettings and CFLUtilSettings[cfgKey]
                local c2 = d2 and (opt.value == (d2.outline or ""))
                rl:SetTextColor(c2 and C.accent[1] or C.text[1], c2 and C.accent[2] or C.text[2], c2 and C.accent[3] or C.text[3])
            end)
            row:SetScript("OnClick", function()
                if CFLUtilSettings and CFLUtilSettings[cfgKey] then CFLUtilSettings[cfgKey].outline = opt.value end
                btnLbl:SetText(opt.label)
                if onApply then onApply() end
                sharedDD:Hide(); ddCloseBtn:Hide()
            end)
        end
        sharedDD:ClearAllPoints()
        sharedDD:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, -2)
        sharedDD._owner = self; sharedDD:Show(); ddCloseBtn:Show()
        ddSF:SetVerticalScroll(0)
    end)
    return btn
end

local function MakeSizeSpinner(parent, cfgKey, onApply, anchorFrame, ax, ay)
    local eb = MakeUtilEditBox(parent, 44, "LEFT", anchorFrame, "RIGHT", ax or 8, ay or 0)
    eb:SetText("12"); eb:SetMaxLetters(3)
    local _sup = false
    local function Apply()
        if _sup then return end
        local v = tonumber(eb:GetText())
        local cfg = CFLUtilSettings and CFLUtilSettings[cfgKey]
        if v and v >= 6 and v <= 48 then
            if cfg then cfg.size = v end
            if onApply then onApply() end
        else
            _sup = true; eb:SetText(tostring(cfg and cfg.size or 12)); _sup = false
        end
    end
    eb.SetTextSafe = function(self, val) _sup=true; self:SetText(val); _sup=false end
    eb:SetScript("OnEnterPressed", function(self) self:ClearFocus(); Apply() end)
    eb:SetScript("OnEditFocusLost", Apply)
    return eb
end

local function MakeColorPicker(parent, cfgKey, onApply, anchorFrame, ax, ay)
    local swatch = CreateFrame("Button", nil, parent, "BackdropTemplate")
    swatch:SetSize(24, 24)
    swatch:SetPoint("LEFT", anchorFrame, "RIGHT", ax or 8, ay or 0)
    Backdrop(swatch, {0,0,0,0}, C.border)
    local swTex = swatch:CreateTexture(nil, "ARTWORK")
    swTex:SetPoint("TOPLEFT", swatch, "TOPLEFT", 2, -2)
    swTex:SetPoint("BOTTOMRIGHT", swatch, "BOTTOMRIGHT", -2, 2)
    swTex:SetColorTexture(C.text[1], C.text[2], C.text[3], 1)
    swatch._tex = swTex
    swatch:SetScript("OnEnter", function(self) self:SetBackdropBorderColor(C.accent[1],C.accent[2],C.accent[3],1) end)
    swatch:SetScript("OnLeave", function(self) self:SetBackdropBorderColor(C.border[1],C.border[2],C.border[3],1) end)
    local hexBox = MakeUtilEditBox(parent, 72, "LEFT", swatch, "RIGHT", 6, 0)
    hexBox:SetText("D1C7E6"); hexBox:SetMaxLetters(6)
    
    local function ApplyHex()
        local hex = hexBox:GetText():match("^%s*(.-)%s*$"):upper()
        if #hex == 6 then
            local r=tonumber(hex:sub(1,2),16); local g=tonumber(hex:sub(3,4),16); local b=tonumber(hex:sub(5,6),16)
            if r and g and b then
                local D = CFLUtilSettings
                if D and D[cfgKey] then D[cfgKey].r=r/255; D[cfgKey].g=g/255; D[cfgKey].b=b/255 end
                swTex:SetColorTexture(r/255,g/255,b/255,1)
                if onApply then onApply() end
            end
        end
    end
    hexBox:SetScript("OnEnterPressed", ApplyHex)
    hexBox:SetScript("OnEditFocusLost", ApplyHex)
    
    swatch:SetScript("OnClick", function()
        local D = CFLUtilSettings; local cfg = D and D[cfgKey]
        local r0 = cfg and cfg.r or C.text[1]
        local g0 = cfg and cfg.g or C.text[2]
        local b0 = cfg and cfg.b or C.text[3]
        
        ColorPickerFrame:SetupColorPickerAndShow({
            r = r0, g = g0, b = b0,
            hasOpacity = false,
            swatchFunc = function()
                local r, g, b = ColorPickerFrame:GetColorRGB()
                if r then
                    if CFLUtilSettings and CFLUtilSettings[cfgKey] then
                        CFLUtilSettings[cfgKey].r, CFLUtilSettings[cfgKey].g, CFLUtilSettings[cfgKey].b = r, g, b
                    end
                    swTex:SetColorTexture(r, g, b, 1)
                    hexBox:SetText(string.format("%02X%02X%02X", math.floor(r*255), math.floor(g*255), math.floor(b*255)))
                    if onApply then onApply() end
                end
            end,
            cancelFunc = function()
                if CFLUtilSettings and CFLUtilSettings[cfgKey] then
                    CFLUtilSettings[cfgKey].r, CFLUtilSettings[cfgKey].g, CFLUtilSettings[cfgKey].b = r0, g0, b0
                end
                swTex:SetColorTexture(r0, g0, b0, 1)
                hexBox:SetText(string.format("%02X%02X%02X", math.floor(r0*255), math.floor(g0*255), math.floor(b0*255)))
                if onApply then onApply() end
            end
        })
    end)
    return swatch, hexBox
end

local function MakeCheckbox(parent, label, yOff, onToggle)
    local cb = CreateFrame("CheckButton", nil, parent, "UICheckButtonTemplate")
    cb:SetSize(24, 24)
    cb:SetPoint("TOPLEFT", parent, "TOPLEFT", UP_U, -yOff)
    cb:SetScript("OnClick", function(self)
        self._checked = self:GetChecked()
        if onToggle then onToggle(self._checked) end
    end)
    local lbl = parent:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    lbl:SetText(label)
    lbl:SetTextColor(C.text[1], C.text[2], C.text[3])
    lbl:SetPoint("LEFT", cb, "RIGHT", 4, 0)
    cb.SetCheckedState = function(self, v) self._checked=v; self:SetChecked(v) end
    return cb, lbl
end

-- Stripped HUDTextBlock for gold/currency: font + size only, no color/outline pickers.
local function HUDTextBlockFontOnly(scrollChild, yBase, label, cfgKey, showKey)
    local function onApply()
        if not CFLUtilSettings then return end
        CFL_ApplyHUDState()
        RefreshWidgetDisplay(cfgKey)
    end

    local hdr = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    hdr:SetText(label); hdr:SetTextColor(C.text[1], C.text[2], C.text[3])
    hdr:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", UP_U, -yBase)

    local fontLbl = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    fontLbl:SetText(L["Font:"]); fontLbl:SetTextColor(C.sub[1],C.sub[2],C.sub[3])
    fontLbl:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", UP_U, -(yBase + 22))

    local ddBtn = MakeFontDropdown(scrollChild, cfgKey, onApply, fontLbl, 6, 0)

    local sizeLbl = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    sizeLbl:SetText(L["Size:"]); sizeLbl:SetTextColor(C.sub[1],C.sub[2],C.sub[3])
    sizeLbl:SetPoint("LEFT", ddBtn, "RIGHT", 14, 0)

    local sizeEB = MakeSizeSpinner(scrollChild, cfgKey, onApply, sizeLbl, 6, 0)

    statWidgets[cfgKey] = {
        fontLbl=fontLbl, ddBtn=ddBtn,
        sizeLbl=sizeLbl, sizeEB=sizeEB, showKey=showKey,
    }
    return yBase + 54
end

local function HUDTextBlock(scrollChild, yBase, label, cfgKey, showKey, defaultCfg)
    local function onApply()
        if not CFLUtilSettings then return end
        CFL_ApplyHUDState()
        RefreshWidgetDisplay(cfgKey)
    end

    local hdr = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    hdr:SetText(label); hdr:SetTextColor(C.text[1], C.text[2], C.text[3])
    hdr:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", UP_U, -yBase)

    local fontLbl = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    fontLbl:SetText(L["Font:"]); fontLbl:SetTextColor(C.sub[1],C.sub[2],C.sub[3])
    fontLbl:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", UP_U, -(yBase + 22))

    local ddBtn = MakeFontDropdown(scrollChild, cfgKey, onApply, fontLbl, 6, 0)
    local sizeLbl = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    sizeLbl:SetText(L["Size:"]); sizeLbl:SetTextColor(C.sub[1],C.sub[2],C.sub[3])
    sizeLbl:SetPoint("LEFT", ddBtn, "RIGHT", 14, 0)

    local sizeEB = MakeSizeSpinner(scrollChild, cfgKey, onApply, sizeLbl, 6, 0)
    local colorLbl = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    colorLbl:SetText(L["Color:"]); colorLbl:SetTextColor(C.sub[1],C.sub[2],C.sub[3])
    colorLbl:SetPoint("LEFT", sizeEB, "RIGHT", 14, 0)

    local swatch, hexBox = MakeColorPicker(scrollChild, cfgKey, onApply, colorLbl, 6, 0)
    local outlineDD = MakeOutlineDropdown(scrollChild, cfgKey, onApply, hexBox, 10, 0)

    statWidgets[cfgKey] = {
        fontLbl=fontLbl, ddBtn=ddBtn,
        sizeLbl=sizeLbl, sizeEB=sizeEB, colorLbl=colorLbl,
        swatch=swatch, hexBox=hexBox, showKey=showKey, outlineDD=outlineDD,
    }
    return yBase + 54
end

-- Forward declarations for scale slider/editbox (created later in the UI build)
local scaleSlider, scaleEB
local _scaleSup = false

function CFL_RefreshUtilUI()
    local D = CFLUtilSettings
    if not D then return end

    for cfgKey, w in pairs(statWidgets) do
        if cfgKey:sub(1,1) ~= "_" and w then
            RefreshWidgetDisplay(cfgKey)
            if w.cb then w.cb:SetCheckedState(D[w.showKey] or false) end
        end
    end

    local ibKeyMap = {
        _ibShowCB  = "ibShow",     _ibGold    = "ibGold",
        _ibFriends = "ibFriends",  _ibClock   = "ibClock",
        _ibFPS     = "ibFPS",      _ibMS      = "ibMS",
        _ibDur     = "ibDur",      _ibParty   = "ibParty",
        _ibSS      = "ibScreenshot", _ibHeroDC = "ibHeroDC",
        _ibMythDC  = "ibMythDC",   _ibVoidcore = "ibVoidcore",
        _ibLockCB  = "hudLocked",  _ibHearth   = "ibHearthstone",
    }
    for wKey, dKey in pairs(ibKeyMap) do
        local w = statWidgets[wKey]
        if w and w.cb then w.cb:SetChecked(D[dKey] or false) end
    end

    if RefreshClkBtns then RefreshClkBtns() end
    -- Refresh hearthstone binding dropdowns
    local hsCfg = D.ibHsCfg or {}
    local hsDefaults = { left = HS_DEFAULT_LEFT, right = HS_DEFAULT_RIGHT, middle = HS_DEFAULT_MIDDLE }
    for _, wKey in ipairs({"_hsLeft","_hsRight","_hsMiddle"}) do
        local w = statWidgets[wKey]
        if w and w._hsDD and w._hsDD._lbl then
            local val = hsCfg[w._bindKey]
            if not val or val == "" then val = hsDefaults[w._bindKey] end
            w._hsDD._lbl:SetText(val)
        end
    end
    if scaleSlider then
        local v = D.hudScale or 1.0
        _scaleSup = true
        scaleSlider:SetValue(v)
        if scaleEB then scaleEB:SetText(string.format("%.2f", v)) end
        _scaleSup = false
    end
    if RelayoutOrderRows then RelayoutOrderRows() end
end

local utilScroll = CreateFrame("ScrollFrame", nil, UtilFrame, "UIPanelScrollFrameTemplate")
utilScroll:SetPoint("TOPLEFT",     UtilFrame, "TOPLEFT",     0, 0)
utilScroll:SetPoint("BOTTOMRIGHT", UtilFrame, "BOTTOMRIGHT", -22, 0)

local utilSC = CreateFrame("Frame", nil, utilScroll)
utilSC:SetWidth(FRAME_W - SIDEBAR_W - 44)
utilSC:SetHeight(1)  
utilScroll:SetScrollChild(utilSC)

local function SLabel(text, yOff, font, r, g, b)
    local fs = utilSC:CreateFontString(nil, "OVERLAY", font or "GameFontNormal")
    fs:SetText(text)
    fs:SetTextColor(r or C.text[1], g or C.text[2], b or C.text[3])
    fs:SetJustifyH("LEFT")
    fs:SetPoint("TOPLEFT", utilSC, "TOPLEFT", UP_U, -yOff)
    return fs
end

local function SSep(yOff, alpha)
    local t = Tex(utilSC, C.border[1], C.border[2], C.border[3], alpha or 0.8)
    t:SetHeight(1)
    t:SetPoint("TOPLEFT",  utilSC, "TOPLEFT",  UP_U,  -yOff)
    t:SetPoint("TOPRIGHT", utilSC, "TOPRIGHT", -UP_U-20, -yOff)
    return t
end

SLabel(L["HUD"], 16, "GameFontNormalLarge", C.accent[1], C.accent[2], C.accent[3])
SSep(40, 0.3)
SLabel(L["HUD_DESC"], 50, "GameFontNormal", C.sub[1], C.sub[2], C.sub[3])
SSep(72, 0.3)

local hudShowCB = MakeCheckbox(utilSC, L["Show HUD"], 0, function(checked)
    if CFLUtilSettings then CFLUtilSettings.ibShow = checked end
    CFL_ApplyHUDState()
end)
hudShowCB:ClearAllPoints()
hudShowCB:SetPoint("TOPLEFT", utilSC, "TOPLEFT", UP_U, -84)
statWidgets["_ibShowCB"] = { cb = hudShowCB, showKey = "ibShow" }

local TOGGLE_Y  = 116
local TOGGLE_Y2 = TOGGLE_Y + 28   -- second row of checkboxes

local function IBToggleSC(label, key, xOff, row)
    local yOff = (row == 2) and TOGGLE_Y2 or TOGGLE_Y
    local cb = CreateFrame("CheckButton", nil, utilSC, "UICheckButtonTemplate")
    cb:SetSize(20, 20)
    cb:SetPoint("TOPLEFT", utilSC, "TOPLEFT", UP_U + xOff, -yOff)
    cb:SetScript("OnClick", function(self)
        if CFLUtilSettings then CFLUtilSettings[key] = self:GetChecked() end
        CFL_ApplyHUDState()
    end)
    cb.SetCheckedState = function(self, v) self:SetChecked(v) end
    local lbl = utilSC:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    lbl:SetText(label); lbl:SetTextColor(C.text[1],C.text[2],C.text[3])
    lbl:SetPoint("LEFT", cb, "RIGHT", 2, 0)
    return cb
end

local togLbl = utilSC:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
togLbl:SetText(L["Show:"]); togLbl:SetTextColor(C.sub[1],C.sub[2],C.sub[3])
togLbl:SetPoint("TOPLEFT", utilSC, "TOPLEFT", UP_U, -(TOGGLE_Y + 2))

-- Row 1: Currency, Friends, Clock, FPS, MS, Durability
local ibCB_gold    = IBToggleSC(L["Currency"],    "ibGold",        48, 1)
local ibCB_friends = IBToggleSC(L["Friends"],     "ibFriends",    130, 1)
local ibCB_clock   = IBToggleSC(L["Clock"],       "ibClock",      210, 1)
local ibCB_fps     = IBToggleSC(L["FPS"],         "ibFPS",        278, 1)
local ibCB_ms      = IBToggleSC(L["MS"],          "ibMS",         322, 1)
local ibCB_dur     = IBToggleSC(L["Durability"],  "ibDur",        366, 1)
-- Row 2: Party, Screenshot, Hero DC, Myth DC, Voidcore
local ibCB_party   = IBToggleSC(L["Party"],       "ibParty",       48, 2)
local ibCB_ss      = IBToggleSC(L["Screenshot"],  "ibScreenshot", 118, 2)
local ibCB_herodc  = IBToggleSC(L["Hero DC"],     "ibHeroDC",     222, 2)
local ibCB_mythdc  = IBToggleSC(L["Myth DC"],     "ibMythDC",     302, 2)
local ibCB_voidcore= IBToggleSC(L["Voidcore"],    "ibVoidcore",   382, 2)
local ibCB_hearth  = IBToggleSC(L["Hearthstone"], "ibHearthstone",472, 2)

statWidgets["_ibGold"]     = { cb = ibCB_gold,     showKey = "ibGold"      }
statWidgets["_ibFriends"]  = { cb = ibCB_friends,  showKey = "ibFriends"   }
statWidgets["_ibClock"]    = { cb = ibCB_clock,     showKey = "ibClock"    }
statWidgets["_ibFPS"]      = { cb = ibCB_fps,       showKey = "ibFPS"      }
statWidgets["_ibMS"]       = { cb = ibCB_ms,        showKey = "ibMS"       }
statWidgets["_ibDur"]      = { cb = ibCB_dur,       showKey = "ibDur"      }
statWidgets["_ibParty"]    = { cb = ibCB_party,     showKey = "ibParty"    }
statWidgets["_ibSS"]       = { cb = ibCB_ss,        showKey = "ibScreenshot"}
statWidgets["_ibHeroDC"]   = { cb = ibCB_herodc,    showKey = "ibHeroDC"   }
statWidgets["_ibMythDC"]   = { cb = ibCB_mythdc,    showKey = "ibMythDC"   }
statWidgets["_ibVoidcore"] = { cb = ibCB_voidcore,  showKey = "ibVoidcore" }
statWidgets["_ibHearth"]   = { cb = ibCB_hearth,    showKey = "ibHearthstone" }

SSep(170, 0.3)
SLabel(L["Text Style"], 182, "GameFontNormalLarge", C.accent[1], C.accent[2], C.accent[3])
SLabel(L["TEXT_STYLE_DESC"], 206, "GameFontNormal", C.sub[1], C.sub[2], C.sub[3])
SSep(228, 0.3)

local nextY = 240
nextY = HUDTextBlockFontOnly(utilSC, nextY, L["Currency_lbl"],     "ibGoldCfg",    "ibGold")
SSep(nextY - 8, 0.2)
nextY = HUDTextBlock(utilSC, nextY, L["Friends count"],  "ibFriendsCfg", "ibFriends",  {r=C.text[1],g=C.text[2],b=C.text[3],size=12,outline=""})
SSep(nextY - 8, 0.2)
nextY = HUDTextBlock(utilSC, nextY, L["Clock_lbl"],          "ibClockCfg",   "ibClock",    {r=C.accent[1],g=C.accent[2],b=C.accent[3],size=25,outline="OUTLINE"})
SSep(nextY - 8, 0.2)
nextY = HUDTextBlock(utilSC, nextY, L["Stats (FPS / MS / Dur)"], "ibFPScfg", "ibFPS",      {r=C.text[1],g=C.text[2],b=C.text[3],size=14,outline="OUTLINE"})
SSep(nextY - 8, 0.2)
nextY = HUDTextBlock(utilSC, nextY, L["Party count"],    "ibPartyCfg",   "ibParty",    {r=C.text[1],g=C.text[2],b=C.text[3],size=12,outline=""})
SSep(nextY - 8, 0.2)
nextY = HUDTextBlock(utilSC, nextY, L["Hero Dawncrest"], "ibHeroDCcfg",  "ibHeroDC",   {r=C.text[1],g=C.text[2],b=C.text[3],size=14,outline="OUTLINE"})
SSep(nextY - 8, 0.2)
nextY = HUDTextBlock(utilSC, nextY, L["Myth Dawncrest"], "ibMythDCcfg",  "ibMythDC",   {r=C.text[1],g=C.text[2],b=C.text[3],size=14,outline="OUTLINE"})
SSep(nextY - 8, 0.2)
nextY = HUDTextBlock(utilSC, nextY, L["Nebulous Voidcore"],"ibVoidcoreCfg","ibVoidcore",{r=C.text[1],g=C.text[2],b=C.text[3],size=14,outline="OUTLINE"})

SSep(nextY + 4, 0.3)
SLabel(L["Hearthstone Button"], nextY + 16, "GameFontNormalLarge", C.accent[1], C.accent[2], C.accent[3])
SSep(nextY + 40, 0.3)
SLabel(L["HS_BUTTON_DESC"], nextY + 52, "GameFontNormal", C.sub[1], C.sub[2], C.sub[3])

-- HS dropdowns start at nextY+74 with 30px between each row
local HS_BASE_Y = nextY + 74

local HS_LABEL_W = 100  -- fixed column width for bind labels so all dropdowns align
local function MakeHsDropdown(parent, bindKey, yOff)
    local rowLbl = utilSC:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    local bindNames = { left = L["Left-click:"], right = L["Right-click:"], middle = L["Middle-click:"] }
    rowLbl:SetText(bindNames[bindKey]); rowLbl:SetTextColor(C.sub[1], C.sub[2], C.sub[3])
    rowLbl:SetPoint("TOPLEFT", utilSC, "TOPLEFT", UP_U, -(HS_BASE_Y + yOff))
    rowLbl:SetWidth(HS_LABEL_W)  -- fixed width so dropdown always starts at same X

    local ddBtn = CreateFrame("Button", nil, utilSC, "BackdropTemplate")
    ddBtn:SetSize(260, 22)
    -- Anchor to a fixed X offset from utilSC so all three rows are perfectly aligned
    ddBtn:SetPoint("TOPLEFT", utilSC, "TOPLEFT", UP_U + HS_LABEL_W + 8, -(HS_BASE_Y + yOff))
    Backdrop(ddBtn, C.inputBg, C.border)
    local ddLbl = ddBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    ddLbl:SetAllPoints(ddBtn); ddLbl:SetJustifyH("CENTER")
    ddLbl:SetTextColor(C.text[1], C.text[2], C.text[3])

    -- Default labels shown before settings load (will be overwritten by CFL_RefreshUtilUI)
    local defaults = { left = HS_DEFAULT_LEFT, right = HS_DEFAULT_RIGHT, middle = HS_DEFAULT_MIDDLE }
    local function GetCurrent()
        local cfg = CFLUtilSettings and CFLUtilSettings.ibHsCfg
        if cfg and cfg[bindKey] and cfg[bindKey] ~= "" then return cfg[bindKey] end
        return defaults[bindKey]
    end
    ddLbl:SetText(GetCurrent())
    ddBtn._lbl = ddLbl
    ddBtn._getCurrent = GetCurrent

    ddBtn:SetScript("OnClick", function(self)
        if sharedDD:IsShown() and sharedDD._owner == self then
            sharedDD:Hide(); ddCloseBtn:Hide(); return
        end
        for _, c in ipairs({ddSC:GetChildren()}) do c:Hide(); c:SetParent(nil) end
        -- Build a sorted display list: pinned top 3, then A-Z, then pinned bottom 3
        local sorted = {}
        local pinTop    = { "Hearthstone", "Garrison Hearthstone", "Dalaran Hearthstone" }
        local pinBottom = { "Personal Key to the Arcantina", "Astral Recall", "Nothing (disabled)" }
        local pinTopSet = {}; for _, v in ipairs(pinTop) do pinTopSet[v] = true end
        local pinBotSet = {}; for _, v in ipairs(pinBottom) do pinBotSet[v] = true end
        local mid = {}
        for _, entry in ipairs(HS_ACTIONS) do
            if pinTopSet[entry.label] then
                -- keep order from pinTop
            elseif pinBotSet[entry.label] then
                -- keep order from pinBottom
            else
                mid[#mid+1] = entry
            end
        end
        table.sort(mid, function(a, b) return a.label:lower() < b.label:lower() end)
        for _, v in ipairs(pinTop) do
            for _, e in ipairs(HS_ACTIONS) do if e.label == v then sorted[#sorted+1] = e; break end end
        end
        for _, e in ipairs(mid) do sorted[#sorted+1] = e end
        for _, v in ipairs(pinBottom) do
            for _, e in ipairs(HS_ACTIONS) do if e.label == v then sorted[#sorted+1] = e; break end end
        end
        local ITEM_H = 22
        sharedDD:SetWidth(280)   -- wider to fit long hearthstone names
        sharedDD:SetHeight(math.min(#sorted, 12) * ITEM_H + 4)
        ddSC:SetWidth(sharedDD:GetWidth() - 20)
        ddSC:SetHeight(#sorted * ITEM_H)
        local currentVal = GetCurrent()
        for i, entry in ipairs(sorted) do
            local row = CreateFrame("Button", nil, ddSC)
            row:SetHeight(ITEM_H)
            row:SetPoint("TOPLEFT",  ddSC, "TOPLEFT",  0, -(i - 1) * ITEM_H)
            row:SetPoint("TOPRIGHT", ddSC, "TOPRIGHT", 0, -(i - 1) * ITEM_H)
            local rl = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
            rl:SetText(entry.label); rl:SetPoint("LEFT", row, "LEFT", 6, 0)
            local isC = (entry.label == currentVal)
            rl:SetTextColor(isC and C.accent[1] or C.text[1], isC and C.accent[2] or C.text[2], isC and C.accent[3] or C.text[3])
            row:SetScript("OnEnter", function() rl:SetTextColor(C.accent[1], C.accent[2], C.accent[3]) end)
            row:SetScript("OnLeave", function()
                local c2 = (entry.label == GetCurrent())
                rl:SetTextColor(c2 and C.accent[1] or C.text[1], c2 and C.accent[2] or C.text[2], c2 and C.accent[3] or C.text[3])
            end)
            row:SetScript("OnClick", function()
                if CFLUtilSettings and CFLUtilSettings.ibHsCfg then
                    CFLUtilSettings.ibHsCfg[bindKey] = entry.label
                end
                ddLbl:SetText(entry.label)
                sharedDD:Hide(); ddCloseBtn:Hide()
                -- Refresh HUD macros immediately
                if not InCombatLockdown() then CFL_ApplyHUDState() end
            end)
        end
        sharedDD:ClearAllPoints()
        sharedDD:SetPoint("TOPLEFT", self, "BOTTOMLEFT", 0, -2)
        sharedDD._owner = self; sharedDD:Show(); ddCloseBtn:Show()
        ddSF:SetVerticalScroll(0)
    end)
    return ddBtn
end

local hsDD_left   = MakeHsDropdown(utilSC, "left",   0)
local hsDD_right  = MakeHsDropdown(utilSC, "right",  30)
local hsDD_middle = MakeHsDropdown(utilSC, "middle", 60)

-- Store refs so CFL_RefreshUtilUI can update them
statWidgets["_hsLeft"]   = { _hsDD = hsDD_left,   _bindKey = "left"   }
statWidgets["_hsRight"]  = { _hsDD = hsDD_right,  _bindKey = "right"  }
statWidgets["_hsMiddle"] = { _hsDD = hsDD_middle, _bindKey = "middle" }

-- HS section bottom = HS_BASE_Y + 60 + ~24 padding
local CLK_Y = nextY + 16 + 160  -- section header(16) + sep(24) + desc(20) + 3 rows(90) + padding(10)
SLabel(L["Clock Format"], CLK_Y, "GameFontNormalLarge", C.accent[1], C.accent[2], C.accent[3])
SSep(CLK_Y + 24, 0.3)

local clkFmtLbl = utilSC:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
clkFmtLbl:SetText(L["Format:"]); clkFmtLbl:SetTextColor(C.sub[1],C.sub[2],C.sub[3])
clkFmtLbl:SetPoint("TOPLEFT", utilSC, "TOPLEFT", UP_U, -(CLK_Y + 36))

local clkBtn12 = CreateFrame("Button", nil, utilSC, "BackdropTemplate")
clkBtn12:SetSize(44, 20); clkBtn12:SetPoint("LEFT", clkFmtLbl, "RIGHT", 8, 0)
Backdrop(clkBtn12, C.inputBg, C.border)
local clkLbl12 = clkBtn12:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
clkLbl12:SetText("12h"); clkLbl12:SetAllPoints(clkBtn12); clkLbl12:SetJustifyH("CENTER"); clkLbl12:SetTextColor(C.text[1],C.text[2],C.text[3])

local clkBtn24 = CreateFrame("Button", nil, utilSC, "BackdropTemplate")
clkBtn24:SetSize(44, 20); clkBtn24:SetPoint("LEFT", clkBtn12, "RIGHT", 4, 0)
Backdrop(clkBtn24, C.inputBg, C.border)
local clkLbl24 = clkBtn24:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
clkLbl24:SetText("24h"); clkLbl24:SetAllPoints(clkBtn24); clkLbl24:SetJustifyH("CENTER"); clkLbl24:SetTextColor(C.text[1],C.text[2],C.text[3])

-- Source row: Server / Local  (must be created before RefreshClkBtns closure so they're valid upvalues)
local clkSrcLbl = utilSC:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
clkSrcLbl:SetText(L["Source:"]); clkSrcLbl:SetTextColor(C.sub[1],C.sub[2],C.sub[3])
clkSrcLbl:SetPoint("LEFT", clkFmtLbl, "RIGHT", 130, 0)

local clkBtnServer = CreateFrame("Button", nil, utilSC, "BackdropTemplate")
clkBtnServer:SetSize(60, 20); clkBtnServer:SetPoint("LEFT", clkSrcLbl, "RIGHT", 8, 0)
Backdrop(clkBtnServer, C.inputBg, C.border)
local clkLblServer = clkBtnServer:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
clkLblServer:SetText(L["Server"]); clkLblServer:SetAllPoints(clkBtnServer); clkLblServer:SetJustifyH("CENTER"); clkLblServer:SetTextColor(C.text[1],C.text[2],C.text[3])

local clkBtnLocal = CreateFrame("Button", nil, utilSC, "BackdropTemplate")
clkBtnLocal:SetSize(50, 20); clkBtnLocal:SetPoint("LEFT", clkBtnServer, "RIGHT", 4, 0)
Backdrop(clkBtnLocal, C.inputBg, C.border)
local clkLblLocal = clkBtnLocal:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
clkLblLocal:SetText(L["Local"]); clkLblLocal:SetAllPoints(clkBtnLocal); clkLblLocal:SetJustifyH("CENTER"); clkLblLocal:SetTextColor(C.text[1],C.text[2],C.text[3])

RefreshClkBtns = function()
    local use24    = CFLUtilSettings and CFLUtilSettings.ib24h
    local useLocal = CFLUtilSettings and CFLUtilSettings.ibLocalTime
    if use24 then
        clkBtn24:SetBackdropColor(C.accent[1]*0.5,C.accent[2]*0.5,C.accent[3]*0.5,1); clkLbl24:SetTextColor(C.accent[1],C.accent[2],C.accent[3])
        clkBtn12:SetBackdropColor(C.inputBg[1],C.inputBg[2],C.inputBg[3],1); clkLbl12:SetTextColor(C.text[1],C.text[2],C.text[3])
    else
        clkBtn12:SetBackdropColor(C.accent[1]*0.5,C.accent[2]*0.5,C.accent[3]*0.5,1); clkLbl12:SetTextColor(C.accent[1],C.accent[2],C.accent[3])
        clkBtn24:SetBackdropColor(C.inputBg[1],C.inputBg[2],C.inputBg[3],1); clkLbl24:SetTextColor(C.text[1],C.text[2],C.text[3])
    end
    if useLocal then
        clkBtnLocal:SetBackdropColor(C.accent[1]*0.5,C.accent[2]*0.5,C.accent[3]*0.5,1); clkLblLocal:SetTextColor(C.accent[1],C.accent[2],C.accent[3])
        clkBtnServer:SetBackdropColor(C.inputBg[1],C.inputBg[2],C.inputBg[3],1); clkLblServer:SetTextColor(C.text[1],C.text[2],C.text[3])
    else
        clkBtnServer:SetBackdropColor(C.accent[1]*0.5,C.accent[2]*0.5,C.accent[3]*0.5,1); clkLblServer:SetTextColor(C.accent[1],C.accent[2],C.accent[3])
        clkBtnLocal:SetBackdropColor(C.inputBg[1],C.inputBg[2],C.inputBg[3],1); clkLblLocal:SetTextColor(C.text[1],C.text[2],C.text[3])
    end
end

clkBtn12:SetScript("OnClick", function() if CFLUtilSettings then CFLUtilSettings.ib24h = false end; RefreshClkBtns(); CFL_ApplyHUDState() end)
clkBtn24:SetScript("OnClick", function() if CFLUtilSettings then CFLUtilSettings.ib24h = true end; RefreshClkBtns(); CFL_ApplyHUDState() end)
clkBtnServer:SetScript("OnClick", function() if CFLUtilSettings then CFLUtilSettings.ibLocalTime = false end; RefreshClkBtns(); CFL_ApplyHUDState() end)
clkBtnLocal:SetScript("OnClick", function() if CFLUtilSettings then CFLUtilSettings.ibLocalTime = true end; RefreshClkBtns(); CFL_ApplyHUDState() end)


local POS_Y = CLK_Y + 80
SSep(POS_Y - 12, 0.3)
SLabel(L["Position & Layout"], POS_Y, "GameFontNormalLarge", C.accent[1], C.accent[2], C.accent[3])
SSep(POS_Y + 24, 0.3)

local hudResetBtn = CreateFrame("Button", nil, utilSC, "BackdropTemplate")
hudResetBtn:SetSize(160, 26)
hudResetBtn:SetPoint("TOPLEFT", utilSC, "TOPLEFT", UP_U, -(POS_Y + 36))
Backdrop(hudResetBtn, {0.10,0.06,0.18,1}, C.border)
local hudResetLbl = hudResetBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
hudResetLbl:SetText(L["Reset HUD Position"]); hudResetLbl:SetTextColor(C.accent[1],C.accent[2],C.accent[3])
hudResetLbl:SetAllPoints(hudResetBtn); hudResetLbl:SetJustifyH("CENTER")
hudResetBtn:SetScript("OnEnter", function(self) self:SetBackdropColor(0.20,0.10,0.32,1) end)
hudResetBtn:SetScript("OnLeave", function(self) self:SetBackdropColor(0.10,0.06,0.18,1) end)
hudResetBtn:SetScript("OnClick", function()
    if CFLUtilSettings then
        CFLUtilSettings.hudPX = nil   -- nil = centre on next load
        CFLUtilSettings.hudPY = 30
        CFL_ApplyHUDState()
    end
end)

local hudLockCB = MakeCheckbox(utilSC, L["Lock Position"], 0, function(checked)
    if CFLUtilSettings then CFLUtilSettings.hudLocked = checked end
    if checked then CFL_SetHudSelected(false) end
end)
hudLockCB:ClearAllPoints()
hudLockCB:SetPoint("LEFT", hudResetBtn, "RIGHT", 16, 0)
statWidgets["_ibLockCB"] = { cb = hudLockCB, showKey = "hudLocked" }

scaleSlider = CreateFrame("Slider", nil, utilSC, "OptionsSliderTemplate")
scaleSlider:SetPoint("LEFT", hudLockCB, "RIGHT", 110, 0)
scaleSlider:SetMinMaxValues(0.5, 2.0)
scaleSlider:SetValueStep(0.05)
scaleSlider:SetObeyStepOnDrag(true)
scaleSlider:SetValue(CFLUtilSettings and CFLUtilSettings.hudScale or 1.0)
scaleSlider.Text:SetText(L["HUD Scale"])
scaleSlider.Low:SetText("0.5")
scaleSlider.High:SetText("2.0")

scaleEB = MakeUtilEditBox(utilSC, 52, "LEFT", scaleSlider, "RIGHT", 10, 0)
scaleEB:SetMaxLetters(4)
scaleEB:SetText(string.format("%.2f", CFLUtilSettings and CFLUtilSettings.hudScale or 1.0))

-- _scaleSup declared above CFL_RefreshUtilUI

local NUDGE_Y = POS_Y + 74
local nudgeLbl = utilSC:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
nudgeLbl:SetText(L["Nudge Position"]); nudgeLbl:SetTextColor(C.sub[1], C.sub[2], C.sub[3])
nudgeLbl:SetPoint("TOPLEFT", utilSC, "TOPLEFT", UP_U, -NUDGE_Y)

local function MakeNudgeBtn(glyph, anchor, dx, dy)
    local btn = CreateFrame("Button", nil, utilSC, "BackdropTemplate")
    btn:SetSize(24, 24)
    if anchor then
        btn:SetPoint("LEFT", anchor, "RIGHT", 4, 0)
    else
        btn:SetPoint("LEFT", nudgeLbl, "RIGHT", 10, 0)
    end
    Backdrop(btn, C.inputBg, C.border)
    local lbl = btn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    lbl:SetAllPoints(btn); lbl:SetJustifyH("CENTER"); lbl:SetText(glyph)
    lbl:SetTextColor(C.text[1], C.text[2], C.text[3])
    btn:SetScript("OnEnter", function(self) self:SetBackdropColor(0.20,0.10,0.32,1) end)
    btn:SetScript("OnLeave", function(self) self:SetBackdropColor(C.inputBg[1],C.inputBg[2],C.inputBg[3],1) end)
    btn:SetScript("OnClick", function()
        local step = IsShiftKeyDown() and 10 or 1
        CFL_NudgeHud(dx * step, dy * step)
    end)
    return btn
end

local nudgeLeft  = MakeNudgeBtn("<", nil,        -1,  0)
local nudgeUp    = MakeNudgeBtn("^", nudgeLeft,   0,  1)
local nudgeDown  = MakeNudgeBtn("v", nudgeUp,     0, -1)
local nudgeRight = MakeNudgeBtn(">", nudgeDown,   1,  0)

local nudgeHint = utilSC:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
nudgeHint:SetText(L["HUD_NUDGE_HINT"]); nudgeHint:SetTextColor(C.sub[1], C.sub[2], C.sub[3])
nudgeHint:SetPoint("LEFT", nudgeRight, "RIGHT", 12, 0)

scaleSlider:SetScript("OnValueChanged", function(self, value)
    if CFLUtilSettings then CFLUtilSettings.hudScale = value end
    if HUD then HUD:SetScale(value) end
    if not _scaleSup then
        _scaleSup = true
        scaleEB:SetText(string.format("%.2f", value))
        _scaleSup = false
    end
end)

local function ApplyScaleEB()
    if _scaleSup then return end
    local v = tonumber(scaleEB:GetText())
    if v then
        v = math.max(0.5, math.min(2.0, v))
        -- round to nearest 0.05 step
        v = math.floor(v / 0.05 + 0.5) * 0.05
        _scaleSup = true
        scaleSlider:SetValue(v)
        scaleEB:SetText(string.format("%.2f", v))
        _scaleSup = false
        if CFLUtilSettings then CFLUtilSettings.hudScale = v end
        if HUD then HUD:SetScale(v) end
    else
        -- restore last valid value
        local cur = CFLUtilSettings and CFLUtilSettings.hudScale or 1.0
        scaleEB:SetText(string.format("%.2f", cur))
    end
end

scaleEB:SetScript("OnEnterPressed", function(self) self:ClearFocus(); ApplyScaleEB() end)
scaleEB:SetScript("OnEditFocusLost", ApplyScaleEB)

-- ============================================================
--  Icon Order — reorder + left/right assignment for every wing element
-- ============================================================
local ORDER_Y = POS_Y + 122
SSep(ORDER_Y - 12, 0.3)
SLabel(L["Icon Order"], ORDER_Y, "GameFontNormalLarge", C.accent[1], C.accent[2], C.accent[3])
SSep(ORDER_Y + 24, 0.3)
SLabel(L["ICON_ORDER_DESC"], ORDER_Y + 36, "GameFontNormal", C.sub[1], C.sub[2], C.sub[3])

local ORDER_ITEMS = {
    { key = "gold",        label = L["Gold"] },
    { key = "friends",     label = L["Friends"] },
    { key = "party",       label = L["Party"] },
    { key = "screenshot",  label = L["Screenshot"] },
    { key = "hearthstone", label = L["Hearthstone"] },
    { key = "herodc",      label = L["Hero DC"] },
    { key = "mythdc",      label = L["Myth DC"] },
    { key = "voidcore",    label = L["Voidcore"] },
}

local ORDER_ROW_H  = 30
local ORDER_ROW_GAP = 3
local ORDER_ROWS_Y = ORDER_Y + 64
local orderRowContainers = {}   -- key -> row frame
local orderRowRefreshers = {}   -- list of left/right highlight refresh functions

local function MakeOrderRow(item)
    local row = CreateFrame("Frame", "CFLOrderRow_"..item.key, utilSC, "BackdropTemplate")
    row:SetSize(420, ORDER_ROW_H - ORDER_ROW_GAP)
    row:SetFrameLevel(utilSC:GetFrameLevel() + 2)
    Backdrop(row, C.row1, {0, 0, 0, 0})
    row:EnableMouse(true)
    row:RegisterForDrag("LeftButton")

    local grip = CreateFrame("Frame", nil, row)
    grip:SetSize(14, 16)
    grip:SetPoint("LEFT", row, "LEFT", 8, 0)
    for i = 1, 3 do
        local bar = Tex(grip, C.sub[1], C.sub[2], C.sub[3], 0.8)
        bar:SetSize(14, 2)
        bar:SetPoint("TOP", grip, "TOP", 0, -(i - 1) * 6)
    end

    local rowLbl = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    rowLbl:SetText(item.label); rowLbl:SetTextColor(C.text[1], C.text[2], C.text[3])
    rowLbl:SetPoint("LEFT", grip, "RIGHT", 12, 0)
    rowLbl:SetWidth(130); rowLbl:SetJustifyH("LEFT")

    local leftBtn = CreateFrame("Button", "CFLOrderRow_"..item.key.."_Left", row, "BackdropTemplate")
    leftBtn:SetSize(56, 20)
    leftBtn:SetPoint("LEFT", rowLbl, "RIGHT", 6, 0)
    Backdrop(leftBtn, C.inputBg, C.border)
    local leftLbl = leftBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    leftLbl:SetAllPoints(leftBtn); leftLbl:SetJustifyH("CENTER"); leftLbl:SetText(L["Left"])
    leftLbl:SetTextColor(C.text[1], C.text[2], C.text[3])

    local rightBtn = CreateFrame("Button", "CFLOrderRow_"..item.key.."_Right", row, "BackdropTemplate")
    rightBtn:SetSize(56, 20)
    rightBtn:SetPoint("LEFT", leftBtn, "RIGHT", 4, 0)
    Backdrop(rightBtn, C.inputBg, C.border)
    local rightLbl = rightBtn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    rightLbl:SetAllPoints(rightBtn); rightLbl:SetJustifyH("CENTER"); rightLbl:SetText(L["Right"])
    rightLbl:SetTextColor(C.text[1], C.text[2], C.text[3])

    local function RefreshVisual()
        local D = CFLUtilSettings
        local side = (D and D.hudSide and D.hudSide[item.key]) or DEFAULT_HUD_SIDE[item.key]
        if side == "right" then
            rightBtn:SetBackdropColor(C.accent[1]*0.5, C.accent[2]*0.5, C.accent[3]*0.5, 1)
            rightLbl:SetTextColor(C.accent[1], C.accent[2], C.accent[3])
            leftBtn:SetBackdropColor(C.inputBg[1], C.inputBg[2], C.inputBg[3], 1)
            leftLbl:SetTextColor(C.text[1], C.text[2], C.text[3])
        else
            leftBtn:SetBackdropColor(C.accent[1]*0.5, C.accent[2]*0.5, C.accent[3]*0.5, 1)
            leftLbl:SetTextColor(C.accent[1], C.accent[2], C.accent[3])
            rightBtn:SetBackdropColor(C.inputBg[1], C.inputBg[2], C.inputBg[3], 1)
            rightLbl:SetTextColor(C.text[1], C.text[2], C.text[3])
        end
    end

    leftBtn:SetScript("OnClick", function() CFL_SetItemSide(item.key, "left") end)
    rightBtn:SetScript("OnClick", function() CFL_SetItemSide(item.key, "right") end)

    row:SetScript("OnEnter", function(self) if not self._dragging then self:SetBackdropColor(C.row1[1]*1.6, C.row1[2]*1.6, C.row1[3]*1.6, 1) end end)
    row:SetScript("OnLeave", function(self) if not self._dragging then Backdrop(self, C.row1, {0, 0, 0, 0}) end end)

    row:SetScript("OnDragStart", function(self)
        self._dragging = true
        self:SetFrameStrata("TOOLTIP")
        Backdrop(self, C.row1, C.accent)
        self:SetBackdropColor(C.row1[1]*2, C.row1[2]*2, C.row1[3]*2, 1)
        local _, my = GetCursorPosition()
        local startMouseY = my / UIParent:GetEffectiveScale()
        local order = CFL_NormalizedOrder()
        local startIndex = 1
        for i, k in ipairs(order) do if k == item.key then startIndex = i break end end
        local baseOffsetY = ORDER_ROWS_Y + (startIndex - 1) * ORDER_ROW_H
        local lastIndex = startIndex
        self:SetScript("OnUpdate", function(sf)
            local _, cy = GetCursorPosition()
            cy = cy / UIParent:GetEffectiveScale()
            local deltaY = startMouseY - cy
            sf:ClearAllPoints()
            sf:SetPoint("TOPLEFT", utilSC, "TOPLEFT", UP_U, -(baseOffsetY + deltaY))
            local steps = math.floor(deltaY / ORDER_ROW_H + 0.5)
            local newIndex = math.max(1, math.min(startIndex + steps, #ORDER_ITEMS))
            if newIndex ~= lastIndex then
                if CFL_ReorderTo(item.key, newIndex) then
                    lastIndex = newIndex
                    RelayoutOrderRows(item.key)
                    CFL_ApplyHUDState()
                end
            end
        end)
    end)

    row:SetScript("OnDragStop", function(self)
        self._dragging = false
        self:SetScript("OnUpdate", nil)
        self:SetFrameStrata(utilSC:GetFrameStrata())
        Backdrop(self, C.row1, {0, 0, 0, 0})
        RelayoutOrderRows()
        CFL_ApplyHUDState()
    end)

    orderRowContainers[item.key] = row
    orderRowRefreshers[#orderRowRefreshers+1] = RefreshVisual
end

for _, item in ipairs(ORDER_ITEMS) do MakeOrderRow(item) end

RelayoutOrderRows = function(excludeKey)
    local order = CFL_NormalizedOrder()
    for i, key in ipairs(order) do
        if key ~= excludeKey then
            local row = orderRowContainers[key]
            if row then
                row:ClearAllPoints()
                row:SetPoint("TOPLEFT", utilSC, "TOPLEFT", UP_U, -(ORDER_ROWS_Y + (i - 1) * ORDER_ROW_H))
            end
        end
    end
    for _, fn in ipairs(orderRowRefreshers) do fn() end
end
RelayoutOrderRows()

local TOTAL_CONTENT_H = ORDER_ROWS_Y + (#ORDER_ITEMS * ORDER_ROW_H) + 30
utilSC:SetHeight(TOTAL_CONTENT_H)

-- ============================================================
--  Slash commands & Modern Interface Category Options
-- ============================================================
SLASH_CFL1 = "/cfl"
SlashCmdList["CFL"] = function()
    if Main:IsShown() then Main:Hide() else Main:Show() end
end

-- Everything below is self-contained (nothing after end-of-file could ever
-- reference these by name), so it's wrapped in a do-block purely to keep
-- this file under Lua 5.1's 200-top-level-local ceiling as the addon grows.
do
local OptionsFrame = CreateFrame("Frame")
OptionsFrame.name = "Custom Font Loader"

local category = Settings.RegisterCanvasLayoutCategory(OptionsFrame, OptionsFrame.name)
Settings.RegisterAddOnCategory(category)

-- ============================================================
--  Minimap Button
-- ============================================================
local MinimapBtn = CreateFrame("Button", "CFLMinimapButton", Minimap)
MinimapBtn:SetSize(32, 32)
MinimapBtn:SetFrameStrata("MEDIUM")
MinimapBtn:SetFrameLevel(8)
MinimapBtn:SetClampedToScreen(true)
MinimapBtn:SetMovable(true)
MinimapBtn:RegisterForClicks("anyUp")
MinimapBtn:RegisterForDrag("LeftButton")

local bg = MinimapBtn:CreateTexture(nil, "BACKGROUND")
bg:SetSize(25, 25)
bg:SetPoint("CENTER", 0, 0)
bg:SetTexture("Interface\\Minimap\\UI-Minimap-Background")
bg:SetVertexColor(0, 0, 0, 1)

local icon = MinimapBtn:CreateTexture(nil, "ARTWORK")
icon:SetSize(18, 18)
icon:SetPoint("CENTER", 0, 0)
icon:SetTexture("Interface\\AddOns\\CustomFontLoader\\cfl.png")

local overlay = MinimapBtn:CreateTexture(nil, "OVERLAY")
overlay:SetSize(53, 53)
overlay:SetPoint("TOPLEFT", MinimapBtn, "TOPLEFT", 0, 0)
overlay:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")

MinimapBtn:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")

local isDragging = false
MinimapBtn:SetScript("OnEnter", function(self)
    if isDragging then return end
    GameTooltip:SetOwner(self, "ANCHOR_NONE")
    GameTooltip:SetPoint("TOPRIGHT", self, "TOPLEFT", -2, 0)
    GameTooltip:SetText(L["TT_MINIMAP"], 0.65, 0.30, 1.00)
    GameTooltip:AddLine(L["TT_MINIMAP_CLICK"], 0.82, 0.78, 0.90)
    GameTooltip:AddLine(L["TT_MINIMAP_DRAG"], 0.82, 0.78, 0.90)
    GameTooltip:Show()
end)
MinimapBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
MinimapBtn:SetScript("OnClick", function()
    if Main:IsShown() then Main:Hide() else Main:Show() end
end)

local function UpdateMinimapPos(angle)
    local mw, mh   = Minimap:GetWidth(), Minimap:GetHeight()
    local radius   = (math.max(mw, mh) / 2) + 5
    local rad      = math.rad(angle)
    MinimapBtn:ClearAllPoints()
    MinimapBtn:SetPoint("CENTER", Minimap, "CENTER", math.cos(rad) * radius, math.sin(rad) * radius)
end

local function DragOnUpdate()
    local mx, my   = Minimap:GetCenter()
    local cx, cy   = GetCursorPosition()
    local scale    = Minimap:GetEffectiveScale()
    cx, cy         = cx / scale, cy / scale
    CFLMinimapAngle = math.deg(math.atan2(cy - my, cx - mx))
    UpdateMinimapPos(CFLMinimapAngle)
end

MinimapBtn:SetScript("OnDragStart", function(self)
    isDragging = true
    self:LockHighlight()
    self:SetScript("OnUpdate", DragOnUpdate)
    GameTooltip:Hide()
end)

MinimapBtn:SetScript("OnDragStop", function(self)
    self:SetScript("OnUpdate", nil)
    self:UnlockHighlight()
    isDragging = false
    UpdateMinimapPos(CFLMinimapAngle)
end)

local initLoader = CreateFrame("Frame")
initLoader:RegisterEvent("ADDON_LOADED")
initLoader:SetScript("OnEvent", function(self, event, addon)
    if addon ~= addonName then return end
    if not CFLMinimapAngle then CFLMinimapAngle = 220 end
    UpdateMinimapPos(CFLMinimapAngle)
end)
end
