-- Portal Authority — options/panel_dock.lua
-- Mythic+ Dock settings panel and dock import/export helpers.
--
-- Depends on: PortalAuthority.OptionsInternal shared helpers; PortalAuthority
-- Exposes:    PortalAuthority.OptionsInternal.createDockPanel
-- DB keys:    dock*
-- Events:     none
-- Search key: dock

PortalAuthority = PortalAuthority or {}

if PortalAuthority.IsSettingsBaselineGateEnabled and PortalAuthority:IsSettingsBaselineGateEnabled() then
    return
end

if not Settings or not Settings.RegisterCanvasLayoutCategory then
    return
end

PortalAuthority.OptionsInternal = PortalAuthority.OptionsInternal or {}
local OptionsInternal = PortalAuthority.OptionsInternal

local settingsSearchRuntime = OptionsInternal.SearchRuntime
local trim = OptionsInternal.trim
local rgba255 = OptionsInternal.rgba255
local unpackColor = OptionsInternal.unpackColor
local applyGlobalFontToObject = OptionsInternal.applyGlobalFontToObject
local getPortalMedia = OptionsInternal.getPortalMedia
local normalizeSoundChoiceValue = OptionsInternal.normalizeSoundChoiceValue
local compareChoiceLabels = OptionsInternal.compareChoiceLabels
local normalizeChoicePathKey = OptionsInternal.normalizeChoicePathKey
local resolveFontChoicePath = OptionsInternal.resolveFontChoicePath
local getSoundChoiceValueKey = OptionsInternal.getSoundChoiceValueKey
local buildFallbackSoundChoices = OptionsInternal.buildFallbackSoundChoices
local getSummonSoundChoices = OptionsInternal.getSummonSoundChoices
local getAvailableFontChoices = OptionsInternal.getAvailableFontChoices
local buildSharedFontPickerChoices = OptionsInternal.buildSharedFontPickerChoices
local SUMMON_SOUND_CHANNEL_CHOICES = OptionsInternal.SUMMON_SOUND_CHANNEL_CHOICES
local debugSettingsLog = OptionsInternal.debugSettingsLog or function() end
local FLAT_SETTINGS_BACKDROP = OptionsInternal.FLAT_SETTINGS_BACKDROP
local SETTINGS_WINDOW_HOSTED_TOP_RHYTHM_NUDGE = OptionsInternal.SETTINGS_WINDOW_HOSTED_TOP_RHYTHM_NUDGE
local SETTINGS_WINDOW_SCROLLBAR_WIDTH = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_WIDTH
local SETTINGS_WINDOW_SCROLLBAR_GAP = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_GAP
local SETTINGS_WINDOW_SCROLLBAR_MIN_THUMB_HEIGHT = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_MIN_THUMB_HEIGHT
local SETTINGS_WINDOW_HOST_COLORS = OptionsInternal.SETTINGS_WINDOW_HOST_COLORS
local SETTINGS_WINDOW_SCROLLBAR_TRACK = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_TRACK
local SETTINGS_WINDOW_SCROLLBAR_THUMB = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_THUMB
local SETTINGS_WINDOW_SCROLLBAR_THUMB_HOVER = OptionsInternal.SETTINGS_WINDOW_SCROLLBAR_THUMB_HOVER
local frameIsAlive = OptionsInternal.frameIsAlive
local isDescendedFromFrame = OptionsInternal.isDescendedFromFrame
local markSettingsWindowVisualOptIn = OptionsInternal.markSettingsWindowVisualOptIn
local isUsingCustomSettingsWindowVisualStyle = OptionsInternal.isUsingCustomSettingsWindowVisualStyle
local registerSettingsWindowVisualRefresh = OptionsInternal.registerSettingsWindowVisualRefresh
local setTextureSolidColor = OptionsInternal.setTextureSolidColor
local hideDefaultScrollBarPart = OptionsInternal.hideDefaultScrollBarPart
local getTemplateScrollBar = OptionsInternal.getTemplateScrollBar
local hideDefaultScrollBarChrome = OptionsInternal.hideDefaultScrollBarChrome
local refreshCustomSettingsScrollBarThumb = OptionsInternal.refreshCustomSettingsScrollBarThumb
local settingsScrollRuntime = PortalAuthority._settingsScrollRuntime or OptionsInternal.settingsScrollRuntime
local createStyledFontString = OptionsInternal.createStyledFontString
local createTopRightHeader = OptionsInternal.createTopRightHeader
local PA_SafeBool = OptionsInternal.PA_SafeBool
local getClassHex = OptionsInternal.getClassHex
local createCheckbox = OptionsInternal.createCheckbox
local createActionButton = OptionsInternal.createActionButton
local styleSecondaryActionButtonShared = OptionsInternal.styleSecondaryActionButtonShared
local createNumeric = OptionsInternal.createNumeric
local createDropdown = OptionsInternal.createDropdown
local createDynamicDropdown = OptionsInternal.createDynamicDropdown
local createPremiumSegmentedChoice = OptionsInternal.createPremiumSegmentedChoice
local CreatePicker = OptionsInternal.CreatePicker
local CreateStepperControl = OptionsInternal.CreateStepperControl
local createCanonicalDockFontSizeStepperControl = OptionsInternal.CreateStepperControl or OptionsInternal.createCanonicalDockFontSizeStepperControl
local createTextPlacement = OptionsInternal.createTextPlacement
local setSettingsControlEnabled = OptionsInternal.setSettingsControlEnabled
local setSettingsTextEnabled = OptionsInternal.setSettingsTextEnabled
local createScrollableChild = OptionsInternal.createScrollableChild
local updateScrollBehavior = OptionsInternal.updateScrollBehavior
local withPanelHydration = OptionsInternal.withPanelHydration
local panelHasSizedViewport = OptionsInternal.panelHasSizedViewport
local measureSettingsVisibleContentExtent = OptionsInternal.measureSettingsVisibleContentExtent
local isControlHydrating = OptionsInternal.isControlHydrating
local handleSettingsFirstOpenSizeReady = OptionsInternal.handleSettingsFirstOpenSizeReady
local handleSettingsFirstOpenPanelShow = OptionsInternal.handleSettingsFirstOpenPanelShow
local handleSettingsFirstOpenPanelHide = OptionsInternal.handleSettingsFirstOpenPanelHide
local shouldSuppressSettingsFirstOpenExternalRefresh = OptionsInternal.shouldSuppressSettingsFirstOpenExternalRefresh
local isPanelUsingCustomSettingsWindowHost = OptionsInternal.isPanelUsingCustomSettingsWindowHost
local refreshSettingsWindowHeaderAccessoryIfHosted = OptionsInternal.refreshSettingsWindowHeaderAccessoryIfHosted
local refreshPanel = OptionsInternal.refreshPanel
local createScrollableTextArea = OptionsInternal.createScrollableTextArea
local createSettingsSectionDescriptor = OptionsInternal.createSettingsSectionDescriptor
local layoutSettingsSectionDescriptor = OptionsInternal.layoutSettingsSectionDescriptor
local registerRefreshCallback = OptionsInternal.registerRefreshCallback
local ensureDatabase = OptionsInternal.ensureDatabase
local IsPointerInsideActivePickerSafeZone = OptionsInternal.IsPointerInsideActivePickerSafeZone
local CloseActivePortalPicker = OptionsInternal.CloseActivePortalPicker
local openColorPicker = OptionsInternal.openColorPicker
local settingsFirstOpenHost = OptionsInternal.settingsFirstOpenHost
local resetSettingsFirstOpenPanelState = OptionsInternal.resetSettingsFirstOpenPanelState

local DOCK_IMPORT_EXPORT_KEYS = {
    { key = "dockEnabled", type = "boolean" },
    { key = "dockOrientation", type = "string" },
    { key = "dockX", type = "number" },
    { key = "dockY", type = "number" },
    { key = "dockLocked", type = "boolean" },
    { key = "dockHideInMajorCity", type = "boolean" },
    { key = "dockHideInDungeon", type = "boolean" },
    { key = "dockHideInCombat", type = "boolean" },
    { key = "dockIconWidth", type = "number" },
    { key = "dockIconHeight", type = "number" },
    { key = "dockRowSpacing", type = "number" },
    { key = "dockTextIconSpacing", type = "number" },
    { key = "dockHideDungeonName", type = "boolean" },
    { key = "dockGizmoMode", type = "boolean" },
    { key = "dockUseShortNames", type = "boolean" },
    { key = "dockVerticalTextSide", type = "string" },
    { key = "dockHorizontalTextPosition", type = "string" },
    { key = "dockFontPath", type = "string" },
    { key = "dockFontSize", type = "number" },
    { key = "dockFontColor", type = "color" },
    { key = "dockTextOutline", type = "string" },
    { key = "dockTextShadow", type = "boolean" },
    { key = "dockShadowOffsetX", type = "number" },
    { key = "dockShadowOffsetY", type = "number" },
    { key = "dockLayoutMode", type = "string" },
    { key = "dockWrapAfter", type = "number" },
    { key = "dockIconSize", type = "number" },
    { key = "dockSpacingX", type = "number" },
    { key = "dockSpacingY", type = "number" },
    { key = "dockPadding", type = "number" },
    { key = "dockGrowthX", type = "string" },
    { key = "dockGrowthY", type = "string" },
    { key = "dockAnchorPoint", type = "string" },
    { key = "dockCenterAlignment", type = "boolean" },
    { key = "dockPreferAxis", type = "string" },
    { key = "dockCompactMode", type = "boolean" },
    { key = "dockTestMode", type = "boolean" },
    { key = "dockSortMode", type = "string" },
    { key = "dockAnimateReflow", type = "boolean" },
    { key = "dockAnimateDuration", type = "number" },
    { key = "dockBackdropEnabled", type = "boolean" },
    { key = "dockInactiveAlpha", type = "number" },
    { key = "dockHoverGlow", type = "boolean" },
    { key = "dockSimpleLayoutMode", type = "string" },
    { key = "dockIconsPerLine", type = "number" },
    { key = "dockIconSpacing", type = "number" },
    { key = "dockDensity", type = "number" },
    { key = "dockTextDirection", type = "string" },
    { key = "dockTextAlign", type = "string" },
    { key = "dockLabelAlignMode", type = "string" },
    { key = "dockTextOffset", type = "number" },
    { key = "dockLabelNudge", type = "number" },
    { key = "dockLabelMode", type = "string" },
    { key = "dockLabelSide", type = "string" },
    { key = "dockLabelSideOutsidePersist", type = "string" },
    { key = "dockHoverGlowAlpha", type = "number" },
    { key = "dockHoverGlowSize", type = "number" },
}

local DOCK_IMPORT_EXPORT_KEYSET = {}
for _, entry in ipairs(DOCK_IMPORT_EXPORT_KEYS) do
    DOCK_IMPORT_EXPORT_KEYSET[entry.key] = entry.type
end

local function escapeValue(text)
    return tostring(text):gsub("%%", "%%25"):gsub("|", "%%7C")
end

local function unescapeValue(text)
    return tostring(text):gsub("%%(%x%x)", function(hex)
        return string.char(tonumber(hex, 16) or 0)
    end)
end

local BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
local BASE64_DECODE_MAP = {}
for i = 1, #BASE64_ALPHABET do
    BASE64_DECODE_MAP[BASE64_ALPHABET:sub(i, i)] = i - 1
end

local function base64Encode(input)
    local bytes = { string.byte(input, 1, #input) }
    local out = {}

    for i = 1, #bytes, 3 do
        local b1 = bytes[i] or 0
        local b2 = bytes[i + 1] or 0
        local b3 = bytes[i + 2] or 0
        local n = b1 * 65536 + b2 * 256 + b3

        local c1 = math.floor(n / 262144) % 64
        local c2 = math.floor(n / 4096) % 64
        local c3 = math.floor(n / 64) % 64
        local c4 = n % 64

        out[#out + 1] = BASE64_ALPHABET:sub(c1 + 1, c1 + 1)
        out[#out + 1] = BASE64_ALPHABET:sub(c2 + 1, c2 + 1)
        out[#out + 1] = (i + 1 <= #bytes) and BASE64_ALPHABET:sub(c3 + 1, c3 + 1) or "="
        out[#out + 1] = (i + 2 <= #bytes) and BASE64_ALPHABET:sub(c4 + 1, c4 + 1) or "="
    end

    return table.concat(out)
end

local function base64Decode(input)
    local clean = trim(input)
    if clean == "" or (#clean % 4 ~= 0) then
        return nil, "invalid base64 length"
    end
    if clean:find("[^A-Za-z0-9%+/%=]") then
        return nil, "invalid base64 characters"
    end
    if clean:find("=[^=]") then
        return nil, "invalid base64 padding"
    end

    local out = {}
    for i = 1, #clean, 4 do
        local c1 = clean:sub(i, i)
        local c2 = clean:sub(i + 1, i + 1)
        local c3 = clean:sub(i + 2, i + 2)
        local c4 = clean:sub(i + 3, i + 3)

        local v1 = BASE64_DECODE_MAP[c1]
        local v2 = BASE64_DECODE_MAP[c2]
        local v3 = (c3 == "=") and 0 or BASE64_DECODE_MAP[c3]
        local v4 = (c4 == "=") and 0 or BASE64_DECODE_MAP[c4]
        if v1 == nil or v2 == nil or v3 == nil or v4 == nil then
            return nil, "invalid base64 sequence"
        end

        local n = v1 * 262144 + v2 * 4096 + v3 * 64 + v4
        local b1 = math.floor(n / 65536) % 256
        local b2 = math.floor(n / 256) % 256
        local b3 = n % 256

        out[#out + 1] = string.char(b1)
        if c3 ~= "=" then
            out[#out + 1] = string.char(b2)
        end
        if c4 ~= "=" then
            out[#out + 1] = string.char(b3)
        end
    end

    return table.concat(out)
end

local DOCK_IMPORT_PREFIX = "PA1:"
local DOCK_IMPORT_NUMERIC_LIMITS = {
    dockX = { min = -3000, max = 3000 },
    dockY = { min = -3000, max = 3000 },
    dockIconWidth = { min = 16, max = 128 },
    dockIconHeight = { min = 16, max = 128 },
    dockRowSpacing = { min = 0, max = 40 },
    dockTextIconSpacing = { min = 0, max = 40 },
    dockFontSize = { min = 8, max = 48 },
    dockShadowOffsetX = { min = -10, max = 10 },
    dockShadowOffsetY = { min = -10, max = 10 },
}

local DOCK_IMPORT_STRING_ENUMS = {
    dockOrientation = { VERTICAL = true, HORIZONTAL = true },
    dockVerticalTextSide = { LEFT = true, RIGHT = true },
    dockHorizontalTextPosition = { ABOVE = true, BELOW = true },
    dockTextOutline = { [""] = true, OUTLINE = true, THICKOUTLINE = true },
}

local function getDockSettingValue(key)
    local db = PortalAuthorityDB or {}
    local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
    local value = db[key]
    if value == nil then
        value = defaults[key]
    end
    return value
end

local function encodeDockValue(entry)
    local value = getDockSettingValue(entry.key)
    if entry.type == "boolean" then
        return value and "1" or "0"
    elseif entry.type == "number" then
        return tostring(math.floor(tonumber(value) or 0))
    elseif entry.type == "color" then
        local c = type(value) == "table" and value or { r = 1, g = 0.82, b = 0, a = 1 }
        return string.format("%.3f,%.3f,%.3f,%.3f", c.r or 1, c.g or 0.82, c.b or 0, c.a or 1)
    end
    return escapeValue(value or "")
end

local function decodeDockValue(entry, raw)
    if entry.type == "boolean" then
        if raw == "1" then return true end
        if raw == "0" then return false end
        return nil, "expected boolean"
    elseif entry.type == "number" then
        local n = tonumber(raw)
        if not n then
            return nil, "expected number"
        end
        return math.floor(n)
    elseif entry.type == "color" then
        local r, g, b, a = raw:match("^([^,]+),([^,]+),([^,]+),([^,]+)$")
        r, g, b, a = tonumber(r), tonumber(g), tonumber(b), tonumber(a)
        if not r or not g or not b or not a then
            return nil, "expected color"
        end
        return {
            r = math.max(0, math.min(1, r)),
            g = math.max(0, math.min(1, g)),
            b = math.max(0, math.min(1, b)),
            a = math.max(0, math.min(1, a)),
        }
    end
    return unescapeValue(raw)
end

local function validateDockValue(entry, value)
    if entry.type == "number" then
        local limits = DOCK_IMPORT_NUMERIC_LIMITS[entry.key]
        if limits and (value < limits.min or value > limits.max) then
            return nil, string.format("%s must be between %d and %d.", entry.key, limits.min, limits.max)
        end
    elseif entry.type == "string" then
        local enumSet = DOCK_IMPORT_STRING_ENUMS[entry.key]
        if enumSet and not enumSet[value] then
            return nil, string.format("%s has an unsupported value.", entry.key)
        end
    end
    return value
end

local function serializeDockSettings()
    local parts = {}
    for _, entry in ipairs(DOCK_IMPORT_EXPORT_KEYS) do
        parts[#parts + 1] = string.format("%s=%s", entry.key, encodeDockValue(entry))
    end
    return DOCK_IMPORT_PREFIX .. base64Encode(table.concat(parts, "|"))
end

local function parseDockSettings(text)
    local payload = trim(text)
    if payload == "" then
        return nil, "Import string is empty."
    end

    if payload:sub(1, #DOCK_IMPORT_PREFIX) ~= DOCK_IMPORT_PREFIX then
        return nil, "Missing PA1 prefix."
    end

    local decodedPayload, decodeErr = base64Decode(payload:sub(#DOCK_IMPORT_PREFIX + 1))
    if not decodedPayload then
        return nil, "Invalid encoded payload (" .. decodeErr .. ")."
    end

    local rawMap = {}
    for segment in decodedPayload:gmatch("([^|]+)") do
        local key, raw = segment:match("^([^=]+)=(.*)$")
        if key and DOCK_IMPORT_EXPORT_KEYSET[key] then
            rawMap[key] = raw
        end
    end

    local values = {}
    for _, entry in ipairs(DOCK_IMPORT_EXPORT_KEYS) do
        local raw = rawMap[entry.key]
        local parsedValue

        if raw == nil then
            parsedValue = getDockSettingValue(entry.key)
        else
            local decoded, err = decodeDockValue(entry, raw)
            if err then
                return nil, string.format("Invalid value for %s (%s).", entry.key, err)
            end
            parsedValue = decoded
        end

        local _, err = validateDockValue(entry, parsedValue)
        if err then
            return nil, err
        end

        values[entry.key] = parsedValue
    end

    return values
end

local function applyDockImport(values)
    for _, entry in ipairs(DOCK_IMPORT_EXPORT_KEYS) do
        PortalAuthorityDB[entry.key] = values[entry.key]
    end

    if PortalAuthority.EnsureDB then
        PortalAuthority:EnsureDB()
    end
    if PortalAuthority.UpdateDockMovableState then
        PortalAuthority:UpdateDockMovableState()
    end
    if PortalAuthority.RebuildDock then
        PortalAuthority:RebuildDock()
    end
    if PortalAuthority.ApplyDockPosition then
        PortalAuthority:ApplyDockPosition()
    end
    if InCombatLockdown() then
        if PortalAuthority.QueueDockVisibilityRefresh then
            PortalAuthority:QueueDockVisibilityRefresh()
        end
        return
    end

    if PortalAuthority.UpdateDockVisibility then
        PortalAuthority:UpdateDockVisibility(true)
    elseif PortalAuthority.RefreshDockVisibility then
        PortalAuthority:RefreshDockVisibility(true)
    end
end
local function createDockPanel()
    local panel = CreateFrame("Frame")
    panel.name = "Mythic+ Dock"
    panel._paSearchSectionKey = "dock"
    panel._paSearchSectionName = panel.name
    local DEBUG_XY = false
    local ensureDockPanelAnchored
    local renderDockPanel
    debugSettingsLog("create frame:", panel.name)

    panel.EnsureBuilt = function(self)
        debugSettingsLog("EnsureBuilt:", self.name, "initialized=", self._initialized and "true" or "false")
        if self._initialized and self._layout and self._refresh then
            return
        end
        resetSettingsFirstOpenPanelState(self)
        ensureDatabase()
        settingsSearchRuntime.beginDescriptorCapture(self)

        local scrollFrame, scrollChild = createScrollableChild(self)
        if not scrollFrame or not scrollChild then
            settingsSearchRuntime.endDescriptorCapture(self)
            return
        end
        self._sizingFrame = scrollFrame
        local panelFrame = self
        local widgets, relayout = {}, nil
        local slotRows = {}

        local function safeDockMetric(object, methodName)
            if not object then
                return nil
            end
            local method = object[methodName]
            if type(method) ~= "function" then
                return nil
            end
            local ok, value = pcall(method, object)
            if not ok then
                return nil
            end
            return tonumber(value) or nil
        end

        local function dockNumbersDiffer(a, b)
            a = tonumber(a)
            b = tonumber(b)
            if a == nil or b == nil then
                return a ~= b
            end
            return math.abs(a - b) > 0.5
        end

        local function isDockInteractionVisible()
            return panelFrame
                and panelFrame.IsShown and panelFrame:IsShown()
                and scrollFrame
                and scrollFrame.IsShown and scrollFrame:IsShown()
        end

        local function getDockObservedScrollMax()
            local observed = 0

            local function absorbRange(owner)
                if not owner or type(owner.GetMinMaxValues) ~= "function" then
                    return
                end
                local ok, _, maxValue = pcall(owner.GetMinMaxValues, owner)
                if ok and type(maxValue) == "number" then
                    observed = math.max(observed, maxValue)
                end
            end

            local nativeRange = safeDockMetric(scrollFrame, "GetVerticalScrollRange")
            if nativeRange ~= nil then
                observed = math.max(observed, nativeRange)
            end

            absorbRange(scrollFrame and scrollFrame.ScrollBar or nil)
            absorbRange(scrollFrame and scrollFrame._paCustomScrollBar or nil)
            return observed
        end

        local function getDockFallbackScrollMax()
            local childHeight = safeDockMetric(scrollChild, "GetHeight") or 0
            local frameHeight = safeDockMetric(scrollFrame, "GetHeight") or 0
            return math.max(0, childHeight - frameHeight)
        end

        local function getDockNativeScrollMax()
            if not isDockInteractionVisible() then
                return nil
            end
            local nativeRange = safeDockMetric(scrollFrame, "GetVerticalScrollRange")
            if nativeRange == nil then
                return nil
            end
            nativeRange = math.max(0, nativeRange)
            if nativeRange <= 0 and getDockFallbackScrollMax() > 1 then
                return nil
            end
            return nativeRange
        end

        local function resolveDockCanonicalScrollMax(options)
            options = type(options) == "table" and options or {}
            local passToken = tonumber(panelFrame._dockScrollLayoutPassToken) or 0
            if panelFrame._dockResolvedNativeScrollPassToken == passToken then
                local cachedNativeMax = tonumber(panelFrame._dockResolvedNativeScrollMax)
                if cachedNativeMax ~= nil then
                    return cachedNativeMax, "native"
                end
            end

            local nativeMax = getDockNativeScrollMax()
            if nativeMax ~= nil then
                panelFrame._dockResolvedNativeScrollPassToken = passToken
                panelFrame._dockResolvedNativeScrollMax = nativeMax
                return nativeMax, "native"
            end

            if options.allowFallback == true then
                return getDockFallbackScrollMax(), "fallback"
            end

            return nil, "unavailable"
        end

        local applyDockScrollState
        local function armDockPostRelayoutSync(passToken)
            passToken = tonumber(passToken) or 0
            if passToken <= 0 or not (C_Timer and C_Timer.After) then
                return false
            end
            if panelFrame._dockPendingPostRelayoutSyncPassToken == passToken then
                return false
            end

            panelFrame._dockPendingPostRelayoutSyncPassToken = passToken
            C_Timer.After(0, function()
                if panelFrame._dockPendingPostRelayoutSyncPassToken ~= passToken then
                    return
                end
                panelFrame._dockPendingPostRelayoutSyncPassToken = nil
                if not isDockInteractionVisible() then
                    return
                end
                if (tonumber(panelFrame._dockScrollLayoutPassToken) or 0) ~= passToken then
                    return
                end
                if applyDockScrollState then
                    applyDockScrollState(nil, {
                        allowFallback = false,
                        allowPostRelayoutSync = false,
                    })
                end
            end)
            return true
        end

        local function ensureDockScrollInteractionBindings()
            panelFrame._dockMouseWheelHandler = panelFrame._dockMouseWheelHandler or function(frame, delta)
                local wheelDelta = tonumber(delta) or 0
                if wheelDelta == 0 then
                    return
                end
                if settingsScrollRuntime.closeFloatingControlsForParentScroll(frame) then
                    return
                end
                settingsScrollRuntime.noteFrameInteraction(frame)
                local currentScroll = safeDockMetric(frame, "GetVerticalScroll") or 0
                local nextScroll = currentScroll - (wheelDelta * 40)
                if applyDockScrollState then
                    applyDockScrollState(nextScroll, {
                        allowFallback = false,
                        allowPostRelayoutSync = false,
                    })
                end
            end

            local needsScroll = (safeDockMetric(scrollChild, "GetHeight") or 0) > ((safeDockMetric(scrollFrame, "GetHeight") or 0) + 1)
            scrollFrame:EnableMouseWheel(needsScroll)
            if scrollFrame.GetScript and scrollFrame.SetScript then
                if needsScroll then
                    if scrollFrame:GetScript("OnMouseWheel") ~= panelFrame._dockMouseWheelHandler then
                        scrollFrame:SetScript("OnMouseWheel", panelFrame._dockMouseWheelHandler)
                    end
                elseif scrollFrame:GetScript("OnMouseWheel") ~= nil then
                    scrollFrame:SetScript("OnMouseWheel", nil)
                end
            end
        end

        applyDockScrollState = function(requestedScroll, options)
            options = type(options) == "table" and options or {}
            if panelFrame._dockScrollSyncActive then
                return false
            end

            local maxScroll, maxSource = resolveDockCanonicalScrollMax({
                allowFallback = options.allowFallback == true,
            })
            if maxScroll == nil then
                if options.allowPostRelayoutSync == true then
                    armDockPostRelayoutSync(tonumber(panelFrame._dockScrollLayoutPassToken) or 0)
                end
                return false
            end

            local currentScroll = safeDockMetric(scrollFrame, "GetVerticalScroll") or 0
            local nextScroll = tonumber(requestedScroll)
            if nextScroll == nil then
                nextScroll = currentScroll
            end
            nextScroll = math.max(0, math.min(maxScroll, nextScroll))

            panelFrame._dockScrollSyncActive = true
            local ok = pcall(function()
                if dockNumbersDiffer(currentScroll, nextScroll) and scrollFrame.SetVerticalScroll then
                    scrollFrame._paSkipNextCustomScrollBarHookSync = true
                    scrollFrame:SetVerticalScroll(nextScroll)
                end
                settingsScrollRuntime.sync(
                    scrollFrame,
                    scrollChild,
                    safeDockMetric(scrollFrame, "GetHeight") or 0,
                    safeDockMetric(scrollChild, "GetHeight") or 0
                )
            end)
            panelFrame._dockScrollSyncActive = nil
            if not ok then
                return false
            end

            if maxSource == "native" then
                panelFrame._dockPendingPostRelayoutSyncPassToken = nil
            end
            panelFrame._dockLastCanonicalScrollMax = maxScroll
            panelFrame._dockLastCanonicalScrollSource = maxSource
            panelFrame._dockExpectedScrollMax = getDockFallbackScrollMax()
            panelFrame._dockObservedScrollMax = math.max(getDockObservedScrollMax(), maxScroll)
            return true
        end

        scrollFrame._paCustomScrollAdapter = {
            panelId = "dock",
            resolveCanonicalMax = function(adapterOptions)
                return resolveDockCanonicalScrollMax(adapterOptions)
            end,
            getCurrentValue = function()
                return safeDockMetric(scrollFrame, "GetVerticalScroll") or 0
            end,
            applyValue = function(value, adapterOptions)
                return applyDockScrollState(value, {
                    allowFallback = adapterOptions and adapterOptions.allowFallback == true,
                    allowPostRelayoutSync = adapterOptions and adapterOptions.allowPostRelayoutSync == true,
                })
            end,
        }

        local function sectionHeader(text)
            local fs = createStyledFontString(scrollChild, "ARTWORK", "GameFontNormalLarge", 14)
            fs:SetText(string.format("|cffffd100%s|r", text))
            return fs
        end

        local function buildDockLayoutTokens()
            return {
                PAGE = {
                    MIN_CONTENT_W = 340,
                    SCROLL_WIDTH_FUDGE = 24,
                    LEFT = 16,
                    RIGHT = 16,
                    NARROW_BREAKPOINT = 700,
                    WIDE_COLUMN_GAP = 26,
                    CHECK_LEFT = 20,
                    LABEL_X = 34,
                    CONTROL_X = 202,
                    PLAYER_HEADER_PAD = 20,
                    OFFSCREEN_X = -5000,
                    OFFSCREEN_Y = -5000,
                    BOTTOM_PAD = 20,
                    ROW_H = 24,
                    ROW_GAP = 4,
                    PAIR_ROW_STEP = 30,
                    DROPDOWN_MIN_W = 120,
                    DROPDOWN_MAX_W = 260,
                    NUMERIC_MIN_W = 74,
                    NUMERIC_MAX_W = 120,
                    SLIDER_MIN_W = 120,
                    SLIDER_MAX_W = 220,
                    CONTROL_WIDTH_RIGHT_PAD = 16,
                    SLIDER_WIDTH_RIGHT_PAD = 36,
                    CONTROL_PLACEMENT = {
                        DROPDOWN_X_SHIFT = -16,
                        DROPDOWN_Y_SHIFT = 4,
                        COMPACT_CONTROL_Y_SHIFT = -1,
                        SLIDER_Y_SHIFT = -2,
                        VALUE_TEXT_GAP = 8,
                    },
                },
                TOP_BLOCK = {
                    TOP_PAD = 20,
                    HEADER_LEFT = 16,
                    CONTENT_RIGHT = 20,
                    HEADER_FLOOR = 16,
                    HEADER_BOTTOM_GAP = 14,
                    LABEL_TO_TOGGLE_GAP = 12,
                    TOGGLE_TO_RESET_GAP = 12,
                    LOCK_ROW_FLOOR = 22,
                    HELPER_GAP_RATIO = 0.625,
                    DIVIDER_GAP_RATIO = 1.25,
                    DIVIDER_OFFSET_RATIO = 0.5,
                    PRESET_HEADER_GAP_RATIO = 1.5,
                    LOCK_STACK_NUDGE_Y = -0.5,
                    PRESERVED_HEADER_ADVANCE = 30,
                    LOCK_TOGGLE_MIN_W = 96,
                    LOCK_BUTTON_MIN_TEXT_W = 36,
                    LOCK_BUTTON_PAD_X = 20,
                    LOCK_SEGMENT_INNER_GAP = 4,
                },
                PRESET_AND_TABLE = {
                    LOADOUT_ROW_SHIFT_Y = 8,
                    PRESET_DIVIDER_FALLBACK_GAP = 4,
                    LOADOUT_PRESET_MIN_W = 260,
                    LOADOUT_PRESET_MAX_W = 360,
                    LOADOUT_RIGHT_PAD = 6,
                    MATCH_STATUS_GAP = 2,
                    TABLE_BLOCK_SHIFT_Y = 50,
                    DESTINATION_COLUMN_Y_SHIFT = 40,
                    NAME_SPELL_COLUMNS_Y_SHIFT = 40,
                    RESULT_COLUMN_Y_SHIFT = 46,
                    STATUS_TEXT_Y_SHIFT = 33,
                    SLOT_ENABLE_CHECKBOX_Y_SHIFT = 41,
                    SLOT_HEADER_TEXT_X_SHIFT = 5,
                    TABLE_HEADER_BOTTOM_GAP = 18,
                    SLOT_ENABLE_GAP = 12,
                    SLOT_DEST_MIN_W = 170,
                    SLOT_DEST_RATIO = 0.34,
                    SLOT_NAME_MIN_W = 120,
                    SLOT_SPELL_W = 68,
                    SLOT_RESULT_W = 30,
                    TABLE_COLUMN_GAP = 14,
                    SLOT_ROW_TOP_NUDGE = 2,
                    SLOT_RESULT_TOP_NUDGE = 4,
                    SLOT_ROW_STEP = 34,
                },
                LOWER_SECTIONS = {
                    BLOCK_OFFSET_Y = 30,
                    TABLE_TO_DIVIDER_GAP = 4,
                    DIVIDER_TO_LAYOUT_GAP = 24,
                    SEGMENTED_MIN_W = 220,
                    SEGMENTED_MAX_W = 360,
                    CONTROL_RIGHT_PAD = 12,
                    SLIDER_CONTROL_MIN_W = 162,
                    SLIDER_CONTROL_MAX_W = 230,
                    LAYOUT_STEPPER_MIN_W = 132,
                    LAYOUT_STEPPER_MAX_W = 154,
                    FONT_SIZE_STEPPER_MIN_W = 56,
                    FONT_SIZE_STEPPER_MAX_W = 68,
                    OUTLINE_ROW_MIN_W = 120,
                    OUTLINE_ROW_MAX_W = 150,
                    SECTION_HEADER_STEP = 26,
                    ROW_STEP = 34,
                    WRAP_PRESENT_TIGHTEN = 5,
                    LAYOUT_HINT_TOP_NUDGE = 3,
                    LAYOUT_HINT_STEP = 18,
                    SECTION_GAP_TIGHT = 4,
                    SECTION_GAP_LOOSE = 6,
                    VISIBILITY_DIVIDER_TOP_GAP = 4,
                    VISIBILITY_DIVIDER_STEP = 12,
                    VISIBILITY_BLOCK_SHIFT_Y = 16,
                    VISIBILITY_LABELS_SHIFT_Y = 5,
                    VISIBILITY_HEADER_STEP = 24,
                    FONT_TO_SIZE_GAP = 5,
                    SIZE_TO_SWATCH_GAP = 1,
                    FONT_ROW_CONTROL_MIN_W = 260,
                    FONT_PICKER_MIN_W = 118,
                    FONT_ROW_STEP = 36,
                },
                FIXED_FOOTPRINTS = {
                    SEED_W = 1,
                    LOCK_ROW_SEED_H = 22,
                    LOWER_SECTIONS_SEED_H = 1,
                    RESET_ANCHOR_W = 102,
                    RESET_ANCHOR_H = 22,
                    VISIBILITY_LABEL_SPACER_H = 22,
                    VISIBILITY_SPACER_H = 22,
                    ROW_ENABLE_HOLDER_W = 132,
                    ROW_ENABLE_HOLDER_H = 22,
                    ROW_ENABLE_BUTTON_W = 64,
                    ROW_ENABLE_BUTTON_H = 22,
                    STANDARD_CONTROL_H = 22,
                    DIVIDER_H = 1,
                    FONT_COLOR_SWATCH_SIZE = 16,
                    LAYOUT_STEPPER_SEED_W = 168,
                    LAYOUT_ICON_SLIDER_SEED_W = 260,
                    FONT_PICKER_SEED_W = 180,
                    FONT_SIZE_STEPPER_SEED_W = 168,
                },
            }
        end

        local dockLayoutTokens = buildDockLayoutTokens()
        local PAGE = dockLayoutTokens.PAGE
        local TOP_BLOCK = dockLayoutTokens.TOP_BLOCK
        local PRESET_AND_TABLE = dockLayoutTokens.PRESET_AND_TABLE
        local LOWER_SECTIONS = dockLayoutTokens.LOWER_SECTIONS
        local FIXED_FOOTPRINTS = dockLayoutTokens.FIXED_FOOTPRINTS

        local function rebuildDock()
            if PortalAuthority.RebuildDock then PortalAuthority:RebuildDock() end
            if PortalAuthority.ApplyDockPosition then PortalAuthority:ApplyDockPosition() end
        end

        local function refreshDockVisibility()
            if PortalAuthority.ApplyDockCombatVisibilityDriver then
                PortalAuthority:ApplyDockCombatVisibilityDriver()
            end

            if InCombatLockdown() then
                if PortalAuthority.QueueDockVisibilityRefresh then
                    PortalAuthority:QueueDockVisibilityRefresh()
                end
                return
            end

            if PortalAuthority.UpdateDockVisibility then
                PortalAuthority:UpdateDockVisibility(true)
            elseif PortalAuthority.RefreshDockVisibility then
                PortalAuthority:RefreshDockVisibility(true)
            end
        end

        local function queueDockPanelRefresh()
            if not panel or panel._paDockRefreshQueued then
                return
            end
            panel._paDockRefreshQueued = true
            if C_Timer and C_Timer.After then
                C_Timer.After(0, function()
                    if panel then
                        panel._paDockRefreshQueued = nil
                    end
                    if not panel or not panel.IsShown or not panel:IsShown() or not panel._refresh then
                        return
                    end
                    if isControlHydrating(panel) then
                        return
                    end
                    panel:_refresh()
                end)
                return
            end
            panel._paDockRefreshQueued = nil
            if panel and panel.IsShown and panel:IsShown() and panel._refresh and not isControlHydrating(panel) then
                panel:_refresh()
            end
        end

        local function applyLiveDockXY(x, y)
            if widgets.x and widgets.x.SetValue then
                widgets.x:SetValue(x)
            end
            if widgets.y and widgets.y.SetValue then
                widgets.y:SetValue(y)
            end

            if PortalAuthorityDB.dockX == nil then
                PortalAuthorityDB.dockX = x
            end
            if PortalAuthorityDB.dockY == nil then
                PortalAuthorityDB.dockY = y
            end
        end

        local refreshDockXYDisplay

        refreshDockXYDisplay = function()
            local x, y
            if PortalAuthority and type(PortalAuthority._lastDockX) == "number" and type(PortalAuthority._lastDockY) == "number" then
                x, y = PortalAuthority._lastDockX, PortalAuthority._lastDockY
            else
                x, y = PortalAuthorityDB.dockX or 0, PortalAuthorityDB.dockY or 0
            end

            applyLiveDockXY(x, y)
        end

        widgets.playerHeader = createTopRightHeader(self, -PAGE.PLAYER_HEADER_PAD)
        widgets.header = sectionHeader("Mythic+ Dock")
        widgets.sectionDescriptor = createSettingsSectionDescriptor(scrollChild, "Configure your custom portal layout.")
        widgets.positionLabel = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlight", 12)
        widgets.positionLabel:SetText("")
        widgets.positionLabel:SetTextColor(1, 1, 1, 1)

        widgets.lockDockDivider = scrollChild:CreateTexture(nil, "ARTWORK")
        widgets.lockDockDivider:SetColorTexture(0.24, 0.27, 0.32, 0.10)
        widgets.hideDungeonDivider = scrollChild:CreateTexture(nil, "ARTWORK")
        widgets.hideDungeonDivider:SetColorTexture(0.24, 0.27, 0.32, 0.10)
        widgets.visibilityDivider = scrollChild:CreateTexture(nil, "ARTWORK")
        widgets.visibilityDivider:SetColorTexture(0.24, 0.27, 0.32, 0.10)
        widgets.loadoutDivider = scrollChild:CreateTexture(nil, "ARTWORK")
        widgets.loadoutDivider:SetColorTexture(0.24, 0.27, 0.32, 0.10)
        widgets.masterRowContainer = CreateFrame("Frame", nil, scrollChild)
        widgets.masterRowContainer:SetSize(FIXED_FOOTPRINTS.SEED_W, FIXED_FOOTPRINTS.LOCK_ROW_SEED_H)
        widgets.lockRowContainer = CreateFrame("Frame", nil, scrollChild)
        widgets.lockRowContainer:SetSize(FIXED_FOOTPRINTS.SEED_W, FIXED_FOOTPRINTS.LOCK_ROW_SEED_H)
        widgets.lowerSectionsContainer = CreateFrame("Frame", nil, scrollChild)
        widgets.lowerSectionsContainer:SetSize(FIXED_FOOTPRINTS.SEED_W, FIXED_FOOTPRINTS.LOWER_SECTIONS_SEED_H)

        widgets.resetDockAnchor = createActionButton(scrollChild, "Reset Anchor", function()
            PortalAuthorityDB.dockLocked = false
            PortalAuthorityDB.dockX, PortalAuthorityDB.dockY = 0, 0
            if PortalAuthority.ResetDockAnchor then
                PortalAuthority:ResetDockAnchor()
            elseif PortalAuthority.ApplyDockPosition then
                PortalAuthority:ApplyDockPosition()
            end
            if PortalAuthority.UpdateDockMovableState then
                PortalAuthority:UpdateDockMovableState()
            end
            refreshDockVisibility()
            if PortalAuthority and PortalAuthority.dockFrame then
                PortalAuthority.dockFrame:SetFrameStrata("HIGH")
                PortalAuthority.dockFrame:Raise()
                if PortalAuthority.dockDragHandle then
                    PortalAuthority.dockDragHandle:SetFrameStrata(PortalAuthority.dockFrame:GetFrameStrata())
                    PortalAuthority.dockDragHandle:SetFrameLevel((PortalAuthority.dockFrame:GetFrameLevel() or 1) + 2)
                end
            end
            if PortalAuthority.NotifyDockPositionChanged then
                PortalAuthority:NotifyDockPositionChanged(PortalAuthorityDB.dockX, PortalAuthorityDB.dockY)
            end
            if widgets.lockDock and widgets.lockDock.Refresh then
                widgets.lockDock:Refresh()
            end
            if relayout then relayout() end
        end, FIXED_FOOTPRINTS.RESET_ANCHOR_W)

        widgets.enableLabel = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlight", 12)
        widgets.enableLabel:SetText("Visibility Rules")
        widgets.enableLabel:SetTextColor(1, 1, 1, 1)
        widgets.visibilityLabelSpacer = CreateFrame("Frame", nil, scrollChild)
        widgets.visibilityLabelSpacer:SetSize(FIXED_FOOTPRINTS.SEED_W, FIXED_FOOTPRINTS.VISIBILITY_LABEL_SPACER_H)
        widgets.visibilitySpacer = CreateFrame("Frame", nil, scrollChild)
        widgets.visibilitySpacer:SetSize(FIXED_FOOTPRINTS.SEED_W, FIXED_FOOTPRINTS.VISIBILITY_SPACER_H)
        widgets.dockCustomizationLabel = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlight", 13)
        widgets.dockCustomizationLabel:SetText("")
        widgets.dockCustomizationLabel:SetTextColor(1, 1, 1, 1)
        widgets.lockStateHelper = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 10)
        widgets.lockStateHelper:SetText("")
        widgets.lockStateHelper:SetTextColor(0.58, 0.58, 0.58, 0.92)
        widgets.lockStateHelper:SetWordWrap(false)
        if widgets.lockStateHelper.SetMaxLines then
            widgets.lockStateHelper:SetMaxLines(1)
        end
        widgets.dockCustomizationHelper = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 10)
        widgets.dockCustomizationHelper:SetText("Applies preset destinations for the selected Mythic+ Dock preset.")
        widgets.dockCustomizationHelper:SetTextColor(0.58, 0.58, 0.58, 0.92)
        widgets.dockCustomizationMatchStatus = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 10)
        widgets.dockCustomizationMatchStatus:SetText("")
        widgets.dockCustomizationMatchStatus:SetTextColor(0.66, 0.66, 0.66, 0.94)
        widgets.dockCustomizationMatchStatus:SetWordWrap(false)
        widgets.dockCustomizationMatchStatus:SetJustifyH("LEFT")

        local function getSpellNameByID(spellID)
            if type(spellID) ~= "number" or spellID <= 0 then
                return ""
            end
            if C_Spell and C_Spell.GetSpellInfo then
                local info = C_Spell.GetSpellInfo(spellID)
                if type(info) == "table" and type(info.name) == "string" and info.name ~= "" then
                    return info.name
                end
            end
            if C_Spell and C_Spell.GetSpellName then
                local name = C_Spell.GetSpellName(spellID)
                if type(name) == "string" then
                    return name
                end
            end
            if GetSpellInfo then
                local name = GetSpellInfo(spellID)
                if type(name) == "string" then
                    return name
                end
            end
            return ""
        end

        local function getSpellTextureByID(spellID)
            if type(spellID) ~= "number" or spellID <= 0 then
                return nil
            end
            if C_Spell and C_Spell.GetSpellInfo then
                local info = C_Spell.GetSpellInfo(spellID)
                if type(info) == "table" and info.iconID then
                    return info.iconID
                end
            end
            if C_Spell and C_Spell.GetSpellTexture then
                return C_Spell.GetSpellTexture(spellID)
            end
            if GetSpellTexture then
                return GetSpellTexture(spellID)
            end
            if GetSpellInfo then
                local _, _, texture = GetSpellInfo(spellID)
                return texture
            end
            return nil
        end

        local DOCK_NAME_MAX_LEN = 10
        local getDockSlot
        local resolveSlotName
        local resolveSlotDefaultLabel
        local setDockSlotDefaultName
        local normalizeDockSlotRenameState
        local setDockSlotCustomName
        local getDockSlotDisplayName
        local pendingSpellLoads = {}
        local function requestSpellDataRefresh(slotIndex, spellID)
            if type(spellID) ~= "number" or spellID <= 0 then
                return
            end
            if pendingSpellLoads[slotIndex] == spellID then
                return
            end
            pendingSpellLoads[slotIndex] = spellID

            if C_Spell and C_Spell.RequestLoadSpellData then
                pcall(C_Spell.RequestLoadSpellData, spellID)
            end

            local attempts = 0
            local function tryResolve()
                if pendingSpellLoads[slotIndex] ~= spellID then
                    return
                end

                local slot = getDockSlot(slotIndex)
                if slot.spellID ~= spellID then
                    pendingSpellLoads[slotIndex] = nil
                    return
                end

                if slot.renamed ~= true then
                    setDockSlotDefaultName(slot, spellID)
                end
                local resolved = trim(slot.name or "")
                local texture = getSpellTextureByID(spellID)
                if texture then
                    pendingSpellLoads[slotIndex] = nil
                    queueDockPanelRefresh()
                    if PortalAuthority and PortalAuthority.RebuildDock then
                        PortalAuthority:RebuildDock()
                    end
                    return
                end
                if resolved ~= "" then
                    pendingSpellLoads[slotIndex] = nil
                    return
                end

                attempts = attempts + 1
                if attempts >= 10 or not (C_Timer and C_Timer.After) then
                    pendingSpellLoads[slotIndex] = nil
                    return
                end
                C_Timer.After(0.12, tryResolve)
            end

            tryResolve()
        end

        getDockSlot = function(index)
            PortalAuthorityDB.dockSlots = PortalAuthorityDB.dockSlots or {}
            local slot = PortalAuthorityDB.dockSlots[index]
            if type(slot) ~= "table" then
                slot = {}
                PortalAuthorityDB.dockSlots[index] = slot
            end
            if slot.enabled == nil then slot.enabled = false end
            if slot.selection == nil or slot.selection == "" then
                local spellID = math.floor(tonumber(slot.spellID) or 0)
                slot.selection = (spellID > 0) and tostring(spellID) or "CUSTOM"
            end
            slot.enabled = PA_SafeBool(slot.enabled)
            slot.selection = tostring(slot.selection or "CUSTOM")
            slot.spellID = math.max(0, math.floor(tonumber(slot.spellID) or 0))
            slot.name = tostring(slot.name or "")
            normalizeDockSlotRenameState(slot)
            return slot
        end

        resolveSlotName = function(spellID, fallback)
            local spellName = trim(getSpellNameByID(spellID))
            if spellName ~= "" then
                return spellName
            end
            local map = PortalAuthority and PortalAuthority.SpellMap
            local mapEntry = map and map[spellID]
            if mapEntry and trim(mapEntry.dest) ~= "" then
                return trim(mapEntry.dest)
            end
            return trim(fallback or "")
        end

        resolveSlotDefaultLabel = function(spellID, fallback)
            local map = PortalAuthority and PortalAuthority.SpellMap
            local mapEntry = map and map[spellID]
            local short = trim((mapEntry and mapEntry.short) or "")
            if short ~= "" then
                return short
            end
            local fallbackText = trim(fallback or "")
            if fallbackText ~= "" then
                return fallbackText
            end
            local spellName = trim(getSpellNameByID(spellID))
            if spellName ~= "" then
                return spellName
            end
            if mapEntry and trim(mapEntry.dest) ~= "" then
                return trim(mapEntry.dest)
            end
            return ""
        end

        local function getDockSlotCanonicalDefaultName(spellID)
            local sid = math.max(0, math.floor(tonumber(spellID) or 0))
            if sid <= 0 then
                return ""
            end
            return (resolveSlotDefaultLabel and resolveSlotDefaultLabel(sid, "")) or resolveSlotName(sid, "")
        end

        setDockSlotDefaultName = function(slot, spellID)
            if type(slot) ~= "table" then
                return ""
            end
            local sid = math.max(0, math.floor(tonumber(spellID ~= nil and spellID or slot.spellID) or 0))
            local defaultName = getDockSlotCanonicalDefaultName(sid)
            slot.name = defaultName
            slot.renamed = false
            return defaultName
        end

        normalizeDockSlotRenameState = function(slot)
            if type(slot) ~= "table" then
                return slot
            end
            slot.name = tostring(slot.name or "")
            slot.renamed = slot.renamed == true
            if slot.renamed then
                if trim(slot.name or "") == "" then
                    setDockSlotDefaultName(slot, slot.spellID)
                end
            else
                setDockSlotDefaultName(slot, slot.spellID)
            end
            return slot
        end

        setDockSlotCustomName = function(slot, text)
            if type(slot) ~= "table" then
                return ""
            end
            local customName = trim(text or "")
            if string.len(customName) > DOCK_NAME_MAX_LEN then
                customName = customName:sub(1, DOCK_NAME_MAX_LEN)
            end
            local defaultName = getDockSlotCanonicalDefaultName(slot.spellID)
            if customName == "" or customName == defaultName then
                return setDockSlotDefaultName(slot, slot.spellID)
            end
            slot.name = customName
            slot.renamed = true
            return customName
        end

        getDockSlotDisplayName = function(slot)
            if type(slot) ~= "table" then
                return ""
            end
            normalizeDockSlotRenameState(slot)
            return trim(slot.name or "")
        end

        local function getSpellMapEntryBySpellID(spellID)
            local map = PortalAuthority and PortalAuthority.SpellMap
            return map and map[spellID] or nil
        end

        local function getVisualDockIDForSpell(spellID)
            local sid = math.max(0, math.floor(tonumber(spellID) or 0))
            if sid <= 0 then
                return 0
            end
            local entry = getSpellMapEntryBySpellID(sid)
            local itemID = entry and math.floor(tonumber(entry.useItemID) or 0) or 0
            if itemID > 0 then
                return itemID
            end
            return sid
        end

        local function resolveSpellIDFromVisualDockID(rawValue)
            local visualID = math.max(0, math.floor(tonumber(rawValue) or 0))
            if visualID <= 0 then
                return 0
            end
            local map = PortalAuthority and PortalAuthority.SpellMap
            if type(map) == "table" then
                for sid, entry in pairs(map) do
                    local itemID = entry and math.floor(tonumber(entry.useItemID) or 0) or 0
                    if itemID > 0 and itemID == visualID then
                        local spellID = math.floor(tonumber(sid) or 0)
                        if spellID > 0 then
                            return spellID
                        end
                    end
                end
            end
            return visualID
        end

        local destinationChoicesCache = nil
        local destinationChoiceByValue = nil

        local function buildAcronym(text)
            local out = {}
            for token in tostring(text or ""):gmatch("%a+") do
                table.insert(out, token:sub(1, 1):upper())
            end
            return table.concat(out, "")
        end

        local DOCK_EXPANSION_BY_SPELL_ID = {
            [1254572] = "MIDNIGHT",
            [1254559] = "MIDNIGHT",
            [1254563] = "MIDNIGHT",
            [1254400] = "MIDNIGHT",
            [445440] = "TWW",
            [445417] = "TWW",
            [445416] = "TWW",
            [445441] = "TWW",
            [1237215] = "TWW",
            [1239155] = "TWW",
            [1216786] = "TWW",
            [445444] = "TWW",
            [445414] = "TWW",
            [445443] = "TWW",
            [445269] = "TWW",
            [1226482] = "TWW",
            [432257] = "DF",
            [393273] = "DF",
            [432258] = "DF",
            [393267] = "DF",
            [393283] = "DF",
            [393276] = "DF",
            [393256] = "DF",
            [424197] = "DF",
            [393279] = "DF",
            [393262] = "DF",
            [393222] = "DF",
            [432254] = "DF",
            [373190] = "SL",
            [354468] = "SL",
            [354465] = "SL",
            [354463] = "SL",
            [373191] = "SL",
            [354469] = "SL",
            [373192] = "SL",
            [354466] = "SL",
            [367416] = "SL",
            [354462] = "SL",
            [354467] = "SL",
            [424187] = "BFA",
            [410071] = "BFA",
            [464256] = "BFA",
            [467555] = "BFA",
            [410074] = "BFA",
            [424167] = "BFA",
            [424153] = "LEGION",
            [393766] = "LEGION",
            [424163] = "LEGION",
            [393764] = "LEGION",
            [373262] = "LEGION",
            [410078] = "LEGION",
            [1254551] = "LEGION",
            [159897] = "WOD",
            [159895] = "WOD",
            [159900] = "WOD",
            [159896] = "WOD",
            [159899] = "WOD",
            [159898] = "WOD",
            [159901] = "WOD",
            [159902] = "WOD",
            [131225] = "MOP",
            [131222] = "MOP",
            [131231] = "MOP",
            [131229] = "MOP",
            [131232] = "MOP",
            [131206] = "MOP",
            [131228] = "MOP",
            [131205] = "MOP",
            [131204] = "MOP",
            [445424] = "CATA",
            [410080] = "CATA",
            [424142] = "CATA",
            [1254555] = "WOTLK",
        }

        local DOCK_EXPANSION_BADGE_STYLE = {
            MIDNIGHT = { abbr = "MID", rgb = { 0.63, 0.52, 0.93 } },
            TWW = { abbr = "TWW", rgb = { 0.93, 0.73, 0.24 } },
            DF = { abbr = "DF", rgb = { 0.36, 0.78, 0.95 } },
            SL = { abbr = "SL", rgb = { 0.45, 0.63, 1.00 } },
            BFA = { abbr = "BFA", rgb = { 0.96, 0.77, 0.29 } },
            LEGION = { abbr = "LEG", rgb = { 0.38, 0.94, 0.29 } },
            WOD = { abbr = "WOD", rgb = { 0.89, 0.45, 0.27 } },
            MOP = { abbr = "MOP", rgb = { 0.41, 0.88, 0.57 } },
            CATA = { abbr = "CATA", rgb = { 0.96, 0.40, 0.33 } },
            WOTLK = { abbr = "WOTLK", rgb = { 0.47, 0.72, 0.95 } },
        }

        local function getDockExpansionKey(spellID, info)
            local sid = math.floor(tonumber(spellID) or 0)
            if sid <= 0 then
                return nil
            end
            local mapExpansion = trim((info and info.expansion) or "")
            if mapExpansion ~= "" then
                return mapExpansion:upper()
            end
            return DOCK_EXPANSION_BY_SPELL_ID[sid]
        end

        local function getDockExpansionBadgeMarkup(expansionKey)
            local key = trim(tostring(expansionKey or "")):upper()
            if key == "" then
                return ""
            end
            local style = DOCK_EXPANSION_BADGE_STYLE[key]
            if not style then
                return ""
            end
            local rgb = style.rgb or { 0.75, 0.75, 0.75 }
            local r = math.max(0, math.min(255, math.floor((tonumber(rgb[1]) or 0.75) * 255 + 0.5)))
            local g = math.max(0, math.min(255, math.floor((tonumber(rgb[2]) or 0.75) * 255 + 0.5)))
            local b = math.max(0, math.min(255, math.floor((tonumber(rgb[3]) or 0.75) * 255 + 0.5)))
            local icon = string.format("|TInterface\\Buttons\\WHITE8x8:10:10:0:0:8:8:0:8:0:8:%d:%d:%d:255|t", r, g, b)
            return string.format("%s |cffb8b8b8%s|r", icon, style.abbr or key)
        end

        local function getDestinationChoices()
            if destinationChoicesCache and destinationChoiceByValue then
                return destinationChoicesCache
            end

            local options = {}
            local mapByValue = {}
            local entries = {}
            local categoryOrder = { portals = 1, teleports = 2, mplus = 3, raid = 3 }
            local categoryLabel = { portals = "Portal", teleports = "Teleport", mplus = "Mythic+", raid = "Raid" }
            local spellMap = (PortalAuthority and PortalAuthority.SpellMap) or {}
            local playerClass = nil
            local playerFaction = nil
            if UnitClass then
                _, playerClass = UnitClass("player")
            end
            if UnitFactionGroup then
                playerFaction = UnitFactionGroup("player")
            end
            local function getClassLabel(classFile)
                if type(classFile) ~= "string" or classFile == "" then
                    return ""
                end
                return tostring((LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE[classFile]) or (LOCALIZED_CLASS_NAMES_FEMALE and LOCALIZED_CLASS_NAMES_FEMALE[classFile]) or classFile)
            end
            local function normalizeCityKey(name)
                local key = tostring(name or "")
                key = key:gsub("?", "'")
                key = key:gsub("`", "'")
                return key:lower()
            end
            local ALLIANCE_CITY_DESTS = {
                [normalizeCityKey("Stormwind")] = true,
                [normalizeCityKey("Ironforge")] = true,
                [normalizeCityKey("Darnassus")] = true,
                [normalizeCityKey("Exodar")] = true,
                [normalizeCityKey("Theramore")] = true,
                [normalizeCityKey("Stormshield")] = true,
                [normalizeCityKey("Boralus")] = true,
            }
            local HORDE_CITY_DESTS = {
                [normalizeCityKey("Orgrimmar")] = true,
                [normalizeCityKey("Undercity")] = true,
                [normalizeCityKey("Thunder Bluff")] = true,
                [normalizeCityKey("Silvermoon")] = true,
                [normalizeCityKey("Silvermoon (TBC)")] = true,
                [normalizeCityKey("Stonard")] = true,
                [normalizeCityKey("Warspear")] = true,
                [normalizeCityKey("Dazar'alor")] = true,
            }

            local custom = {
                label = "Custom",
                secondary = "Use Spell ID for manual assignment",
                buttonLabel = "Custom",
                value = "CUSTOM",
                aliases = { "custom", "manual", "spell id" },
            }
            table.insert(options, custom)
            mapByValue[custom.value] = custom

            for spellID, info in pairs(spellMap) do
                local sid = tonumber(spellID)
                local category = info and info.category or nil
                if sid and categoryOrder[category] then
                    local visualID = getVisualDockIDForSpell(sid)
                    local displayCategory = trim((info and info.displayCategory) or "")
                    if displayCategory == "" then
                        displayCategory = category
                    end
                    local displayOrder = categoryOrder[displayCategory] or categoryOrder[category]
                    local displayLabel = categoryLabel[displayCategory] or categoryLabel[category] or tostring(category or "")
                    local displayName = trim((info and info.dest) or "")
                    if displayName == "" then
                        displayName = trim(resolveSlotName(sid, ""))
                    end
                    if displayName == "" then
                        displayName = "Spell " .. sid
                    end
                    local spellName = trim(getSpellNameByID(sid))
                    if spellName == "" then
                        spellName = displayName
                    end
                    local shortName = trim((info and info.short) or "")
                    local requiredClass = trim((info and info.class) or "")
                    if requiredClass == "" and (category == "portals" or category == "teleports") then
                        requiredClass = "MAGE"
                    end
                    if requiredClass == "ANY" then
                        requiredClass = ""
                    end
                    local classLocked = requiredClass ~= "" and playerClass ~= requiredClass
                    local factionLocked = false
                    if playerClass == "MAGE" and (playerFaction == "Alliance" or playerFaction == "Horde") then
                        local cityKey = normalizeCityKey(displayName)
                        if playerFaction == "Alliance" and HORDE_CITY_DESTS[cityKey] then
                            factionLocked = true
                        elseif playerFaction == "Horde" and ALLIANCE_CITY_DESTS[cityKey] then
                            factionLocked = true
                        end
                    end
                    local optionLocked = classLocked or factionLocked
                    local classLabel = getClassLabel(requiredClass)
                    local secondary = string.format("%s  |  ID %d  |  %s", displayLabel, visualID, spellName)
                    local expansionBadge = getDockExpansionBadgeMarkup(getDockExpansionKey(sid, info))
                    if expansionBadge ~= "" then
                        secondary = string.format("%s  |  %s", secondary, expansionBadge)
                    end
                    if classLabel ~= "" then
                        secondary = string.format("%s  |  %s only", secondary, classLabel)
                    end
                    if classLocked then
                        secondary = string.format("%s  |  Unavailable on this class", secondary)
                    end
                    if factionLocked then
                        secondary = string.format("%s  |  Unavailable for your faction", secondary)
                    end
                    local aliases = {
                        tostring(sid),
                        displayName,
                        spellName,
                        shortName,
                        buildAcronym(displayName),
                    }
                    if visualID ~= sid then
                        table.insert(aliases, tostring(visualID))
                    end
                    if classLabel ~= "" then
                        table.insert(aliases, classLabel)
                    end
                    table.insert(entries, {
                        order = displayOrder,
                        name = displayName,
                        value = tostring(sid),
                        label = displayName ~= "" and displayName or ("Spell " .. sid),
                        buttonLabel = string.format("%s: %s", displayLabel, displayName ~= "" and displayName or ("Spell " .. sid)),
                        secondary = string.format("%s  ?  ID %d", categoryLabel[category], sid),
                        aliases = aliases,
                    })
                    local added = entries[#entries]
                    if added then
                        added.secondary = secondary
                        added.disabled = optionLocked
                    end
                end
            end

            table.sort(entries, function(a, b)
                if a.order ~= b.order then
                    return a.order < b.order
                end
                local an = a.name:lower()
                local bn = b.name:lower()
                if an ~= bn then
                    return an < bn
                end
                return tonumber(a.value) < tonumber(b.value)
            end)

            for _, entry in ipairs(entries) do
                table.insert(options, entry)
                mapByValue[entry.value] = entry
            end

            destinationChoicesCache = options
            destinationChoiceByValue = mapByValue
            return options
        end

        local function getDestinationOptionByValue(value)
            getDestinationChoices()
            return destinationChoiceByValue and destinationChoiceByValue[tostring(value or "CUSTOM")] or nil
        end

        local function setDropdownEnabled(spec, enabled)
            if not spec or not spec.control then
                return
            end
            if UIDropDownMenu_EnableDropDown and UIDropDownMenu_DisableDropDown then
                if enabled then
                    UIDropDownMenu_EnableDropDown(spec.control)
                else
                    UIDropDownMenu_DisableDropDown(spec.control)
                end
            end
            spec.control:SetAlpha(enabled and 1 or 0.6)
        end

        local function applyDockChanges(reposition)
            rebuildDock()
            if reposition and PortalAuthority.ApplyDockPosition then
                PortalAuthority:ApplyDockPosition()
            end
            refreshDockVisibility()
        end

        local function onUIChanged(reposition)
            applyDockChanges(reposition)
            local dockSearchPanel = settingsSearchRuntime.getPanelFromObject(scrollChild)
            if dockSearchPanel then
                dockSearchPanel._paSearchRegistryDirty = true
            end
            if relayout then relayout() end
        end

        local function ApplyDockLayoutSettings(reposition)
            if PortalAuthority and PortalAuthority.ApplyDockLayoutSettings then
                PortalAuthority:ApplyDockLayoutSettings(reposition and true or false)
            else
                applyDockChanges(reposition)
            end
            if relayout then relayout() end
        end

        local function ApplyDockAppearanceSettings()
            if PortalAuthority and PortalAuthority.ApplyDockAppearanceSettings then
                PortalAuthority:ApplyDockAppearanceSettings()
            else
                applyDockChanges(false)
            end
            if relayout then relayout() end
        end

        local function normalizeDockSimpleLayoutModeValue(value)
            local mode = string.upper(trim(tostring(value or "GRID")))
            if mode == "HORIZONTAL" then
                mode = "HORIZONTAL_ROW"
            elseif mode == "VERTICAL" then
                mode = "VERTICAL_COLUMN"
            end
            if mode ~= "HORIZONTAL_ROW" and mode ~= "VERTICAL_COLUMN" and mode ~= "GRID" then
                mode = "GRID"
            end
            return mode
        end

        local function normalizeDockLabelModeValue(value)
            if value == false then
                return "OFF"
            end
            if value == true then
                return "OUTSIDE"
            end
            local mode = string.upper(trim(tostring(value or "OUTSIDE")))
            if mode ~= "OFF" and mode ~= "OUTSIDE" and mode ~= "INSIDE" then
                mode = "OUTSIDE"
            end
            return mode
        end

        local function normalizeDockLabelSideValue(value)
            local side = string.upper(trim(tostring(value or "BOTTOM")))
            if side ~= "BOTTOM" and side ~= "TOP" and side ~= "LEFT" and side ~= "RIGHT" and side ~= "CENTER" then
                side = "BOTTOM"
            end
            return side
        end

        local function isOutsideDockLabelSideValue(value)
            return value == "BOTTOM" or value == "TOP" or value == "LEFT" or value == "RIGHT"
        end

        local function normalizeDockOutsideLabelSideValue(value, fallback)
            local resolvedFallback = string.upper(trim(tostring(fallback or "BOTTOM")))
            if resolvedFallback ~= "BOTTOM" and resolvedFallback ~= "TOP" and resolvedFallback ~= "LEFT" and resolvedFallback ~= "RIGHT" then
                resolvedFallback = "BOTTOM"
            end

            local side = string.upper(trim(tostring(value or resolvedFallback)))
            if side ~= "BOTTOM" and side ~= "TOP" and side ~= "LEFT" and side ~= "RIGHT" then
                side = resolvedFallback
            end
            if side ~= "BOTTOM" and side ~= "TOP" and side ~= "LEFT" and side ~= "RIGHT" then
                side = "BOTTOM"
            end
            return side
        end

        local function normalizeOutlineToggleValue(value)
            if type(value) == "string" then
                local upper = string.upper(trim(value))
                if upper == "OUTLINE" or upper == "THICKOUTLINE" then
                    return "OUTLINE"
                end
            end
            return ""
        end

        local function normalizeHoverGlowToggleValue(value)
            if value == true then
                return true
            end
            if value == false or value == nil then
                return false
            end
            if type(value) == "number" then
                return value ~= 0
            end
            if type(value) == "string" then
                local lower = string.lower(trim(value))
                if lower == "true" or lower == "1" then
                    return true
                end
                return false
            end
            return PA_SafeBool(value)
        end

        local function getDockSimpleLayoutModeForSettings(defaults)
            local db = PortalAuthorityDB or {}
            local fallback = (defaults and defaults.dockSimpleLayoutMode) or "GRID"

            -- Runtime effective mode is authoritative for what the dock is currently rendering.
            if PortalAuthority and PortalAuthority._dockEffectiveLayoutMode ~= nil then
                local runtimeMode = normalizeDockSimpleLayoutModeValue(PortalAuthority._dockEffectiveLayoutMode)
                db.dockSimpleLayoutMode = runtimeMode
                db.dockSimpleLayoutModePersist = runtimeMode
                return runtimeMode
            end

            if db.dockSimpleLayoutMode ~= nil then
                local persistedMode = normalizeDockSimpleLayoutModeValue(db.dockSimpleLayoutMode)
                db.dockSimpleLayoutMode = persistedMode
                db.dockSimpleLayoutModePersist = persistedMode
                return persistedMode
            end

            if db.dockSimpleLayoutModePersist ~= nil then
                local fallbackPersistMode = normalizeDockSimpleLayoutModeValue(db.dockSimpleLayoutModePersist)
                db.dockSimpleLayoutMode = fallbackPersistMode
                db.dockSimpleLayoutModePersist = fallbackPersistMode
                return fallbackPersistMode
            end

            local normalizedFallback = normalizeDockSimpleLayoutModeValue(fallback)
            db.dockSimpleLayoutMode = normalizedFallback
            db.dockSimpleLayoutModePersist = normalizedFallback
            return normalizedFallback
        end

        local function syncLayoutModeSegmentSelection(forceVisualOnly)
            local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
            local resolvedMode = getDockSimpleLayoutModeForSettings(defaults)
            if widgets.layoutModeSegment and widgets.layoutModeSegment.Refresh and not forceVisualOnly then
                widgets.layoutModeSegment:Refresh()
            end
            if widgets.layoutModeSegment and widgets.layoutModeSegment.SetVisualSelectedValue then
                widgets.layoutModeSegment:SetVisualSelectedValue(resolvedMode)
            end
        end

        local function getDockLabelModeForSettings(defaults)
            if ensureDatabase then
                ensureDatabase()
            end
            local db = PortalAuthorityDB or {}
            local fallback = (defaults and defaults.dockLabelMode) or "OUTSIDE"
            local liveRaw = db.dockLabelMode
            local persistRaw = db.dockLabelModePersist
            if normalizeDockLabelModeValue(liveRaw or "") == "OFF" or normalizeDockLabelModeValue(persistRaw or "") == "OFF" then
                db.dockHideDungeonName = true
            end
            if db.dockHideDungeonName then
                db.dockLabelMode = "OFF"
                db.dockLabelModePersist = "OFF"
                return "OFF"
            end
            local live = db.dockLabelMode
            local persisted = db.dockLabelModePersist
            local resolvedLive = (live ~= nil) and normalizeDockLabelModeValue(live) or nil
            local resolvedPersist = (persisted ~= nil) and normalizeDockLabelModeValue(persisted) or nil
            local resolved
            if resolvedLive == "OFF" or resolvedPersist == "OFF" then
                resolved = "OFF"
            elseif resolvedLive ~= nil then
                resolved = resolvedLive
            elseif resolvedPersist ~= nil then
                resolved = resolvedPersist
            else
                resolved = normalizeDockLabelModeValue(fallback)
            end
            db.dockLabelMode = resolved
            db.dockLabelModePersist = resolved
            return resolved
        end

        local function getDockStoredLabelSideForSettings(defaults)
            if ensureDatabase then
                ensureDatabase()
            end
            local db = PortalAuthorityDB or {}
            local fallback = (defaults and defaults.dockLabelSide) or "BOTTOM"
            local source = db.dockLabelSide
            if source == nil then
                source = db.dockLabelSidePersist
            end
            return normalizeDockLabelSideValue(source or fallback)
        end

        local function getDockOutsideLabelSideForSettings(defaults)
            local db = PortalAuthorityDB or {}
            local mode = getDockLabelModeForSettings(defaults)
            local fallback = (defaults and defaults.dockLabelSideOutsidePersist) or (defaults and defaults.dockLabelSide) or "BOTTOM"
            local resolved = getDockStoredLabelSideForSettings(defaults)
            if mode == "OUTSIDE" and isOutsideDockLabelSideValue(resolved) then
                db.dockLabelSideOutsidePersist = resolved
            end
            if mode == "OUTSIDE" and isOutsideDockLabelSideValue(resolved) then
                return resolved
            end
            return normalizeDockOutsideLabelSideValue(db.dockLabelSideOutsidePersist, fallback)
        end

        local function getDockInsideLabelSideForSettings(defaults)
            local resolved = getDockStoredLabelSideForSettings(defaults)
            if resolved == "TOP" or resolved == "BOTTOM" then
                return resolved
            end
            return "CENTER"
        end

        local function setDockLabelSideForSettings(side, modeOverride)
            local db = PortalAuthorityDB or {}
            local resolvedSide = normalizeDockLabelSideValue(side)
            local resolvedMode = normalizeDockLabelModeValue(modeOverride or db.dockLabelMode or db.dockLabelModePersist or "OUTSIDE")
            db.dockLabelSide = resolvedSide
            db.dockLabelSidePersist = resolvedSide
            if resolvedMode == "OUTSIDE" and isOutsideDockLabelSideValue(resolvedSide) then
                db.dockLabelSideOutsidePersist = resolvedSide
            end
            return resolvedSide
        end

        local setLabelStyleRowsVisible

        local function forceDockLabelUiState()
            local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
            local resolvedMode = getDockLabelModeForSettings(defaults)
            local resolvedOutsideSide = getDockOutsideLabelSideForSettings(defaults)
            local resolvedInsideSide = getDockInsideLabelSideForSettings(defaults)
            if PortalAuthorityDB and resolvedMode == "OFF" then
                PortalAuthorityDB.dockHideDungeonName = true
                PortalAuthorityDB.dockLabelMode = "OFF"
                PortalAuthorityDB.dockLabelModePersist = "OFF"
            end
            if widgets.labelMode and widgets.labelMode.SetVisualSelectedValue then
                widgets.labelMode:SetVisualSelectedValue(resolvedMode)
            end
            if widgets.labelSide and widgets.labelSide.SetVisualSelectedValue then
                widgets.labelSide:SetVisualSelectedValue(resolvedOutsideSide)
            end
            if widgets.labelSideInside and widgets.labelSideInside.SetVisualSelectedValue then
                widgets.labelSideInside:SetVisualSelectedValue(resolvedInsideSide)
            end
            local showSide = (resolvedMode ~= "OFF")
            if widgets.labelSide and widgets.labelSide.control and widgets.labelSide.label then
                if resolvedMode == "OUTSIDE" then
                    widgets.labelSide.control:Show()
                    widgets.labelSide.label:Show()
                else
                    widgets.labelSide.control:Hide()
                    widgets.labelSide.label:Hide()
                end
            end
            if widgets.labelSideInside and widgets.labelSideInside.control and widgets.labelSideInside.label then
                if resolvedMode == "INSIDE" then
                    widgets.labelSideInside.control:Show()
                    widgets.labelSideInside.label:Show()
                else
                    widgets.labelSideInside.control:Hide()
                    widgets.labelSideInside.label:Hide()
                end
            end
            setLabelStyleRowsVisible(showSide)
        end

        setLabelStyleRowsVisible = function(show)
            local visible = show and true or false

            local function setSpecVisible(spec, specVisible)
                if not spec then
                    return
                end
                if spec.label and spec.label.SetShown then
                    spec.label:SetShown(specVisible)
                end
                if spec.control and spec.control.SetShown then
                    spec.control:SetShown(specVisible)
                end
            end

            setSpecVisible(widgets.font, visible)
            if widgets.fontSize and widgets.fontSize.control and widgets.fontSize.control.SetShown then
                widgets.fontSize.control:SetShown(visible)
            end
            if widgets.color and widgets.color.swatch and widgets.color.swatch.SetShown then
                widgets.color.swatch:SetShown(visible)
            end
            if widgets.color and widgets.color.inlineLabel and widgets.color.inlineLabel.SetShown then
                widgets.color.inlineLabel:SetShown(false)
            end
            setSpecVisible(widgets.outlineToggle, visible)

            if not visible and widgets.font and widgets.font.picker and widgets.font.picker.Close then
                widgets.font.picker:Close()
            end
        end

        local function forceStyleToggleUiState()
            local db = PortalAuthorityDB or {}
            local outlineValue = normalizeOutlineToggleValue(db.dockTextOutline)
            local hoverValue = normalizeHoverGlowToggleValue(db.dockHoverGlow)

            -- Canonicalize persisted values so visual state and runtime state cannot drift.
            db.dockTextOutline = outlineValue
            db.dockHoverGlow = hoverValue and true or false

            if widgets.outlineToggle and widgets.outlineToggle.SetVisualSelectedValue then
                widgets.outlineToggle:SetVisualSelectedValue(outlineValue)
            end
            if widgets.hoverGlowSegment and widgets.hoverGlowSegment.SetVisualSelectedValue then
                widgets.hoverGlowSegment:SetVisualSelectedValue(hoverValue and true or false)
            end
        end

        local function forceDockFontUiState()
            local db = PortalAuthorityDB or {}
            local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
            local selectedFontPath = db.dockFontPath
            if selectedFontPath == nil then
                selectedFontPath = defaults.dockFontPath or ""
            end
            if widgets.font and widgets.font.SetVisualSelectedValue then
                widgets.font:SetVisualSelectedValue(selectedFontPath)
            elseif widgets.font and widgets.font.Refresh then
                widgets.font:Refresh()
            end
        end

        local function forceDockFontSizeUiState()
            local db = PortalAuthorityDB or {}
            local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
            local rawValue = tonumber(db.dockFontSizeUI or db.dockFontSize or defaults.dockFontSize or 12)
            local n = math.floor((rawValue or 12) + 0.5)
            if n < 8 then n = 8 end
            if n > 48 then n = 48 end
            db.dockFontSize = n
            db.dockFontSizeUI = n

            if widgets.fontSize and widgets.fontSize.SetVisualValue then
                widgets.fontSize:SetVisualValue(n)
            elseif widgets.fontSize and widgets.fontSize.Refresh then
                widgets.fontSize:Refresh()
            end
        end

        local function syncLabelModeSegmentSelection(forceVisualOnly)
            forceDockLabelUiState()
        end

        local function syncLabelSideSegmentSelection(forceVisualOnly)
            forceDockLabelUiState()
        end

        local updateLabelModeApplicability

        local function updateLayoutModeApplicability()
            local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
            local mode = getDockSimpleLayoutModeForSettings(defaults)
            local isGrid = (mode == "GRID")

            if widgets.iconsPerRow and widgets.iconsPerRow.SetEnabled then
                widgets.iconsPerRow:SetEnabled(isGrid)
            end

            if widgets.iconsPerRow and widgets.iconsPerRow.control and widgets.iconsPerRow.label then
                if isGrid then
                    widgets.iconsPerRow.control:Show()
                    widgets.iconsPerRow.label:Show()
                else
                    widgets.iconsPerRow.control:Hide()
                    widgets.iconsPerRow.label:Hide()
                end
            end

            if widgets.iconsPerRowGridOnlyHint then
                widgets.iconsPerRowGridOnlyHint:SetShown(false)
            end
        end

        updateLabelModeApplicability = function()
            if not widgets.labelMode then
                return
            end

            local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
            local mode = getDockLabelModeForSettings(defaults)
            local showSide = (mode ~= "OFF")

            if widgets.labelSide and widgets.labelSide.control and widgets.labelSide.label then
                if mode == "OUTSIDE" then
                    widgets.labelSide.control:Show()
                    widgets.labelSide.label:Show()
                else
                    widgets.labelSide.control:Hide()
                    widgets.labelSide.label:Hide()
                end
            end
            if widgets.labelSideInside and widgets.labelSideInside.control and widgets.labelSideInside.label then
                if mode == "INSIDE" then
                    widgets.labelSideInside.control:Show()
                    widgets.labelSideInside.label:Show()
                else
                    widgets.labelSideInside.control:Hide()
                    widgets.labelSideInside.label:Hide()
                end
            end
            setLabelStyleRowsVisible(showSide)
        end

        local function getSeason01PresetSpellIDs()
            if PortalAuthority and PortalAuthority.GetMPlusDockPresetSpellIDs then
                local spellIDs = PortalAuthority:GetMPlusDockPresetSpellIDs("SEASON_01")
                if type(spellIDs) == "table" and #spellIDs > 0 then
                    return spellIDs
                end
            end

            return {
                1254400, -- Windrunner Spire
                159898,  -- Skyreach
                1254551, -- Seat of the Triumvirate
                1254555, -- Pit of Saron
                1254563, -- Nexus-Point Xenas
                1254559, -- Maisara Caverns
                1254572, -- Magister's Terrace
                393273,  -- Algeth'ar Academy
            }
        end

        local season01PresetSpellIDs = getSeason01PresetSpellIDs()

        local function getCustomSeason01MatchState()
            local enabledSpellIDs = {}
            for i = 1, 10 do
                local slot = getDockSlot(i)
                if PA_SafeBool(slot.enabled) then
                    table.insert(enabledSpellIDs, math.max(0, math.floor(tonumber(slot.spellID) or 0)))
                end
            end

            if #enabledSpellIDs ~= #season01PresetSpellIDs then
                return false, false
            end

            local counts = {}
            for _, spellID in ipairs(season01PresetSpellIDs) do
                counts[spellID] = (counts[spellID] or 0) + 1
            end
            for _, spellID in ipairs(enabledSpellIDs) do
                if spellID <= 0 then
                    return false, false
                end
                local remaining = counts[spellID] or 0
                if remaining <= 0 then
                    return false, false
                end
                counts[spellID] = remaining - 1
            end

            local orderMatches = true
            for idx, spellID in ipairs(season01PresetSpellIDs) do
                if enabledSpellIDs[idx] ~= spellID then
                    orderMatches = false
                    break
                end
            end

            return true, orderMatches
        end

        local function applySeason01Loadout()
            -- Midnight Season 01 loadout slots 1-8; slots 9-10 remain editable but disabled.
            local presetSpellIDs = season01PresetSpellIDs

            for i = 1, 10 do
                local slot = getDockSlot(i)
                local presetSpellID = presetSpellIDs[i]
                if presetSpellID and presetSpellID > 0 then
                    slot.spellID = presetSpellID
                    slot.selection = tostring(presetSpellID)
                    setDockSlotDefaultName(slot, presetSpellID)
                    slot.enabled = true
                else
                    setDockSlotDefaultName(slot, 0)
                    slot.enabled = false
                end
            end

            onUIChanged(false)
        end

        local function normalizeLoadoutPresetValue(value)
            local preset = string.upper(trim(tostring(value or "CUSTOM")))
            if preset == "SEASON_02" then
                preset = "SEASON_01"
            end
            if preset ~= "SEASON_01" and preset ~= "CUSTOM" then
                preset = "CUSTOM"
            end
            return preset
        end

        PortalAuthorityDB.dockLoadoutPreset = normalizeLoadoutPresetValue(PortalAuthorityDB.dockLoadoutPreset)

        local function buildCustomLoadoutSnapshot()
            local snapshot = {}
            for i = 1, 10 do
                local slot = getDockSlot(i)
                local spellID = math.max(0, math.floor(tonumber(slot.spellID) or 0))
                local selection = tostring(slot.selection or "")
                if selection == "" then
                    selection = (spellID > 0) and tostring(spellID) or "CUSTOM"
                end
                snapshot[i] = {
                    enabled = PA_SafeBool(slot.enabled),
                    selection = selection,
                    spellID = spellID,
                    name = tostring(slot.name or ""),
                    renamed = slot.renamed == true,
                }
            end
            return snapshot
        end

        local function saveCustomLoadoutSnapshot()
            PortalAuthorityDB.dockCustomLoadoutSlots = buildCustomLoadoutSnapshot()
        end

        local function restoreCustomLoadoutSnapshot()
            local snapshot = PortalAuthorityDB and PortalAuthorityDB.dockCustomLoadoutSlots
            if type(snapshot) ~= "table" then
                return false
            end

            local changed = false
            for i = 1, 10 do
                local source = snapshot[i]
                if type(source) == "table" then
                    local slot = getDockSlot(i)
                    local enabled = PA_SafeBool(source.enabled)
                    local spellID = math.max(0, math.floor(tonumber(source.spellID) or 0))
                    local selection = tostring(source.selection or "")
                    if selection == "" then
                        selection = (spellID > 0) and tostring(spellID) or "CUSTOM"
                    end
                    local name = tostring(source.name or "")
                    local renamed = source.renamed == true

                    if slot.enabled ~= enabled
                        or slot.spellID ~= spellID
                        or tostring(slot.selection or "") ~= selection
                        or tostring(slot.name or "") ~= name
                        or (slot.renamed == true) ~= renamed then
                        changed = true
                    end

                    slot.enabled = enabled
                    slot.spellID = spellID
                    slot.selection = selection
                    slot.name = name
                    slot.renamed = renamed
                    normalizeDockSlotRenameState(slot)
                end
            end
            return changed
        end

        local lastLoadoutPresetSelection = normalizeLoadoutPresetValue(PortalAuthorityDB.dockLoadoutPreset)
        PortalAuthorityDB.dockLoadoutPreset = lastLoadoutPresetSelection
        if lastLoadoutPresetSelection == "CUSTOM" and type(PortalAuthorityDB.dockCustomLoadoutSlots) ~= "table" then
            saveCustomLoadoutSnapshot()
        end

        local suppressLoadoutPresetAutoCustom = false
        local function refreshLoadoutPresetControl()
            lastLoadoutPresetSelection = normalizeLoadoutPresetValue(PortalAuthorityDB.dockLoadoutPreset)
            if widgets.loadoutPresetSegment and widgets.loadoutPresetSegment.Refresh then
                widgets.loadoutPresetSegment:Refresh()
            end
        end
        local function setLoadoutPresetCustom()
            if suppressLoadoutPresetAutoCustom then
                return
            end
            saveCustomLoadoutSnapshot()
            lastLoadoutPresetSelection = "CUSTOM"
            if PortalAuthorityDB.dockLoadoutPreset ~= "CUSTOM" then
                PortalAuthorityDB.dockLoadoutPreset = "CUSTOM"
                refreshLoadoutPresetControl()
            end
        end

        local function createSliderControl(labelText, key, min, max, step, isFloat, onApply)
            local label = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlight", 12)
            label:SetText(labelText)
            local slider = CreateFrame("Slider", nil, scrollChild, "OptionsSliderTemplate")
            slider:SetMinMaxValues(min, max)
            slider:SetValueStep(step)
            if slider.SetObeyStepOnDrag then
                slider:SetObeyStepOnDrag(true)
            end
            if slider.Low then slider.Low:SetText("") end
            if slider.High then slider.High:SetText("") end
            if slider.Text then slider.Text:SetText("") end
            local valueText = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 11)

            local suppress = false
            local function normalize(v)
                local n = tonumber(v) or min
                if n < min then n = min end
                if n > max then n = max end
                if isFloat then
                    return tonumber(string.format("%.2f", n))
                end
                return math.floor(n + 0.5)
            end
            local function fmt(v)
                if isFloat then
                    return string.format("%.2f", v)
                end
                return tostring(math.floor(v + 0.5))
            end

            slider:SetScript("OnValueChanged", function(selfSlider, value)
                if suppress or isControlHydrating(selfSlider) then return end
                local n = normalize(value)
                if not isFloat and selfSlider:GetValue() ~= n then
                    suppress = true
                    selfSlider:SetValue(n)
                    suppress = false
                end
                PortalAuthorityDB[key] = n
                valueText:SetText(fmt(n))
                if onApply then onApply() end
            end)

            local spec = { kind = "slider", label = label, control = slider, valueText = valueText }
            spec.Refresh = function()
                local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
                local n = normalize(PortalAuthorityDB[key] or defaults[key] or min)
                PortalAuthorityDB[key] = n
                suppress = true
                slider:SetValue(n)
                suppress = false
                valueText:SetText(fmt(n))
            end
            spec.Refresh()
            return settingsSearchRuntime.registerSpec(spec)
        end

        local CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A = 0.10, 0.11, 0.14, 0.95
        local CONTROL_HOVER_BG_R, CONTROL_HOVER_BG_G, CONTROL_HOVER_BG_B, CONTROL_HOVER_BG_A = 0.16, 0.17, 0.20, 0.95
        local CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A = 0.24, 0.27, 0.32, 1.0
        local CONTROL_HOVER_BORDER_R, CONTROL_HOVER_BORDER_G, CONTROL_HOVER_BORDER_B, CONTROL_HOVER_BORDER_A = 0.30, 0.33, 0.38, 1.0

        local function CreatePremiumSlider(parent, labelText, min, max, step, settingKey, width, onApply, isFloat, tooltipText)
            local label = createStyledFontString(parent, "ARTWORK", "GameFontHighlight", 12)
            label:SetText(labelText)

            local sliderMin = tonumber(min) or 0
            local sliderMax = tonumber(max) or 100
            if sliderMax < sliderMin then
                sliderMin, sliderMax = sliderMax, sliderMin
            end

            local container = CreateFrame("Frame", nil, parent)
            container:SetHeight(22)
            container:SetWidth(math.max(120, tonumber(width) or 220))

            local valueButton = CreateFrame("Button", nil, container)
            valueButton:SetSize(44, 16)
            valueButton:SetPoint("RIGHT", container, "RIGHT", 0, 0)

            local valueText = createStyledFontString(valueButton, "ARTWORK", "GameFontHighlightSmall", 11)
            valueText:SetPoint("RIGHT", valueButton, "RIGHT", 0, 0)
            valueText:SetJustifyH("RIGHT")
            valueText:SetTextColor(0.76, 0.76, 0.78, 1)

            local valueEdit = CreateFrame("EditBox", nil, valueButton)
            valueEdit:SetAllPoints(valueButton)
            valueEdit:SetAutoFocus(false)
            valueEdit:SetJustifyH("RIGHT")
            valueEdit:SetTextInsets(0, 0, 0, 0)
            valueEdit:SetTextColor(1, 1, 1, 1)
            valueEdit:SetHighlightColor(1, 1, 1, 0.25)
            applyGlobalFontToObject(valueEdit, 11)
            valueEdit:Hide()

            local slider = CreateFrame("Slider", nil, container)
            slider:SetPoint("TOPLEFT", container, "TOPLEFT", 0, 0)
            slider:SetPoint("BOTTOMRIGHT", valueButton, "BOTTOMLEFT", -8, 0)
            slider:SetMinMaxValues(sliderMin, sliderMax)
            slider:SetValueStep(step)
            if slider.SetObeyStepOnDrag then
                slider:SetObeyStepOnDrag(true)
            end
            slider:SetThumbTexture("Interface\\Buttons\\WHITE8x8")
            local inputThumb = slider:GetThumbTexture()
            if inputThumb then
                inputThumb:SetSize(16, 16)
                -- Keep native thumb almost invisible while preserving drag mechanics.
                inputThumb:SetVertexColor(1, 1, 1, 0.01)
            end
            slider:EnableMouse(true)
            slider:SetOrientation("HORIZONTAL")

            local trackBg = slider:CreateTexture(nil, "BACKGROUND")
            trackBg:SetTexture("Interface\\Buttons\\WHITE8x8")
            trackBg:SetPoint("LEFT", slider, "LEFT", 0, 0)
            trackBg:SetPoint("RIGHT", slider, "RIGHT", 0, 0)
            trackBg:SetHeight(2)
            trackBg:SetVertexColor(0.18, 0.18, 0.19, 0.95)

            local trackActive = slider:CreateTexture(nil, "ARTWORK")
            trackActive:SetTexture("Interface\\Buttons\\WHITE8x8")
            trackActive:SetPoint("LEFT", slider, "LEFT", 0, 0)
            trackActive:SetHeight(2)
            trackActive:SetWidth(0)
            trackActive:SetVertexColor(1.0, 0.82, 0.0, 0.75)

            local thumb = slider:CreateTexture(nil, "OVERLAY")
            thumb:SetTexture("Interface\\Buttons\\WHITE8x8")
            thumb:SetSize(8, 8)
            thumb:SetVertexColor(1.0, 0.84, 0.08, 0.98)
            if thumb.SetMask then
                thumb:SetMask("Interface\\CharacterFrame\\TempPortraitAlphaMask")
            end

            local thumbGlow = slider:CreateTexture(nil, "ARTWORK")
            thumbGlow:SetTexture("Interface\\Buttons\\WHITE8x8")
            thumbGlow:SetSize(14, 14)
            thumbGlow:SetVertexColor(1.0, 0.82, 0.0, 0)
            if thumbGlow.SetMask then
                thumbGlow:SetMask("Interface\\CharacterFrame\\TempPortraitAlphaMask")
            end
            thumbGlow:SetBlendMode("ADD")

            local suppress = false
            local currentValue = nil
            local dragging = false
            local hovering = false

            local function normalize(v)
                local n = tonumber(v) or sliderMin
                if n < sliderMin then n = sliderMin end
                if n > sliderMax then n = sliderMax end
                if step and step > 0 then
                    n = sliderMin + (math.floor(((n - sliderMin) / step) + 0.5) * step)
                end
                if isFloat then
                    return tonumber(string.format("%.2f", n))
                end
                return math.floor(n + 0.5)
            end

            local function fmt(v)
                if isFloat then
                    return string.format("%.2f", tonumber(v) or 0)
                end
                return tostring(math.floor((tonumber(v) or 0) + 0.5))
            end

            local function resolveApply()
                if onApply then
                    onApply()
                    return
                end
                if settingKey == "dockIconsPerLine" or settingKey == "dockIconSpacing" then
                    ApplyDockLayoutSettings(false)
                else
                    ApplyDockAppearanceSettings()
                end
            end

            local function applyVisualState()
                local minV, maxV = slider:GetMinMaxValues()
                local value = normalize(slider:GetValue())
                local span = math.max(0.0001, (maxV - minV))
                local pct = (value - minV) / span
                pct = math.max(0, math.min(1, pct))
                local widthNow = slider:GetWidth() or 0
                local x = pct * widthNow

                thumb:ClearAllPoints()
                thumb:SetPoint("CENTER", slider, "LEFT", x, 0)
                thumbGlow:ClearAllPoints()
                thumbGlow:SetPoint("CENTER", thumb, "CENTER", 0, 0)

                trackActive:ClearAllPoints()
                trackActive:SetPoint("LEFT", slider, "LEFT", 0, 0)
                trackActive:SetPoint("RIGHT", thumb, "CENTER", 0, 0)

                local glowAlpha = 0
                if dragging then
                    glowAlpha = 0.34
                    trackActive:SetVertexColor(1.0, 0.86, 0.16, 0.9)
                    thumb:SetVertexColor(1.0, 0.9, 0.22, 1)
                elseif hovering then
                    glowAlpha = 0.22
                    trackActive:SetVertexColor(1.0, 0.85, 0.12, 0.85)
                    thumb:SetVertexColor(1.0, 0.88, 0.18, 1)
                else
                    trackActive:SetVertexColor(1.0, 0.82, 0.0, 0.75)
                    thumb:SetVertexColor(1.0, 0.84, 0.08, 0.98)
                end
                thumbGlow:SetVertexColor(1.0, 0.82, 0.0, glowAlpha)
            end

            local function setValueFromCursor()
                local minV, maxV = slider:GetMinMaxValues()
                local left = slider:GetLeft()
                local right = slider:GetRight()
                if not left or not right or right <= left then
                    return
                end
                local cx = GetCursorPosition()
                local scale = slider:GetEffectiveScale() or 1
                if not cx or scale <= 0 then
                    return
                end
                cx = cx / scale
                local pct = (cx - left) / (right - left)
                if pct < 0 then pct = 0 end
                if pct > 1 then pct = 1 end
                local raw = minV + ((maxV - minV) * pct)
                slider:SetValue(normalize(raw))
            end

            local function setValueFromInput(text)
                local normalizedText = (text or ""):gsub(",", ".")
                local parsed = tonumber(normalizedText)
                if parsed == nil then
                    return
                end
                local n = normalize(parsed)
                suppress = true
                slider:SetValue(n)
                suppress = false
                if currentValue ~= n then
                    currentValue = n
                    PortalAuthorityDB[settingKey] = n
                    if settingKey == "dockIconSize" then
                        PortalAuthorityDB.dockIconSizeUI = n
                        PortalAuthorityDB.dockIconWidth = n
                        PortalAuthorityDB.dockIconHeight = n
                    end
                    resolveApply()
                end
                valueText:SetText(fmt(n))
                applyVisualState()
            end

            local function beginEdit()
                valueText:Hide()
                valueEdit:SetText(fmt(slider:GetValue()))
                valueEdit:Show()
                valueEdit:SetFocus()
                valueEdit:HighlightText()
            end

            local function endEdit(commit)
                if commit then
                    setValueFromInput(valueEdit:GetText())
                end
                valueEdit:Hide()
                valueText:Show()
                valueEdit:ClearFocus()
                valueText:SetText(fmt(slider:GetValue()))
            end

            valueButton:SetScript("OnClick", function()
                beginEdit()
            end)
            valueEdit:SetScript("OnEnterPressed", function(edit)
                endEdit(true)
                edit:ClearFocus()
            end)
            valueEdit:SetScript("OnEscapePressed", function(edit)
                endEdit(false)
                edit:ClearFocus()
            end)
            valueEdit:SetScript("OnEditFocusLost", function()
                endEdit(true)
            end)

            slider:SetScript("OnValueChanged", function(selfSlider, value)
                local n = normalize(value)
                if selfSlider:GetValue() ~= n then
                    suppress = true
                    selfSlider:SetValue(n)
                    suppress = false
                end
                valueText:SetText(fmt(n))
                applyVisualState()
                if suppress or isControlHydrating(selfSlider) then
                    return
                end
                if currentValue == n then
                    return
                end
                currentValue = n
                PortalAuthorityDB[settingKey] = n
                if settingKey == "dockIconSize" then
                    PortalAuthorityDB.dockIconSizeUI = n
                    PortalAuthorityDB.dockIconWidth = n
                    PortalAuthorityDB.dockIconHeight = n
                end
                resolveApply()
            end)
            slider:SetScript("OnMouseDown", function()
                dragging = true
                setValueFromCursor()
                applyVisualState()
                slider:SetScript("OnUpdate", function()
                    if dragging then
                        setValueFromCursor()
                    end
                end)
            end)
            slider:SetScript("OnMouseUp", function()
                dragging = false
                slider:SetScript("OnUpdate", nil)
                applyVisualState()
            end)
            slider:SetScript("OnEnter", function(selfSlider)
                hovering = true
                applyVisualState()
                if tooltipText and tooltipText ~= "" then
                    GameTooltip:SetOwner(selfSlider, "ANCHOR_TOP")
                    GameTooltip:SetText(tooltipText, 1, 0.82, 0, 1)
                    GameTooltip:Show()
                end
            end)
            slider:SetScript("OnLeave", function()
                hovering = false
                dragging = false
                slider:SetScript("OnUpdate", nil)
                applyVisualState()
                GameTooltip:Hide()
            end)

            if tooltipText and tooltipText ~= "" then
                label:SetScript("OnEnter", function(selfLabel)
                    GameTooltip:SetOwner(selfLabel, "ANCHOR_TOPLEFT")
                    GameTooltip:SetText(tooltipText, 1, 0.82, 0, 1)
                    GameTooltip:Show()
                end)
                label:SetScript("OnLeave", function()
                    GameTooltip:Hide()
                end)
            end

            local spec = { kind = "premium_slider", label = label, control = container, slider = slider, valueText = valueText, valueEdit = valueEdit }
            spec.SetWidth = function(_, newWidth)
                local fullW = math.max(140, math.floor(tonumber(newWidth) or width or 220))
                container:SetWidth(fullW)
                applyVisualState()
            end
            spec.SetRange = function(_, newMin, newMax)
                local a = tonumber(newMin)
                local b = tonumber(newMax)
                if a == nil or b == nil then
                    return
                end
                if b < a then
                    a, b = b, a
                end
                sliderMin = a
                sliderMax = b
                slider:SetMinMaxValues(sliderMin, sliderMax)
                local n = normalize(PortalAuthorityDB[settingKey] or currentValue or slider:GetValue())
                suppress = true
                slider:SetValue(n)
                suppress = false
                currentValue = n
                PortalAuthorityDB[settingKey] = n
                if settingKey == "dockIconSize" then
                    PortalAuthorityDB.dockIconSizeUI = n
                    PortalAuthorityDB.dockIconWidth = n
                    PortalAuthorityDB.dockIconHeight = n
                end
                valueText:SetText(fmt(n))
                applyVisualState()
            end
            spec.Refresh = function()
                local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
                local storedValue = nil
                if PortalAuthorityDB then
                    if settingKey == "dockIconSize" then
                        storedValue = tonumber(PortalAuthorityDB.dockIconSizeUI or PortalAuthorityDB[settingKey])
                    else
                        storedValue = tonumber(PortalAuthorityDB[settingKey])
                    end
                end
                local n = normalize(storedValue or defaults[settingKey] or sliderMin)
                suppress = true
                slider:SetValue(n)
                suppress = false
                currentValue = n
                valueEdit:SetText(fmt(n))
                valueEdit:Hide()
                valueText:Show()
                valueText:SetText(fmt(n))
                applyVisualState()
            end
            container:SetScript("OnSizeChanged", function()
                applyVisualState()
            end)
            spec.Refresh()
            return settingsSearchRuntime.registerSpec(spec)
        end

        local function CreateStepperControl(parent, labelText, min, max, step, settingKey, width, onApply, tooltipText, opts)
            opts = type(opts) == "table" and opts or {}
            local isMicro = opts.micro and true or false
            local microShiftMultiplier = math.max(1, math.floor(tonumber(opts.shiftMultiplier) or 5))
            local customGetValue = type(opts.getValue) == "function" and opts.getValue or nil
            local customSetValue = type(opts.setValue) == "function" and opts.setValue or nil
            local label = createStyledFontString(parent, "ARTWORK", "GameFontHighlight", 12)
            label:SetText(labelText)

            local container = CreateFrame("Frame", nil, parent)
            container:SetHeight(22)
            container:SetWidth(math.max(isMicro and 52 or 120, tonumber(width) or 160))

            local function createStepperButton(symbol)
                local button = CreateFrame("Button", nil, container, isMicro and nil or "BackdropTemplate")
                if not isMicro then
                    button:SetBackdrop({
                        bgFile = "Interface\\Buttons\\WHITE8x8",
                    })
                    button:SetBackdropColor(CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A)
                    button:SetSize(18, 18)
                else
                    button:SetSize(10, 16)
                end

                local text = createStyledFontString(button, "ARTWORK", "GameFontHighlight", 12)
                text:SetPoint("CENTER", button, "CENTER", 0, 0)
                text:SetText(symbol)
                text:SetTextColor(isMicro and 0.74 or 0.9, isMicro and 0.74 or 0.9, isMicro and 0.76 or 0.92, 1)
                button._text = text
                button._hovered = false
                button._pressed = false

                local function applyVisual(selfBtn)
                    if isMicro then
                        if selfBtn._pressed then
                            selfBtn._text:SetTextColor(0.95, 0.95, 0.98, 1)
                            selfBtn:SetAlpha(0.82)
                        elseif selfBtn._hovered then
                            selfBtn._text:SetTextColor(0.95, 0.95, 0.98, 1)
                            selfBtn:SetAlpha(1)
                        else
                            selfBtn._text:SetTextColor(0.74, 0.74, 0.76, 1)
                            selfBtn:SetAlpha(1)
                        end
                    else
                        if selfBtn._hovered then
                            selfBtn:SetBackdropColor(CONTROL_HOVER_BG_R, CONTROL_HOVER_BG_G, CONTROL_HOVER_BG_B, CONTROL_HOVER_BG_A)
                            selfBtn._text:SetTextColor(1, 0.92, 0.38, 1)
                        else
                            selfBtn:SetBackdropColor(CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A)
                            selfBtn._text:SetTextColor(0.9, 0.9, 0.92, 1)
                        end
                    end
                end

                button:SetScript("OnEnter", function(selfBtn)
                    selfBtn._hovered = true
                    applyVisual(selfBtn)
                end)
                button:SetScript("OnLeave", function(selfBtn)
                    selfBtn._hovered = false
                    selfBtn._pressed = false
                    applyVisual(selfBtn)
                end)
                button:SetScript("OnMouseDown", function(selfBtn)
                    selfBtn._pressed = true
                    applyVisual(selfBtn)
                end)
                button:SetScript("OnMouseUp", function(selfBtn)
                    selfBtn._pressed = false
                    applyVisual(selfBtn)
                end)
                return button
            end

            local minusButton = createStepperButton(isMicro and "<" or "-")
            local plusButton = createStepperButton(isMicro and ">" or "+")

            if not isMicro then
                minusButton:SetPoint("LEFT", container, "LEFT", 0, 0)
                plusButton:SetPoint("RIGHT", container, "RIGHT", 0, 0)
            end

            local valueButton = CreateFrame("Button", nil, container)
            local microLayout = nil
            if not isMicro then
                valueButton:SetPoint("LEFT", minusButton, "RIGHT", 6, 0)
                valueButton:SetPoint("RIGHT", plusButton, "LEFT", -6, 0)
                valueButton:SetHeight(18)
            else
                valueButton:SetHeight(16)
                microLayout = function()
                    local gap = 3
                    local buttonW = minusButton:GetWidth() or 10
                    local containerW = container:GetWidth() or 52
                    local valueW = math.max(20, math.min(28, containerW - (buttonW * 2) - (gap * 2)))
                    local clusterW = (buttonW * 2) + valueW + (gap * 2)
                    local startX = math.floor((containerW - clusterW) / 2)
                    if startX < 0 then
                        startX = 0
                    end

                    minusButton:ClearAllPoints()
                    minusButton:SetPoint("LEFT", container, "LEFT", startX, 0)

                    valueButton:ClearAllPoints()
                    valueButton:SetPoint("LEFT", minusButton, "RIGHT", gap, 0)
                    valueButton:SetWidth(valueW)

                    plusButton:ClearAllPoints()
                    plusButton:SetPoint("LEFT", valueButton, "RIGHT", gap, 0)
                end
                microLayout()
                container:HookScript("OnSizeChanged", function()
                    if microLayout then
                        microLayout()
                    end
                end)
            end

            local valueText = createStyledFontString(valueButton, "ARTWORK", "GameFontHighlightSmall", 11)
            valueText:SetPoint("CENTER", valueButton, "CENTER", 0, 0)
            if isMicro then
                valueText:SetTextColor(0.86, 0.86, 0.9, 1)
            else
                valueText:SetTextColor(0.76, 0.76, 0.78, 1)
            end

            local valueEdit = CreateFrame("EditBox", nil, valueButton)
            valueEdit:SetAllPoints(valueButton)
            valueEdit:SetAutoFocus(false)
            valueEdit:SetJustifyH("CENTER")
            valueEdit:SetTextInsets(0, 0, 0, 0)
            valueEdit:SetTextColor(1, 1, 1, 1)
            valueEdit:SetHighlightColor(1, 1, 1, 0.25)
            valueEdit:SetNumeric(true)
            applyGlobalFontToObject(valueEdit, 11)
            valueEdit:Hide()

            local stepValue = tonumber(step) or 1
            if stepValue <= 0 then
                stepValue = 1
            end

            local currentValue = nil
            local editingValue = false
            local controlEnabled = true

            local function normalize(v)
                local n = tonumber(v) or min
                if n < min then n = min end
                if n > max then n = max end
                n = min + (math.floor(((n - min) / stepValue) + 0.5) * stepValue)
                if n < min then n = min end
                if n > max then n = max end
                return math.floor(n + 0.5)
            end

            local function fmt(v)
                return tostring(math.floor((tonumber(v) or 0) + 0.5))
            end

            local function setValue(newValue, triggerApply)
                local n = normalize(newValue)
                if currentValue == n then
                    valueText:SetText(fmt(n))
                    return
                end
                currentValue = n
                if customSetValue then
                    customSetValue(n)
                else
                    PortalAuthorityDB[settingKey] = n
                    if settingKey == "dockFontSize" then
                        PortalAuthorityDB.dockFontSizeUI = n
                    end
                end
                valueText:SetText(fmt(n))
                if triggerApply and onApply then
                    onApply()
                end
            end

            local function beginEdit()
                editingValue = true
                valueText:Hide()
                valueEdit:SetText(fmt(currentValue or min))
                valueEdit:Show()
                valueEdit:SetFocus()
                valueEdit:HighlightText()
            end

            local function endEdit(commit)
                if not editingValue then
                    return
                end
                editingValue = false
                if commit then
                    local parsed = tonumber(valueEdit:GetText())
                    if parsed ~= nil then
                        setValue(parsed, true)
                    end
                end
                valueEdit:Hide()
                valueText:Show()
                valueEdit:ClearFocus()
                valueText:SetText(fmt(currentValue or min))
            end

            local function applyEnabledState()
                local enabledAlpha = controlEnabled and 1 or 0.5
                container:SetAlpha(enabledAlpha)
                label:SetAlpha(enabledAlpha)
                minusButton:SetEnabled(controlEnabled)
                plusButton:SetEnabled(controlEnabled)
                valueButton:SetEnabled(controlEnabled)
                if not controlEnabled and editingValue then
                    endEdit(false)
                end
            end

            minusButton:SetScript("OnClick", function()
                if not controlEnabled then
                    return
                end
                local mult = 1
                if isMicro and IsShiftKeyDown and IsShiftKeyDown() then
                    mult = microShiftMultiplier
                end
                setValue((currentValue or min) - (stepValue * mult), true)
            end)
            plusButton:SetScript("OnClick", function()
                if not controlEnabled then
                    return
                end
                local mult = 1
                if isMicro and IsShiftKeyDown and IsShiftKeyDown() then
                    mult = microShiftMultiplier
                end
                setValue((currentValue or min) + (stepValue * mult), true)
            end)
            valueButton:SetScript("OnClick", function()
                if not controlEnabled then
                    return
                end
                beginEdit()
            end)
            valueEdit:SetScript("OnEnterPressed", function(edit)
                endEdit(true)
                edit:ClearFocus()
            end)
            valueEdit:SetScript("OnEscapePressed", function(edit)
                endEdit(false)
                edit:ClearFocus()
            end)
            valueEdit:SetScript("OnEditFocusLost", function()
                endEdit(true)
            end)

            if tooltipText and tooltipText ~= "" then
                label:SetScript("OnEnter", function(selfLabel)
                    GameTooltip:SetOwner(selfLabel, "ANCHOR_TOPLEFT")
                    GameTooltip:SetText(tooltipText, 1, 0.82, 0, 1)
                    GameTooltip:Show()
                end)
                label:SetScript("OnLeave", function()
                    GameTooltip:Hide()
                end)
            end

            local spec = {
                kind = "stepper",
                label = label,
                control = container,
                valueButton = valueButton,
                valueText = valueText,
                valueEdit = valueEdit,
                minus = minusButton,
                plus = plusButton,
            }
            spec.SetWidth = function(_, newWidth)
                local minWidth = isMicro and 52 or 120
                container:SetWidth(math.max(minWidth, math.floor(tonumber(newWidth) or width or 160)))
            end
            spec.SetEnabled = function(_, enabled)
                controlEnabled = enabled and true or false
                applyEnabledState()
            end
            spec.IsEnabled = function()
                return controlEnabled
            end
            spec.SetVisualValue = function(_, value)
                local n = normalize(value)
                currentValue = n
                valueText:SetText(fmt(n))
                if valueEdit:IsShown() then
                    valueEdit:SetText(fmt(n))
                end
                applyEnabledState()
            end
            spec.Refresh = function()
                local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
                local storedValue = nil
                if customGetValue then
                    storedValue = customGetValue()
                else
                    storedValue = PortalAuthorityDB[settingKey]
                    if settingKey == "dockFontSize" then
                        storedValue = tonumber(PortalAuthorityDB.dockFontSizeUI or PortalAuthorityDB.dockFontSize)
                    end
                end
                local n = normalize(storedValue or defaults[settingKey] or min)
                currentValue = n
                if not customGetValue and not customSetValue then
                    PortalAuthorityDB[settingKey] = n
                    if settingKey == "dockFontSize" then
                        PortalAuthorityDB.dockFontSizeUI = n
                    end
                end
                valueText:SetText(fmt(n))
                if valueEdit:IsShown() then
                    valueEdit:SetText(fmt(n))
                end
                applyEnabledState()
            end
            spec.Refresh()
            return settingsSearchRuntime.registerSpec(spec)
        end
        createCanonicalDockFontSizeStepperControl = CreateStepperControl

        local function CreateFontPicker(parent, labelText, settingKey, width, onApply)
            local label = createStyledFontString(parent, "ARTWORK", "GameFontHighlight", 12)
            label:SetText(labelText)

            local function normalizeFontPathKey(path)
                local value = trim(tostring(path or ""))
                value = value:gsub("/", "\\")
                return string.lower(value)
            end

            local function fallbackFontLabelForPath(path)
                local value = trim(tostring(path or ""))
                value = value:gsub("/", "\\")
                if value == "" then
                    return "Global UI Font"
                end
                local name = value:match("([^\\]+)$")
                if name and name ~= "" then
                    return name
                end
                return value
            end

            local function resolveFontSelection(rawValue, options)
                local selectedValue = tostring(rawValue or "")
                local selectedKey = normalizeFontPathKey(selectedValue)
                local selectedLabel = nil
                local matchedValue = selectedValue

                for _, option in ipairs(options or {}) do
                    local optionValue = tostring(option.value or "")
                    if normalizeFontPathKey(optionValue) == selectedKey then
                        matchedValue = optionValue
                        selectedLabel = option.buttonLabel or option.label
                        break
                    end
                end

                if not selectedLabel then
                    selectedLabel = fallbackFontLabelForPath(selectedValue)
                end

                return tostring(matchedValue or ""), tostring(selectedLabel or "")
            end

            local button = CreateFrame("Button", nil, parent, "BackdropTemplate")
            button:SetBackdrop({
                bgFile = "Interface\\Buttons\\WHITE8x8",
                edgeFile = "Interface\\Buttons\\WHITE8x8",
                edgeSize = 1,
                insets = { left = 0, right = 0, top = 0, bottom = 0 },
            })
            button:SetBackdropColor(CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A)
            button:SetBackdropBorderColor(CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A)
            button:SetHeight(22)
            button:SetWidth(math.max(140, tonumber(width) or 220))

            local text = createStyledFontString(button, "ARTWORK", "GameFontHighlightSmall", 11)
            text:SetPoint("LEFT", button, "LEFT", 8, 0)
            text:SetPoint("RIGHT", button, "RIGHT", -18, 0)
            text:SetJustifyH("LEFT")
            text:SetTextColor(0.92, 0.92, 0.92, 1)

            local caret = createStyledFontString(button, "ARTWORK", "GameFontHighlightSmall", 10)
            caret:SetPoint("RIGHT", button, "RIGHT", -7, 0)
            caret:SetText("v")
            caret:SetTextColor(0.76, 0.76, 0.78, 1)

            local picker
            picker = CreatePicker(UIParent, {}, function(value, option, wasClear)
                local selectedValue = value
                if wasClear then
                    selectedValue = ""
                end
                selectedValue = tostring(selectedValue or "")
                PortalAuthorityDB[settingKey] = selectedValue
                local activeOptions = picker and picker._allOptions or {}
                local visualValue, visualLabel = resolveFontSelection(selectedValue, activeOptions)
                text:SetText(visualLabel ~= "" and visualLabel or "Global UI Font")
                if picker then
                    picker:SetSelectedValue(visualValue)
                end
                if onApply then
                    onApply()
                end
            end, {
                title = "Select Font Face",
                searchHint = "Search fonts by name",
                width = 420,
                height = 380,
                showSecondary = false,
                useOptionFontPreview = true,
                previewFontSize = 13,
                rowHeight = 26,
            })

            local function buildFontOptions()
                return buildSharedFontPickerChoices()
            end

            local function applyButtonVisual(hovered)
                if hovered then
                    button:SetBackdropColor(CONTROL_HOVER_BG_R, CONTROL_HOVER_BG_G, CONTROL_HOVER_BG_B, CONTROL_HOVER_BG_A)
                    button:SetBackdropBorderColor(CONTROL_HOVER_BORDER_R, CONTROL_HOVER_BORDER_G, CONTROL_HOVER_BORDER_B, CONTROL_HOVER_BORDER_A)
                    caret:SetTextColor(0.88, 0.88, 0.90, 1)
                else
                    button:SetBackdropColor(CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A)
                    button:SetBackdropBorderColor(CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A)
                    caret:SetTextColor(0.76, 0.76, 0.78, 1)
                end
            end

            button:SetScript("OnEnter", function()
                applyButtonVisual(true)
            end)
            button:SetScript("OnLeave", function()
                applyButtonVisual(false)
            end)

            local spec = {
                kind = "font_picker",
                label = label,
                control = button,
                picker = picker,
                text = text,
            }

            spec.SetWidth = function(_, newWidth)
                button:SetWidth(math.max(140, math.floor(tonumber(newWidth) or width or 220)))
            end

            spec.SetVisualSelectedValue = function(_, rawValue)
                local options = picker and picker._allOptions or buildFontOptions()
                if picker and (not picker._allOptions or #picker._allOptions == 0) then
                    picker:SetOptions(options)
                    options = picker._allOptions or options
                end
                local selectedValue, selectedLabel = resolveFontSelection(rawValue, options)
                if picker then
                    picker:SetSelectedValue(selectedValue)
                end
                text:SetText(selectedLabel ~= "" and selectedLabel or "Global UI Font")
            end

            spec.Refresh = function()
                local options = buildFontOptions()
                picker:SetOptions(options)

                local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
                local rawValue = PortalAuthorityDB[settingKey]
                if rawValue == nil then
                    rawValue = defaults[settingKey] or ""
                end

                local selectedValue, selectedLabel = resolveFontSelection(rawValue, options)

                picker:SetSelectedValue(selectedValue)
                text:SetText(selectedLabel ~= "" and selectedLabel or "Global UI Font")
                button:SetEnabled(#options > 0)
                button:SetAlpha(#options > 0 and 1 or 0.6)
            end

            button:SetScript("OnClick", function()
                if picker and picker.IsShown and picker:IsShown() then
                    picker:Close()
                    return
                end
                spec.Refresh()
                picker:Open(button)
            end)

            spec.Refresh()
            return settingsSearchRuntime.registerSpec(spec, "font_picker")
        end

        local OFF_RED_BG_R, OFF_RED_BG_G, OFF_RED_BG_B, OFF_RED_BG_A = 0.23, 0.05, 0.075, 0.93
        local OFF_RED_BORDER_R, OFF_RED_BORDER_G, OFF_RED_BORDER_B, OFF_RED_BORDER_A = 0.21, 0.045, 0.068, 0.88
        local OFF_RED_BORDER_SELECTED_R, OFF_RED_BORDER_SELECTED_G, OFF_RED_BORDER_SELECTED_B, OFF_RED_BORDER_SELECTED_A = 0.22, 0.049, 0.074, 1.0
        local OFF_RED_TEXT_R, OFF_RED_TEXT_G, OFF_RED_TEXT_B, OFF_RED_TEXT_A = 0.90, 0.76, 0.76, 1.0
        local OFF_RED_SELECTED_ALPHA = 0.95
        local OFF_RED_TOPSHEEN_ALPHA = 0.14

        local function setSegmentBorderColor(btn, r, g, b, a)
            if not btn then
                return
            end
            if btn._paBorder then
                local rr = tonumber(r) or CONTROL_BASE_BORDER_R
                local gg = tonumber(g) or CONTROL_BASE_BORDER_G
                local bb = tonumber(b) or CONTROL_BASE_BORDER_B
                local aa = tonumber(a) or CONTROL_BASE_BORDER_A
                local colorKey = string.format("%.4f:%.4f:%.4f:%.4f", rr, gg, bb, aa)
                if btn._paBorder._paLastBorderColorKey ~= colorKey then
                    btn._paBorder:SetBackdropBorderColor(rr, gg, bb, aa)
                    btn._paBorder._paLastBorderColorKey = colorKey
                end
                btn._paBorder:SetAlpha(1)
                btn._paBorder:Show()
            end
        end
        local function createSegmentedChoice(labelText, key, choices, onApply, opts)
            return createPremiumSegmentedChoice(scrollChild, labelText, key, choices, onApply, opts)
        end

        local function styleSecondaryActionButton(btn, styleOpts)
            styleSecondaryActionButtonShared(btn, styleOpts)
        end

        local refreshLockDockVisualText
        local applyDockModuleEnabledState
        local applyDockGizmoVisibilityOverrideState
        local function isDockModuleEnabled()
            return PortalAuthorityDB == nil or PortalAuthorityDB.dockEnabled ~= false
        end

        widgets.lockDock = createSegmentedChoice("Lock Position", "dockLocked", {
            { label = "Unlocked", value = false, toggleState = "off" },
            { label = "Locked", value = true, toggleState = "on" },
        }, function()
            if PortalAuthority.UpdateDockMovableState then
                PortalAuthority:UpdateDockMovableState()
            end

            refreshDockVisibility()
            if refreshLockDockVisualText then
                refreshLockDockVisualText()
            end
            if relayout then relayout() end
        end, { semantic = "selection", selectionVariant = "lock_meta" })

        refreshLockDockVisualText = function()
            local isLocked = PA_SafeBool(PortalAuthorityDB and PortalAuthorityDB.dockLocked)
            if widgets.lockDock and type(widgets.lockDock.buttons) == "table" then
                for _, btn in ipairs(widgets.lockDock.buttons) do
                    if btn and btn.Text then
                        if btn._value == false then
                            btn.Text:SetText("Unlocked")
                        elseif btn._value == true then
                            btn.Text:SetText("Locked")
                        end
                    end
                end
            end
            if widgets.lockStateHelper then
                if not isDockModuleEnabled() then
                    widgets.lockStateHelper:SetText("Dock is disabled. Re-enable to edit settings.")
                elseif isLocked then
                    widgets.lockStateHelper:SetText("Dock is locked.")
                else
                    widgets.lockStateHelper:SetText("Dock is unlocked - drag to reposition.")
                end
            end
        end
        refreshLockDockVisualText()

        widgets.dockEnabled = createSegmentedChoice("Module", "dockEnabled", {
            { label = "Off", value = false, toggleState = "off" },
            { label = "On", value = true, toggleState = "on" },
        }, function()
            refreshDockVisibility()
            if refreshLockDockVisualText then
                refreshLockDockVisualText()
            end
            if relayout then
                relayout()
            end
            if applyDockModuleEnabledState then
                applyDockModuleEnabledState()
            elseif self and self._refresh then
                self._refresh()
            end
        end, { semantic = "toggle" })
        self._settingsWindowHeaderToggleSource = widgets.dockEnabled

        widgets.hideCity = createSegmentedChoice("Show in Major City", "dockHideInMajorCity", {
            { label = "Off", value = true, toggleState = "off" },
            { label = "On", value = false, toggleState = "on" },
        }, function()
            refreshDockVisibility()
        end, { semantic = "toggle" })

        widgets.hideDungeon = createSegmentedChoice("Show in Dungeon", "dockHideInDungeon", {
            { label = "Off", value = true, toggleState = "off" },
            { label = "On", value = false, toggleState = "on" },
        }, function()
            refreshDockVisibility()
        end, { semantic = "toggle" })

        widgets.hideCombat = createSegmentedChoice("Show in Combat", "dockHideInCombat", {
            { label = "Off", value = true, toggleState = "off" },
            { label = "On", value = false, toggleState = "on" },
        }, function()
            refreshDockVisibility()
        end, { semantic = "toggle" })

            widgets.hideNames = createSegmentedChoice("Show Portal Name", "dockHideDungeonName", {
                { label = "Off", value = true, toggleState = "off" },
                { label = "On", value = false, toggleState = "on" },
            }, function()
                if PortalAuthorityDB.dockHideDungeonName then
                    PortalAuthorityDB.dockLabelMode = "OFF"
                    PortalAuthorityDB.dockLabelModePersist = "OFF"
                else
                    local currentMode = normalizeDockLabelModeValue(PortalAuthorityDB.dockLabelMode)
                    if currentMode == "OFF" then
                        PortalAuthorityDB.dockLabelMode = "OUTSIDE"
                        PortalAuthorityDB.dockLabelModePersist = "OUTSIDE"
                    end
                end
                syncLabelModeSegmentSelection(false)
                syncLabelSideSegmentSelection(false)
                if updateLabelModeApplicability then
                    updateLabelModeApplicability()
                end
                rebuildDock()
            end, { semantic = "toggle" })

        widgets.gizmoMode = createSegmentedChoice("Gizmo Mode |T132311:14:14:0:0:64:64:4:60:4:60|t", "dockGizmoMode", {
            { label = "Off", value = false, toggleState = "off" },
            { label = "On", value = true, toggleState = "on" },
        }, function()
            refreshDockVisibility()
            if applyDockGizmoVisibilityOverrideState then
                applyDockGizmoVisibilityOverrideState()
            end
        end, { semantic = "toggle" })
        do
            local gizmoTooltip = "Only show the dock when the Mythic+ dungeon window is open."
            local gizmoLabel = widgets.gizmoMode and widgets.gizmoMode.label or nil
            if gizmoLabel then
                local hit = gizmoLabel._paGizmoTooltipHit
                if not hit then
                    hit = CreateFrame("Frame", nil, scrollChild)
                    hit:SetPoint("TOPLEFT", gizmoLabel, "TOPLEFT", -2, 2)
                    hit:SetPoint("BOTTOMRIGHT", gizmoLabel, "BOTTOMRIGHT", 2, -2)
                    hit:EnableMouse(true)
                    hit:SetScript("OnEnter", function(selfHit)
                        GameTooltip:SetOwner(selfHit, "ANCHOR_TOP")
                        GameTooltip:SetText(gizmoTooltip, 1, 1, 1, 1)
                        GameTooltip:Show()
                    end)
                    hit:SetScript("OnLeave", function()
                        GameTooltip:Hide()
                    end)
                    gizmoLabel._paGizmoTooltipHit = hit
                end
                hit:SetShown(gizmoLabel:IsShown())
            end
        end

        -- Gizmo Mode temporarily overrides these three controls visually and at runtime
        -- without rewriting the user's saved DB preferences underneath.
        local function setDockVisibilityControlGizmoLocked(spec, locked, moduleEnabled)
            if not spec then
                return
            end

            if spec.SetEnabled then
                spec:SetEnabled(moduleEnabled and true or false)
            end

            if spec.buttons then
                for _, btn in ipairs(spec.buttons) do
                    if btn then
                        btn._paHovered = false
                        btn._paPressed = false
                        if btn.SetPushedTextOffset then
                            btn:SetPushedTextOffset(0, 0)
                        end
                        if btn.EnableMouse then
                            btn:EnableMouse((moduleEnabled and true or false) and (not locked))
                        end
                    end
                end
            end

            if spec.label and spec.label.SetAlpha then
                spec.label:SetAlpha(locked and moduleEnabled and 0.54 or (moduleEnabled and 1 or 0.55))
            end
            if spec.control and spec.control.SetAlpha then
                spec.control:SetAlpha(locked and moduleEnabled and 0.56 or (moduleEnabled and 1 or 0.55))
            end
            if spec.control and spec.control.Text and spec.control.Text.SetAlpha then
                spec.control.Text:SetAlpha(locked and moduleEnabled and 0.56 or (moduleEnabled and 1 or 0.7))
            end
        end

        applyDockGizmoVisibilityOverrideState = function()
            local moduleEnabled = isDockModuleEnabled()
            local gizmoModeEnabled = moduleEnabled and PA_SafeBool(PortalAuthorityDB and PortalAuthorityDB.dockGizmoMode)

            local function syncVisibilityControl(spec)
                if not spec then
                    return
                end
                if spec.Refresh then
                    spec:Refresh()
                end
                if gizmoModeEnabled then
                    if spec.SetVisualSelectedValue then
                        spec:SetVisualSelectedValue(false)
                    elseif spec.SetSelectedValue then
                        spec:SetSelectedValue(false, false)
                    end
                    setDockVisibilityControlGizmoLocked(spec, true, moduleEnabled)
                else
                    setSettingsControlEnabled(spec, moduleEnabled)
                    setDockVisibilityControlGizmoLocked(spec, false, moduleEnabled)
                end
            end

            syncVisibilityControl(widgets.hideCity)
            syncVisibilityControl(widgets.hideDungeon)
            syncVisibilityControl(widgets.hideCombat)
        end

        for _, spec in ipairs({ widgets.dockEnabled, widgets.hideCity, widgets.hideCombat, widgets.hideDungeon, widgets.gizmoMode }) do
            if spec and spec.label then
                spec.label:SetWordWrap(false)
                if spec.label.SetMaxLines then
                    spec.label:SetMaxLines(1)
                end
            end
        end

        styleSecondaryActionButton(widgets.resetDockAnchor, {
            baseBg = { 0.06, 0.07, 0.09, 0.95 },
            hoverBg = { 0.08, 0.09, 0.11, 0.95 },
            pressedBg = { 0.05, 0.06, 0.08, 0.96 },
            baseBorder = { 0.21, 0.24, 0.28, 0.62 },
            hoverBorder = { 0.25, 0.28, 0.33, 0.68 },
            pressedBorder = { 0.23, 0.26, 0.30, 0.66 },
            textColor = { 0.82, 0.83, 0.86, 1 },
        })
        widgets.resetDockAnchor:HookScript("OnEnter", function(selfButton)
            GameTooltip:SetOwner(selfButton, "ANCHOR_TOP")
            GameTooltip:SetText("Resets dock position to default.", 1, 1, 1, 1)
            GameTooltip:Show()
        end)
        widgets.resetDockAnchor:HookScript("OnLeave", function()
            GameTooltip:Hide()
        end)
        widgets.loadoutPresetSegment = createSegmentedChoice("", "dockLoadoutPreset", {
            { label = "|TInterface\\AddOns\\PortalAuthority\\Media\\Images\\WoW_Overclock_Midnight_Logo_enUS_64.png:24:24:0:0|t Midnight Season 01", value = "SEASON_01" },
            { label = "Custom", value = "CUSTOM" },
        }, function(value)
            local selectedPreset = normalizeLoadoutPresetValue(value)
            local previousPreset = lastLoadoutPresetSelection
            lastLoadoutPresetSelection = selectedPreset
            if widgets.destinationPicker and widgets.destinationPicker.Close then
                widgets.destinationPicker:Close()
            end
            if selectedPreset == "CUSTOM" then
                local restored = restoreCustomLoadoutSnapshot()
                if restored then
                    onUIChanged(false)
                end
            elseif selectedPreset == "SEASON_01" then
                if previousPreset == "CUSTOM" then
                    saveCustomLoadoutSnapshot()
                end
                suppressLoadoutPresetAutoCustom = true
                applySeason01Loadout()
                suppressLoadoutPresetAutoCustom = false
            end
        end, { semantic = "selection" })
        if widgets.loadoutPresetSegment and widgets.loadoutPresetSegment.label then
            widgets.loadoutPresetSegment.label:SetText("")
            widgets.loadoutPresetSegment.label:Hide()
        end

        local function CreateLayoutSection()
            widgets.layoutSectionHeader = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlight", 12)
            widgets.layoutSectionHeader:SetText("Structure")
            widgets.layoutSectionHeader:SetTextColor(1, 1, 1, 1)

            widgets.layoutModeSegment = createSegmentedChoice("Layout Mode", "dockSimpleLayoutMode", {
                { label = "Horizontal Row", value = "HORIZONTAL_ROW" },
                { label = "Vertical Column", value = "VERTICAL_COLUMN" },
                { label = "Grid", value = "GRID" },
            }, function(value)
                PortalAuthorityDB.dockSimpleLayoutMode = normalizeDockSimpleLayoutModeValue(value or PortalAuthorityDB.dockSimpleLayoutMode)
                PortalAuthorityDB.dockSimpleLayoutModePersist = PortalAuthorityDB.dockSimpleLayoutMode
                ApplyDockLayoutSettings(false)
                updateLayoutModeApplicability()
                syncLayoutModeSegmentSelection(false)
                if C_Timer and C_Timer.After then
                    C_Timer.After(0, function()
                        if not panel or not panel:IsShown() then
                            return
                        end
                        syncLayoutModeSegmentSelection(false)
                        updateLayoutModeApplicability()
                    end)
                end
            end, {
                semantic = "selection",
                normalizeValue = normalizeDockSimpleLayoutModeValue,
                getValue = function()
                    local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
                    local mode = getDockSimpleLayoutModeForSettings(defaults)
                    return mode
                end,
                setValue = function(mode)
                    local value = normalizeDockSimpleLayoutModeValue(mode)
                    PortalAuthorityDB.dockSimpleLayoutMode = value
                    PortalAuthorityDB.dockSimpleLayoutModePersist = value
                end,
            })

            widgets.iconsPerRow = CreateStepperControl(scrollChild, "Wrap", 1, 10, 1, "dockIconsPerLine", FIXED_FOOTPRINTS.LAYOUT_STEPPER_SEED_W, function()
                PortalAuthorityDB.dockWrapAfter = PortalAuthorityDB.dockIconsPerLine
                ApplyDockLayoutSettings(false)
            end)
            widgets.iconsPerRowGridOnlyHint = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 10)
            widgets.iconsPerRowGridOnlyHint:SetText("Grid only")
            widgets.iconsPerRowGridOnlyHint:SetTextColor(0.58, 0.58, 0.58, 0.92)
            widgets.iconsPerRowGridOnlyHint:Hide()

            widgets.layoutIconSize = CreatePremiumSlider(scrollChild, "Icon Size", 16, 72, 1, "dockIconSize", FIXED_FOOTPRINTS.LAYOUT_ICON_SLIDER_SEED_W, function()
                ApplyDockLayoutSettings(false)
            end)

            widgets.layoutSpacingHint = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 10)
            widgets.layoutSpacingHint:SetText("Tip: Unlock the dock to adjust icon spacing visually.")
            widgets.layoutSpacingHint:SetTextColor(0.58, 0.58, 0.58, 0.92)
        end

        local function CreateAppearanceSection()
            widgets.labelsSectionHeader = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlight", 12)
            widgets.labelsSectionHeader:SetText("Labels")
            widgets.labelsSectionHeader:SetTextColor(1, 1, 1, 1)

            local function applyDockLabelSideSelection(value, modeOverride)
                local resolvedMode = normalizeDockLabelModeValue(modeOverride or (PortalAuthorityDB and PortalAuthorityDB.dockLabelMode) or "OUTSIDE")
                setDockLabelSideForSettings(value, resolvedMode)
                ApplyDockAppearanceSettings()
                syncLabelSideSegmentSelection(false)
                if updateLabelModeApplicability then
                    updateLabelModeApplicability()
                end
                relayout()
                if C_Timer and C_Timer.After then
                    C_Timer.After(0, function()
                        if not panel or not panel:IsShown() then
                            return
                        end
                        syncLabelSideSegmentSelection(true)
                        if updateLabelModeApplicability then
                            updateLabelModeApplicability()
                        end
                        relayout()
                    end)
                end
            end

            widgets.labelMode = createSegmentedChoice("Labels", "dockLabelMode", {
                { label = "Off", value = "OFF", toggleState = "off" },
                { label = "Outside", value = "OUTSIDE", toggleState = "on" },
                { label = "Inside", value = "INSIDE", toggleState = "on" },
            }, function(value)
                local resolvedMode = normalizeDockLabelModeValue(value or PortalAuthorityDB.dockLabelMode)
                PortalAuthorityDB.dockLabelMode = resolvedMode
                PortalAuthorityDB.dockLabelModePersist = resolvedMode
                PortalAuthorityDB.dockHideDungeonName = (resolvedMode == "OFF")
                ApplyDockAppearanceSettings()
                syncLabelModeSegmentSelection(false)
                syncLabelSideSegmentSelection(false)
                if updateLabelModeApplicability then
                    updateLabelModeApplicability()
                end
                relayout()
                if C_Timer and C_Timer.After then
                    C_Timer.After(0, function()
                        if not panel or not panel:IsShown() then
                            return
                        end
                        syncLabelModeSegmentSelection(true)
                        syncLabelSideSegmentSelection(true)
                        if updateLabelModeApplicability then
                            updateLabelModeApplicability()
                        end
                        relayout()
                    end)
                end
            end, {
                semantic = "selection",
                normalizeValue = normalizeDockLabelModeValue,
                getValue = function()
                    local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
                    return getDockLabelModeForSettings(defaults)
                end,
                setValue = function(mode)
                    local resolvedMode = normalizeDockLabelModeValue(mode)
                    PortalAuthorityDB.dockLabelMode = resolvedMode
                    PortalAuthorityDB.dockLabelModePersist = resolvedMode
                    PortalAuthorityDB.dockHideDungeonName = (resolvedMode == "OFF")
                end,
            })

            widgets.labelSide = createSegmentedChoice("Label Side", "dockLabelSide", {
                { label = "Bottom", value = "BOTTOM" },
                { label = "Top", value = "TOP" },
                { label = "Left", value = "LEFT" },
                { label = "Right", value = "RIGHT" },
            }, function(value)
                applyDockLabelSideSelection(value or (PortalAuthorityDB and PortalAuthorityDB.dockLabelSide), "OUTSIDE")
            end, {
                semantic = "selection",
                normalizeValue = normalizeDockLabelSideValue,
                getValue = function()
                    local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
                    return getDockOutsideLabelSideForSettings(defaults)
                end,
                setValue = function(side)
                    setDockLabelSideForSettings(side, "OUTSIDE")
                end,
            })

            do
                local insideContainer = CreateFrame("Frame", nil, scrollChild)
                local topBottom = createPremiumSegmentedChoice(insideContainer, "", nil, {
                    { label = "Top", value = "TOP" },
                    { label = "Bottom", value = "BOTTOM" },
                }, function(value)
                    applyDockLabelSideSelection(value, "INSIDE")
                end, {
                    semantic = "selection",
                    normalizeValue = normalizeDockLabelSideValue,
                    minWidth = 0,
                })
                local center = createPremiumSegmentedChoice(insideContainer, "", nil, {
                    { label = "Center", value = "CENTER" },
                }, function(value)
                    applyDockLabelSideSelection(value, "INSIDE")
                end, {
                    semantic = "selection",
                    normalizeValue = normalizeDockLabelSideValue,
                    minWidth = 0,
                })

                if topBottom.label then
                    topBottom.label:Hide()
                end
                if center.label then
                    center.label:Hide()
                end

                widgets.labelSideInside = {
                    kind = "segmented_composite",
                    label = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlight", 12),
                    control = insideContainer,
                    topBottom = topBottom,
                    center = center,
                }
                widgets.labelSideInside.label:SetText("Label Side")

                widgets.labelSideInside.Layout = function(_, width)
                    local totalWidth = math.max(120, tonumber(width) or 120)
                    local groupGap = 4
                    local halfWidth = math.max(58, (totalWidth - groupGap) / 2)

                    insideContainer:SetSize(totalWidth, 22)

                    topBottom.control:ClearAllPoints()
                    topBottom.control:SetPoint("TOPLEFT", insideContainer, "TOPLEFT", 0, 0)
                    topBottom:Layout(halfWidth)

                    center.control:ClearAllPoints()
                    center.control:SetPoint("TOPLEFT", topBottom.control, "TOPRIGHT", groupGap, 0)
                    center:Layout(halfWidth)

                    if topBottom.label then
                        topBottom.label:Hide()
                    end
                    if center.label then
                        center.label:Hide()
                    end
                end

                widgets.labelSideInside.Refresh = function(self)
                    local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
                    self:SetVisualSelectedValue(getDockInsideLabelSideForSettings(defaults))
                end

                widgets.labelSideInside.SetVisualSelectedValue = function(_, value)
                    local visual = normalizeDockLabelSideValue(value)
                    if visual ~= "TOP" and visual ~= "BOTTOM" then
                        visual = "CENTER"
                    end
                    topBottom:SetVisualSelectedValue(visual)
                    center:SetVisualSelectedValue(visual)
                end

                widgets.labelSideInside.SetEnabled = function(_, enabled)
                    topBottom:SetEnabled(enabled and true or false)
                    center:SetEnabled(enabled and true or false)
                    widgets.labelSideInside.label:SetAlpha(enabled and 1 or 0.65)
                end

                widgets.labelSideInside:Refresh()
            end

            widgets.styleSectionHeader = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlight", 12)
            widgets.styleSectionHeader:SetText("Style")
            widgets.styleSectionHeader:SetTextColor(1, 1, 1, 1)

            widgets.font = CreateFontPicker(scrollChild, "Font", "dockFontPath", FIXED_FOOTPRINTS.FONT_PICKER_SEED_W, function()
                ApplyDockAppearanceSettings()
            end)
            widgets.fontSize = CreateStepperControl(scrollChild, "Size", 8, 48, 1, "dockFontSize", FIXED_FOOTPRINTS.FONT_SIZE_STEPPER_SEED_W, function()
                ApplyDockAppearanceSettings()
            end, nil, { micro = true, shiftMultiplier = 5 })
            if widgets.fontSize and widgets.fontSize.label then
                widgets.fontSize.label:SetText("")
                widgets.fontSize.label:Hide()
            end
            widgets.outlineToggle = createSegmentedChoice("Outline", "dockTextOutline", {
                { label = "Off", value = "", toggleState = "off" },
                { label = "On", value = "OUTLINE", toggleState = "on" },
            }, function()
                ApplyDockAppearanceSettings()
            end, {
                semantic = "toggle",
                normalizeValue = normalizeOutlineToggleValue,
                getValue = function()
                    local raw = PortalAuthorityDB and PortalAuthorityDB.dockTextOutline or ""
                    return normalizeOutlineToggleValue(raw)
                end,
                setValue = function(value)
                    PortalAuthorityDB.dockTextOutline = normalizeOutlineToggleValue(value)
                end,
            })

            widgets.color = {}
            widgets.color.inlineLabel = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 11)
            widgets.color.inlineLabel:SetText("")
            widgets.color.inlineLabel:Hide()
            widgets.color.swatch = CreateFrame("Button", nil, scrollChild, "BackdropTemplate")
            widgets.color.swatch:SetBackdrop({
                bgFile = "Interface\\Buttons\\WHITE8x8",
                edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
                edgeSize = 10,
                insets = { left = 2, right = 2, top = 2, bottom = 2 },
            })

            widgets.hoverGlowSegment = createSegmentedChoice("Hover Glow", "dockHoverGlow", {
                { label = "Off", value = false, toggleState = "off" },
                { label = "On", value = true, toggleState = "on" },
            }, function()
                ApplyDockAppearanceSettings()
            end, {
                semantic = "toggle",
                normalizeValue = normalizeHoverGlowToggleValue,
                getValue = function()
                    return normalizeHoverGlowToggleValue(PortalAuthorityDB and PortalAuthorityDB.dockHoverGlow)
                end,
                setValue = function(value)
                    PortalAuthorityDB.dockHoverGlow = normalizeHoverGlowToggleValue(value)
                end,
            })
        end

        widgets.portalSlotsLabel = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlight", 12)
        widgets.portalSlotsLabel:SetText("")
        widgets.portalSlotsLabel:SetTextColor(1, 1, 1, 1)
        widgets.slotColStatus = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 11)
        widgets.slotColStatus:SetText("")
        widgets.slotColDestination = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 11)
        widgets.slotColDestination:SetText("Destination")
        widgets.slotColName = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 11)
        widgets.slotColName:SetText("Label")
        widgets.slotColSpellID = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 11)
        widgets.slotColSpellID:SetText("Spell ID")
        widgets.slotColResult = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 11)
        widgets.slotColResult:SetText("")
        CreateLayoutSection()
        CreateAppearanceSection()
        updateLayoutModeApplicability()
        syncLabelModeSegmentSelection(true)
        syncLabelSideSegmentSelection(true)
        if updateLabelModeApplicability then
            updateLabelModeApplicability()
        end
        local initialDestinationChoices = getDestinationChoices()
        widgets.destinationPicker = CreatePicker(UIParent, initialDestinationChoices, nil)
        widgets.destinationPicker._sourceOptionsRef = initialDestinationChoices
        widgets.destinationPicker:HookScript("OnHide", function(picker)
            local owner = picker and picker._ownerButton or nil
            if owner and owner.SetPillState then
                owner:SetPillState("normal")
            elseif owner and owner.UpdateVisual then
                owner._open = false
                owner:UpdateVisual()
            end
            if picker then
                picker._ownerButton = nil
            end
        end)
        local function captureDockPopoverTemplateChromeRegions(edit)
            if not edit or edit._paDockPopoverTemplateChromeRegions then
                return
            end

            local regions = {}
            local seen = {}
            local function capture(region)
                if not region or seen[region] then
                    return
                end
                if not region.GetObjectType or region:GetObjectType() ~= "Texture" then
                    return
                end
                seen[region] = true
                regions[#regions + 1] = region
            end

            capture(edit.Left)
            capture(edit.Middle)
            capture(edit.Right)

            if edit.GetRegions then
                for i = 1, select("#", edit:GetRegions()) do
                    capture(select(i, edit:GetRegions()))
                end
            end

            edit._paDockPopoverTemplateChromeRegions = regions
        end

        local function suppressDockPopoverTemplateChrome(edit)
            if not edit then
                return
            end

            captureDockPopoverTemplateChromeRegions(edit)
            for _, region in ipairs(edit._paDockPopoverTemplateChromeRegions or {}) do
                if region.SetAlpha then
                    region:SetAlpha(0)
                end
                if region.Hide then
                    region:Hide()
                end
            end
        end

        local function ensureDockPopoverEditSkin(edit, textSize)
            if not edit then
                return
            end

            captureDockPopoverTemplateChromeRegions(edit)
            applyGlobalFontToObject(edit, tonumber(textSize) or 11)
            edit:SetTextColor(1, 1, 1, 1)
            edit:SetTextInsets(8, 8, 0, 0)
            edit:SetJustifyH("LEFT")

            if not edit._paPremiumBg then
                edit._paPremiumBg = edit:CreateTexture(nil, "BACKGROUND")
                edit._paPremiumBg:SetAllPoints(edit)
                edit._paPremiumBg:SetTexture("Interface\\Buttons\\WHITE8x8")
                edit._paPremiumBorder = CreateFrame("Frame", nil, edit, "BackdropTemplate")
                edit._paPremiumBorder:SetBackdrop({
                    edgeFile = "Interface\\Buttons\\WHITE8x8",
                    edgeSize = 1,
                })
            end

            local function refreshDockPopoverEditSkin()
                suppressDockPopoverTemplateChrome(edit)
                local parent = edit:GetParent()
                local parentStrata = parent and parent.GetFrameStrata and parent:GetFrameStrata() or "DIALOG"
                local parentLevel = parent and parent.GetFrameLevel and parent:GetFrameLevel() or 1
                edit:SetFrameStrata(parentStrata)
                edit:SetFrameLevel(math.max(parentLevel + 10, edit:GetFrameLevel() or 1))
                if edit._paPremiumBorder then
                    edit._paPremiumBorder:SetPoint("TOPLEFT", edit, "TOPLEFT", -1, 1)
                    edit._paPremiumBorder:SetPoint("BOTTOMRIGHT", edit, "BOTTOMRIGHT", 1, -1)
                    edit._paPremiumBorder:SetFrameStrata(parentStrata)
                    edit._paPremiumBorder:SetFrameLevel(math.max(parentLevel + 9, 1))
                end
                if edit:HasFocus() then
                    edit._paPremiumBg:SetVertexColor(0.12, 0.13, 0.17, 0.95)
                    edit._paPremiumBorder:SetBackdropBorderColor(0.30, 0.33, 0.38, 1.0)
                else
                    edit._paPremiumBg:SetVertexColor(0.10, 0.11, 0.14, 0.95)
                    edit._paPremiumBorder:SetBackdropBorderColor(0.24, 0.27, 0.32, 1.0)
                end
            end

            if not edit._paDockPopoverSkinInitialized then
                edit:HookScript("OnShow", refreshDockPopoverEditSkin)
                edit:HookScript("OnEditFocusGained", refreshDockPopoverEditSkin)
                edit:HookScript("OnEditFocusLost", refreshDockPopoverEditSkin)
                edit._paDockPopoverSkinInitialized = true
                edit._paDockPopoverRefreshVisual = refreshDockPopoverEditSkin
            end

            refreshDockPopoverEditSkin()
        end

        local function restoreDockPopoverAnchorLabel(popover, anchorRow)
            if not popover then
                return
            end

            local label = popover._paSuppressedAnchorLabel
            if label then
                if popover._paSuppressedAnchorLabelWasShown then
                    if label.Show then
                        label:Show()
                    end
                else
                    if label.Hide then
                        label:Hide()
                    end
                end
                if label.SetAlpha then
                    label:SetAlpha(popover._paSuppressedAnchorLabelAlpha or 1)
                end
            end

            popover._paSuppressedAnchorLabel = nil
            popover._paSuppressedAnchorLabelAlpha = nil
            popover._paSuppressedAnchorLabelWasShown = nil

            anchorRow = anchorRow or popover.anchorRow
            if type(popover._paReleaseAnchorVisual) == "function" then
                popover._paReleaseAnchorVisual(anchorRow)
            end
        end

        local function suppressDockPopoverAnchorLabel(popover, row)
            if not popover then
                return
            end

            restoreDockPopoverAnchorLabel(popover)

            local anchorLabelKey = popover._paAnchorLabelKey
            local anchorLabel = anchorLabelKey and row and row[anchorLabelKey] and row[anchorLabelKey].label or nil
            if anchorLabel and anchorLabel.GetAlpha and anchorLabel.SetAlpha then
                popover._paSuppressedAnchorLabel = anchorLabel
                popover._paSuppressedAnchorLabelAlpha = anchorLabel:GetAlpha()
                popover._paSuppressedAnchorLabelWasShown = anchorLabel.IsShown and anchorLabel:IsShown() or true
                if anchorLabel.Hide then
                    anchorLabel:Hide()
                else
                    anchorLabel:SetAlpha(0)
                end
            end

            if type(popover._paApplyAnchorVisual) == "function" then
                popover._paApplyAnchorVisual(row)
            end
        end

        widgets.renamePopover = CreateFrame("Frame", nil, scrollChild, "BackdropTemplate")
        widgets.renamePopover:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8x8",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            edgeSize = 10,
            insets = { left = 2, right = 2, top = 2, bottom = 2 },
        })
        widgets.renamePopover:SetBackdropColor(0.05, 0.06, 0.08, 0.98)
        widgets.renamePopover:SetBackdropBorderColor(0.24, 0.27, 0.31, 1)
        widgets.renamePopover:SetFrameStrata("DIALOG")
        widgets.renamePopover:SetToplevel(true)
        widgets.renamePopover:SetClampedToScreen(true)
        widgets.renamePopover:Hide()
        widgets.renamePopover.slotIndex = nil
        widgets.renamePopover.anchorRow = nil
        widgets.renamePopover.originalText = ""

        widgets.renamePopover.edit = CreateFrame("EditBox", nil, widgets.renamePopover, "InputBoxTemplate")
        widgets.renamePopover.edit:SetAutoFocus(false)
        widgets.renamePopover.edit:SetTextInsets(8, 8, 0, 0)
        widgets.renamePopover.edit:SetTextColor(1, 1, 1, 1)
        widgets.renamePopover.edit:SetHighlightColor(1, 1, 1, 0.25)
        widgets.renamePopover.edit:SetMaxLetters(DOCK_NAME_MAX_LEN)
        ensureDockPopoverEditSkin(widgets.renamePopover.edit, 11)
        widgets.renamePopover._paAnchorLabelKey = "namePill"
        widgets.renamePopover._paApplyAnchorVisual = function(row)
            if row and row.namePill and row.namePill.SetPillState then
                row.namePill:SetPillState("focus")
            elseif row and row.namePill and row.namePill.UpdateVisual then
                row.namePill._focused = true
                row.namePill:UpdateVisual()
            end
        end
        widgets.renamePopover._paReleaseAnchorVisual = function(row)
            if row and row.namePill and row.namePill.SetPillState then
                row.namePill:SetPillState("normal")
            elseif row and row.namePill and row.namePill.UpdateVisual then
                row.namePill._focused = false
                row.namePill:UpdateVisual()
            end
        end

        widgets.renamePopover.hint = createStyledFontString(widgets.renamePopover, "ARTWORK", "GameFontHighlightSmall", 10)
        widgets.renamePopover.hint:SetJustifyH("LEFT")
        widgets.renamePopover.hint:SetTextColor(0.72, 0.72, 0.72, 1)
        widgets.renamePopover.hint:SetText("")

        local function updateRenameHint()
            local pop = widgets.renamePopover
            if not pop or not pop.edit or not pop.hint then
                return
            end
            local text = tostring(pop.edit:GetText() or "")
            local n = string.len(text)
            local over = n > DOCK_NAME_MAX_LEN
            pop.hint:SetText(string.format("%d/%d", n, DOCK_NAME_MAX_LEN))
            if over then
                pop.hint:SetTextColor(1, 0.36, 0.36, 1)
            else
                pop.hint:SetTextColor(0.72, 0.72, 0.72, 1)
            end
        end

        function widgets.renamePopover:Close(save)
            local slotIndex = self.slotIndex
            local edit = self.edit
            local anchorRow = self.anchorRow
            if not self:IsShown() then
                self.slotIndex = nil
                self.anchorRow = nil
                self.originalText = ""
                restoreDockPopoverAnchorLabel(self, anchorRow)
                return
            end

            local changed = false
            if save and slotIndex then
                local slot = getDockSlot(slotIndex)
                local previousName = tostring(slot.name or "")
                local previousRenamed = slot.renamed == true
                local text = trim(edit:GetText() or "")
                setDockSlotCustomName(slot, text)
                if tostring(slot.name or "") ~= previousName or (slot.renamed == true) ~= previousRenamed then
                    changed = true
                end
            end

            self.slotIndex = nil
            self.anchorRow = nil
            self.originalText = ""
            restoreDockPopoverAnchorLabel(self, anchorRow)
            self:Hide()
            if edit then
                edit:ClearFocus()
            end
            if changed then
                setLoadoutPresetCustom()
                onUIChanged(false)
            end
        end

        function widgets.renamePopover:OpenForRow(slotIndex, row)
            if not row or not row.namePill then
                return
            end
            if widgets.spellIDPopover and widgets.spellIDPopover:IsShown() and widgets.spellIDPopover.Close then
                widgets.spellIDPopover:Close(false)
            end
            if self.anchorRow and self.anchorRow ~= row then
                restoreDockPopoverAnchorLabel(self, self.anchorRow)
            end
            local slot = getDockSlot(slotIndex)
            if not slot.enabled then
                return
            end

            local current = getDockSlotDisplayName(slot)

            self:ClearAllPoints()
            self:SetFrameLevel(math.max((row.namePill:GetFrameLevel() or 1) + 20, self:GetFrameLevel() or 1))
            self:SetPoint("TOPLEFT", row.namePill, "TOPLEFT", -2, 2)
            self:SetPoint("TOPRIGHT", row.namePill, "TOPRIGHT", 2, 2)
            self:SetHeight(44)
            self.edit:ClearAllPoints()
            self.edit:SetPoint("TOPLEFT", self, "TOPLEFT", 6, -6)
            self.edit:SetPoint("TOPRIGHT", self, "TOPRIGHT", -6, -6)
            self.edit:SetHeight(20)
            self.hint:ClearAllPoints()
            self.hint:SetPoint("TOPLEFT", self.edit, "BOTTOMLEFT", 2, -2)

            self.slotIndex = slotIndex
            self.anchorRow = row
            self.originalText = current
            suppressDockPopoverAnchorLabel(self, row)
            self.edit:SetText(current)
            updateRenameHint()
            self:Show()
            self:Raise()
            if self.edit._paDockPopoverRefreshVisual then
                self.edit._paDockPopoverRefreshVisual()
            end
            self.edit:SetFocus()
            if self.edit.HighlightText then
                self.edit:HighlightText(0, 0)
            end
            if self.edit.SetCursorPosition then
                self.edit:SetCursorPosition(string.len(current or ""))
            end
        end

        widgets.renamePopover.edit:SetScript("OnEnterPressed", function()
            widgets.renamePopover:Close(true)
        end)
        widgets.renamePopover.edit:SetScript("OnEscapePressed", function()
            widgets.renamePopover:Close(false)
        end)
        widgets.renamePopover.edit:SetScript("OnEditFocusLost", function()
            if widgets.renamePopover and widgets.renamePopover:IsShown() then
                widgets.renamePopover:Close(false)
            end
        end)
        widgets.renamePopover.edit:SetScript("OnTextChanged", function()
            updateRenameHint()
        end)

        local DOCK_SPELLID_MAX_LEN = 10
        widgets.spellIDPopover = CreateFrame("Frame", nil, scrollChild, "BackdropTemplate")
        widgets.spellIDPopover:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8x8",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            edgeSize = 10,
            insets = { left = 2, right = 2, top = 2, bottom = 2 },
        })
        widgets.spellIDPopover:SetBackdropColor(0.05, 0.06, 0.08, 0.98)
        widgets.spellIDPopover:SetBackdropBorderColor(0.24, 0.27, 0.31, 1)
        widgets.spellIDPopover:SetFrameStrata("DIALOG")
        widgets.spellIDPopover:SetToplevel(true)
        widgets.spellIDPopover:SetClampedToScreen(true)
        widgets.spellIDPopover:Hide()
        widgets.spellIDPopover.slotIndex = nil
        widgets.spellIDPopover.anchorRow = nil
        widgets.spellIDPopover.originalText = ""

        widgets.spellIDPopover.edit = CreateFrame("EditBox", nil, widgets.spellIDPopover, "InputBoxTemplate")
        widgets.spellIDPopover.edit:SetAutoFocus(false)
        widgets.spellIDPopover.edit:SetTextInsets(8, 8, 0, 0)
        widgets.spellIDPopover.edit:SetTextColor(1, 1, 1, 1)
        widgets.spellIDPopover.edit:SetHighlightColor(1, 1, 1, 0.25)
        widgets.spellIDPopover.edit:SetMaxLetters(DOCK_SPELLID_MAX_LEN)
        ensureDockPopoverEditSkin(widgets.spellIDPopover.edit, 11)
        widgets.spellIDPopover._paAnchorLabelKey = "spellIDPill"
        widgets.spellIDPopover._paApplyAnchorVisual = nil
        widgets.spellIDPopover._paReleaseAnchorVisual = nil

        widgets.spellIDPopover.hint = createStyledFontString(widgets.spellIDPopover, "ARTWORK", "GameFontHighlightSmall", 10)
        widgets.spellIDPopover.hint:SetJustifyH("LEFT")
        widgets.spellIDPopover.hint:SetTextColor(0.72, 0.72, 0.72, 1)
        widgets.spellIDPopover.hint:SetText("")

        local function updateSpellIDHint()
            local pop = widgets.spellIDPopover
            if not pop or not pop.edit or not pop.hint then
                return
            end
            local text = trim(tostring(pop.edit:GetText() or ""))
            local n = string.len(text)
            local over = n > DOCK_SPELLID_MAX_LEN
            local numeric = (text == "" or text:match("^%d+$") ~= nil)
            pop.hint:SetText(string.format("%d/%d", n, DOCK_SPELLID_MAX_LEN))
            if over or not numeric then
                pop.hint:SetTextColor(1, 0.36, 0.36, 1)
            else
                pop.hint:SetTextColor(0.72, 0.72, 0.72, 1)
            end
        end

        function widgets.spellIDPopover:Close(save)
            local slotIndex = self.slotIndex
            local edit = self.edit
            local anchorRow = self.anchorRow
            if not self:IsShown() then
                self.slotIndex = nil
                self.anchorRow = nil
                self.originalText = ""
                restoreDockPopoverAnchorLabel(self, anchorRow)
                return
            end

            local changed = false
            if save and slotIndex then
                local slot = getDockSlot(slotIndex)
                local rawValue = tonumber(edit:GetText() or "")
                if rawValue ~= nil then
                    local spellID = resolveSpellIDFromVisualDockID(rawValue)
                    if slot.spellID ~= spellID then
                        changed = true
                    end
                    slot.spellID = spellID
                    if spellID > 0 and getDestinationOptionByValue(tostring(spellID)) then
                        slot.selection = tostring(spellID)
                    else
                        slot.selection = "CUSTOM"
                    end
                    setDockSlotDefaultName(slot, spellID)
                    if spellID > 0 and trim(slot.name or "") == "" then
                        requestSpellDataRefresh(slotIndex, spellID)
                    end
                end
            end

            self.slotIndex = nil
            self.anchorRow = nil
            self.originalText = ""
            restoreDockPopoverAnchorLabel(self, anchorRow)
            self:Hide()
            if edit then
                edit:ClearFocus()
            end
            if changed then
                setLoadoutPresetCustom()
                onUIChanged(false)
            end
        end

        function widgets.spellIDPopover:OpenForRow(slotIndex, row)
            if not row or not row.spellIDPill then
                return
            end
            local slot = getDockSlot(slotIndex)
            if not slot.enabled then
                return
            end

            if widgets.renamePopover and widgets.renamePopover:IsShown() and widgets.renamePopover.Close then
                widgets.renamePopover:Close(false)
            end
            if self.anchorRow and self.anchorRow ~= row then
                restoreDockPopoverAnchorLabel(self, self.anchorRow)
            end

            local current = tostring(getVisualDockIDForSpell(slot.spellID or 0))
            self:ClearAllPoints()
            self:SetFrameLevel(math.max((row.spellIDPill:GetFrameLevel() or 1) + 20, self:GetFrameLevel() or 1))
            self:SetPoint("TOPLEFT", row.spellIDPill, "TOPLEFT", -2, 2)
            self:SetPoint("TOPRIGHT", row.spellIDPill, "TOPRIGHT", 2, 2)
            self:SetHeight(44)
            self.edit:ClearAllPoints()
            self.edit:SetPoint("TOPLEFT", self, "TOPLEFT", 6, -6)
            self.edit:SetPoint("TOPRIGHT", self, "TOPRIGHT", -6, -6)
            self.edit:SetHeight(20)
            self.hint:ClearAllPoints()
            self.hint:SetPoint("TOPLEFT", self.edit, "BOTTOMLEFT", 2, -2)

            self.slotIndex = slotIndex
            self.anchorRow = row
            self.originalText = current
            suppressDockPopoverAnchorLabel(self, row)
            self.edit:SetText(current)
            updateSpellIDHint()
            self:Show()
            self:Raise()
            if self.edit._paDockPopoverRefreshVisual then
                self.edit._paDockPopoverRefreshVisual()
            end
            self.edit:SetFocus()
            if self.edit.HighlightText then
                self.edit:HighlightText(0, 0)
            end
            if self.edit.SetCursorPosition then
                self.edit:SetCursorPosition(string.len(current or ""))
            end
        end

        widgets.spellIDPopover.edit:SetScript("OnEnterPressed", function()
            widgets.spellIDPopover:Close(true)
        end)
        widgets.spellIDPopover.edit:SetScript("OnEscapePressed", function()
            widgets.spellIDPopover:Close(false)
        end)
        widgets.spellIDPopover.edit:SetScript("OnEditFocusLost", function()
            if widgets.spellIDPopover and widgets.spellIDPopover:IsShown() then
                widgets.spellIDPopover:Close(false)
            end
        end)
        widgets.spellIDPopover.edit:SetScript("OnTextChanged", function()
            updateSpellIDHint()
        end)

        local function setPillCursor(mode)
            if not SetCursor and not ResetCursor then
                return
            end

            local function trySet(path)
                if not SetCursor or not path or path == "" then
                    return false
                end
                local ok = pcall(SetCursor, path)
                return ok and true or false
            end

            if mode == "ibeam" then
                if trySet("Interface\\Cursor\\UI-Cursor-Chat") then
                    return
                end
                if trySet("Interface\\CURSOR\\UI-Cursor-Chat") then
                    return
                end
            elseif mode == "hand" then
                if trySet("Interface\\Cursor\\UI-Cursor-Move") then
                    return
                end
                if trySet("Interface\\CURSOR\\UI-Cursor-Move") then
                    return
                end
            end

            if ResetCursor then
                pcall(ResetCursor)
            end
        end

        local function SetPillState(frame, state)
            if not frame then
                return
            end
            local s = tostring(state or "normal")
            frame._disabled = (s == "disabled")
            frame._focused = (s == "focus")
            frame._open = (s == "open")
            frame._pressed = (s == "pressed")
            frame._hovered = (s == "hover")
            if frame._disabled then
                frame._focused = false
                frame._open = false
                frame._pressed = false
                frame._hovered = false
            end
            if frame.UpdateVisual then
                frame:UpdateVisual()
            end
        end

        local function styleRowPill(frame, palette, pillKind)
            frame:SetBackdrop({
                bgFile = "Interface\\Buttons\\WHITE8x8",
                edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
                edgeSize = 8,
                insets = { left = 1, right = 1, top = 1, bottom = 1 },
            })
            frame._palette = palette or {}
            frame._pillKind = pillKind or "readonly"
            frame._hovered = false
            frame._pressed = false
            frame._focused = false
            frame._open = false
            frame._disabled = false

            if not frame.glow then
                frame.glow = frame:CreateTexture(nil, "BORDER")
                frame.glow:SetAllPoints()
                frame.glow:SetTexture("Interface\\Buttons\\WHITE8x8")
                frame.glow:SetBlendMode("ADD")
            end

            function frame:SetPillState(state)
                SetPillState(self, state)
            end

            function frame:UpdateVisual()
                local p = self._palette or {}
                local state = "base"
                if self._disabled then
                    state = "disabled"
                elseif self._focused then
                    state = "focus"
                elseif self._open then
                    state = "open"
                elseif self._pressed then
                    state = "pressed"
                elseif self._hovered then
                    state = "hover"
                end
                local c = p[state] or p.base or { 0.08, 0.09, 0.12, 0.95, 0.24, 0.27, 0.32, 1, 0 }
                self:SetBackdropColor(c[1], c[2], c[3], c[4])
                self:SetBackdropBorderColor(c[5], c[6], c[7], c[8])
                if self.glow then
                    self.glow:SetVertexColor(1, 0.82, 0.1, c[9] or 0)
                end
                if self.label then
                    local tc = p.text or { 1, 1, 1, 1 }
                    local td = p.textDisabled or { 0.62, 0.62, 0.64, 1 }
                    if self._disabled then
                        self.label:SetTextColor(td[1], td[2], td[3], td[4])
                    else
                        self.label:SetTextColor(tc[1], tc[2], tc[3], tc[4])
                    end
                end
                if self.chevron then
                    local cc = p.chevron or { 0.78, 0.78, 0.82, 1 }
                    local cd = p.chevronDisabled or { 0.45, 0.45, 0.48, 1 }
                    if self._disabled then
                        self.chevron:SetTextColor(cd[1], cd[2], cd[3], cd[4])
                    else
                        self.chevron:SetTextColor(cc[1], cc[2], cc[3], cc[4])
                    end
                end
                if self.editGlyph then
                    self.editGlyph:SetShown((not self._disabled) and self._hovered and (not self._focused))
                end
            end

            frame:SetScript("OnEnter", function(selfFrame)
                if selfFrame._disabled then
                    selfFrame._hovered = false
                    selfFrame:UpdateVisual()
                    return
                end
                selfFrame._hovered = true
                selfFrame:UpdateVisual()
            end)
            frame:SetScript("OnLeave", function(selfFrame)
                selfFrame._hovered = false
                selfFrame._pressed = false
                selfFrame:UpdateVisual()
            end)
            frame:SetScript("OnMouseDown", function(selfFrame)
                if selfFrame._disabled then
                    return
                end
                selfFrame._pressed = true
                selfFrame:UpdateVisual()
            end)
            frame:SetScript("OnMouseUp", function(selfFrame)
                selfFrame._pressed = false
                selfFrame:UpdateVisual()
            end)
            frame:UpdateVisual()
        end

        local function ApplyPillStyle_Editable(frame)
            styleRowPill(frame, {
                base = { CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A, CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A, 0.00 },
                hover = { CONTROL_HOVER_BG_R, CONTROL_HOVER_BG_G, CONTROL_HOVER_BG_B, CONTROL_HOVER_BG_A, CONTROL_HOVER_BORDER_R, CONTROL_HOVER_BORDER_G, CONTROL_HOVER_BORDER_B, CONTROL_HOVER_BORDER_A, 0.03 },
                pressed = { CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A, CONTROL_HOVER_BORDER_R, CONTROL_HOVER_BORDER_G, CONTROL_HOVER_BORDER_B, CONTROL_HOVER_BORDER_A, 0.02 },
                focus = { CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A, 1.00, 0.82, 0.10, 0.95, 0.08 },
                disabled = { 0.05, 0.06, 0.08, 0.86, 0.16, 0.18, 0.22, 0.85, 0.00 },
                text = { 0.90, 0.90, 0.92, 1 },
                textDisabled = { 0.58, 0.58, 0.61, 1 },
            }, "editable")
            if not frame.editGlyph then
                frame.editGlyph = createStyledFontString(frame, "ARTWORK", "GameFontHighlightSmall", 10)
                frame.editGlyph:SetPoint("RIGHT", frame, "RIGHT", -6, 0)
                frame.editGlyph:SetText("...")
                frame.editGlyph:SetTextColor(0.72, 0.72, 0.76, 0.9)
                frame.editGlyph:Hide()
            end
        end

        local function ApplyPillStyle_Dropdown(frame)
            styleRowPill(frame, {
                base = { CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A, CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A, 0.00 },
                hover = { CONTROL_HOVER_BG_R, CONTROL_HOVER_BG_G, CONTROL_HOVER_BG_B, CONTROL_HOVER_BG_A, CONTROL_HOVER_BORDER_R, CONTROL_HOVER_BORDER_G, CONTROL_HOVER_BORDER_B, CONTROL_HOVER_BORDER_A, 0.04 },
                pressed = { CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A, CONTROL_HOVER_BORDER_R, CONTROL_HOVER_BORDER_G, CONTROL_HOVER_BORDER_B, CONTROL_HOVER_BORDER_A, 0.03 },
                open = { CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A, 1.00, 0.82, 0.10, 0.82, 0.07 },
                focus = { CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A, CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A, 0.00 },
                disabled = { 0.05, 0.06, 0.08, 0.86, 0.16, 0.18, 0.22, 0.85, 0.00 },
                text = { 0.95, 0.95, 0.97, 1 },
                textDisabled = { 0.56, 0.56, 0.60, 1 },
                chevron = { 0.78, 0.78, 0.82, 1 },
                chevronDisabled = { 0.45, 0.45, 0.48, 1 },
            }, "dropdown")
        end

        local function ApplyPillStyle_Readonly(frame)
            styleRowPill(frame, {
                base = { 0.05, 0.06, 0.08, 0.90, 0.17, 0.19, 0.24, 0.9, 0.00 },
                hover = { 0.05, 0.06, 0.08, 0.90, 0.17, 0.19, 0.24, 0.9, 0.00 },
                pressed = { 0.05, 0.06, 0.08, 0.90, 0.17, 0.19, 0.24, 0.9, 0.00 },
                focus = { 0.05, 0.06, 0.08, 0.90, 0.17, 0.19, 0.24, 0.9, 0.00 },
                disabled = { 0.04, 0.05, 0.07, 0.82, 0.14, 0.16, 0.21, 0.82, 0.00 },
                text = { 0.78, 0.78, 0.82, 1 },
                textDisabled = { 0.52, 0.52, 0.55, 1 },
            }, "readonly")
        end

        local function registerDockSlotRowSearch(row)
            if type(row) ~= "table" or not frameIsAlive(row.enabled) then
                return nil
            end

            local primaryRef = row.destination and row.destination.label or nil
            if not frameIsAlive(primaryRef) then
                primaryRef = row.namePill and row.namePill.label or nil
            end
            if not frameIsAlive(primaryRef) then
                return nil
            end

            local extraRefs = {}
            local candidateRefs = {
                row.namePill and row.namePill.label or nil,
                row.spellIDPill and row.spellIDPill.label or nil,
                row.destination and row.destination.label or nil,
            }
            for i = 1, #candidateRefs do
                local ref = candidateRefs[i]
                if frameIsAlive(ref) and ref ~= primaryRef then
                    extraRefs[#extraRefs + 1] = ref
                end
            end

            return settingsSearchRuntime.registerControlText(primaryRef, row.enabled, "toggle", extraRefs)
        end

        local function createRowEnableToggle(parent, slotIndex)
            local holder = CreateFrame("Frame", nil, parent)
            holder:SetSize(FIXED_FOOTPRINTS.ROW_ENABLE_HOLDER_W, FIXED_FOOTPRINTS.ROW_ENABLE_HOLDER_H)
            holder._checked = false

            local PRESS_TEXT_OFFSET_X = 0.30
            local PRESS_TEXT_OFFSET_Y = -0.30

            local function isDockRowHost(selfBtn)
                return isUsingCustomSettingsWindowVisualStyle(selfBtn)
            end

            local function setDockRowNativeOffset(selfBtn, offsetX, offsetY)
                if not selfBtn or not selfBtn.SetPushedTextOffset then
                    return
                end
                local scale = selfBtn.GetEffectiveScale and selfBtn:GetEffectiveScale() or 1
                if scale == 0 then
                    scale = 1
                end
                local ox = tonumber(offsetX) or 0
                local oy = tonumber(offsetY) or 0
                if math.abs(ox) < 0.5 and ox ~= 0 then
                    ox = (ox < 0) and -0.5 or 0.5
                end
                if math.abs(oy) < 0.5 and oy ~= 0 then
                    oy = (oy < 0) and -0.5 or 0.5
                end
                ox = ox / scale
                oy = oy / scale
                selfBtn:SetPushedTextOffset(ox, oy)
            end

            local function clearDockRowNativeOffset(selfBtn)
                if isDockRowHost(selfBtn) then
                    setDockRowNativeOffset(selfBtn, 0, 0)
                end
            end

            local function createToggleButton(text)
                local btn = createActionButton(holder, text, nil, FIXED_FOOTPRINTS.ROW_ENABLE_BUTTON_W, {
                    textOrigin = "choice_caption",
                    searchChoiceLabelMode = "suppress_generic",
                })
                if btn.Text then
                    applyGlobalFontToObject(btn.Text, 10)
                end
                btn._hovered = false
                btn._selected = false
                btn._kind = "off"

                local function ensureFlatSkin(selfBtn)
                    if not selfBtn then
                        return
                    end
                    if not selfBtn._paSkinReady then
                        selfBtn._paSkinReady = true
                        selfBtn._paBg = selfBtn:CreateTexture(nil, "ARTWORK")
                        selfBtn._paBg:SetAllPoints(selfBtn)
                        selfBtn._paBg:SetTexture("Interface\\Buttons\\WHITE8x8")
                        selfBtn._paBorder = CreateFrame("Frame", nil, selfBtn, "BackdropTemplate")
                        selfBtn._paBorder:SetPoint("TOPLEFT", selfBtn, "TOPLEFT", -1, 1)
                        selfBtn._paBorder:SetPoint("BOTTOMRIGHT", selfBtn, "BOTTOMRIGHT", 1, -1)
                        selfBtn._paBorder:SetBackdrop({
                            edgeFile = "Interface\\Buttons\\WHITE8x8",
                            edgeSize = 1,
                        })
                        selfBtn._paBorder:SetBackdropBorderColor(CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A)
                        setSegmentBorderColor(selfBtn, CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A)
                        selfBtn._paTopSheen = selfBtn:CreateTexture(nil, "OVERLAY")
                        selfBtn._paTopSheen:SetAllPoints(selfBtn)
                        selfBtn._paTopSheen:SetTexture("Interface\\Buttons\\WHITE8x8")
                        if selfBtn._paTopSheen.SetGradientAlpha then
                            selfBtn._paTopSheen:SetGradientAlpha("VERTICAL", 1, 1, 1, 0.12, 1, 1, 1, 0.0)
                        elseif selfBtn._paTopSheen.SetGradient and CreateColor then
                            selfBtn._paTopSheen:SetGradient("VERTICAL", CreateColor(1, 1, 1, 0.12), CreateColor(1, 1, 1, 0.0))
                        else
                            selfBtn._paTopSheen:SetVertexColor(1, 1, 1, 0.1)
                        end
                        selfBtn._paGlow = selfBtn:CreateTexture(nil, "BORDER")
                        selfBtn._paGlow:SetAllPoints(selfBtn)
                        selfBtn._paGlow:SetTexture("Interface\\Buttons\\WHITE8x8")
                        selfBtn._paGlow:SetBlendMode("ADD")
                    end

                    local n = selfBtn.GetNormalTexture and selfBtn:GetNormalTexture() or nil
                    local p = selfBtn.GetPushedTexture and selfBtn:GetPushedTexture() or nil
                    local h = selfBtn.GetHighlightTexture and selfBtn:GetHighlightTexture() or nil
                    local d = selfBtn.GetDisabledTexture and selfBtn:GetDisabledTexture() or nil
                    if n then n:SetAlpha(0); if n.Hide then n:Hide() end end
                    if p then p:SetAlpha(0); if p.Hide then p:Hide() end end
                    if h then h:SetAlpha(0); if h.Hide then h:Hide() end end
                    if d then d:SetAlpha(0); if d.Hide then d:Hide() end end

                    local skinParts = {
                        "Left", "Middle", "Right",
                        "LeftDisabled", "MiddleDisabled", "RightDisabled",
                        "LeftHighlight", "MiddleHighlight", "RightHighlight",
                        "LeftPressed", "MiddlePressed", "RightPressed",
                    }
                    for _, part in ipairs(skinParts) do
                        local tex = selfBtn[part]
                        if tex and tex.SetAlpha then
                            tex:SetAlpha(0)
                        end
                        if tex and tex.Hide then
                            tex:Hide()
                        end
                    end

                    if selfBtn._paBorder then
                        local baseLevel = selfBtn:GetFrameLevel() or 0
                        selfBtn._paBorder:SetFrameStrata(selfBtn:GetFrameStrata())
                        selfBtn._paBorder:SetFrameLevel(baseLevel + 3)
                    end
                end

                function btn:ApplyVisual()
                    ensureFlatSkin(self)
                    local useHostToggleTheme = isUsingCustomSettingsWindowVisualStyle(self)
                    local enabled = self._disabled ~= true
                    if useHostToggleTheme then
                        local bgColor = self._hovered and SETTINGS_WINDOW_HOST_COLORS.hoverBg or SETTINGS_WINDOW_HOST_COLORS.idleBg
                        local borderColor = self._hovered and SETTINGS_WINDOW_HOST_COLORS.hoverBorder or SETTINGS_WINDOW_HOST_COLORS.idleBorder
                        local textColor = self._hovered and SETTINGS_WINDOW_HOST_COLORS.hoverText or SETTINGS_WINDOW_HOST_COLORS.idleText

                        if not enabled then
                            bgColor = SETTINGS_WINDOW_HOST_COLORS.disabledBg
                            borderColor = SETTINGS_WINDOW_HOST_COLORS.disabledBorder
                            textColor = SETTINGS_WINDOW_HOST_COLORS.disabledText
                            if self._paGlow then
                                self._paGlow:SetVertexColor(1, 1, 1, 0)
                            end
                            if self._paTopSheen then
                                self._paTopSheen:SetAlpha(0.05)
                            end
                            self:SetAlpha(0.58)
                        elseif self._selected then
                            if self._kind == "on" then
                                bgColor = SETTINGS_WINDOW_HOST_COLORS.onBg
                                borderColor = SETTINGS_WINDOW_HOST_COLORS.onBorder
                                textColor = SETTINGS_WINDOW_HOST_COLORS.onText
                                if self._paGlow then
                                    self._paGlow:SetVertexColor(unpackColor(SETTINGS_WINDOW_HOST_COLORS.onBorder))
                                    self._paGlow:SetAlpha(self._hovered and 0.16 or 0.10)
                                end
                            else
                                bgColor = SETTINGS_WINDOW_HOST_COLORS.offBg
                                borderColor = SETTINGS_WINDOW_HOST_COLORS.offBorder
                                textColor = SETTINGS_WINDOW_HOST_COLORS.offText
                                if self._paGlow then
                                    self._paGlow:SetVertexColor(unpackColor(SETTINGS_WINDOW_HOST_COLORS.offBorder))
                                    self._paGlow:SetAlpha(self._hovered and 0.12 or 0.08)
                                end
                            end
                            if self._paTopSheen then
                                self._paTopSheen:SetAlpha(self._hovered and 0.18 or 0.12)
                            end
                            self:SetAlpha(1)
                        else
                            if self._paGlow then
                                self._paGlow:SetVertexColor(1, 1, 1, 0)
                            end
                            if self._paTopSheen then
                                self._paTopSheen:SetAlpha(self._hovered and 0.12 or 0.08)
                            end
                            self:SetAlpha(0.98)
                        end

                        self._paBg:SetVertexColor(unpackColor(bgColor))
                        if self._paBorder then
                            setSegmentBorderColor(self, unpackColor(borderColor))
                        end
                        if self.Text then
                            self.Text:SetTextColor(unpackColor(textColor))
                        end
                        return
                    end

                    if not enabled then
                        self._paBg:SetVertexColor(CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A)
                        if self._paGlow then
                            self._paGlow:SetVertexColor(1, 1, 1, 0)
                        end
                        if self._paBorder then
                            setSegmentBorderColor(self, CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, 0.72)
                        end
                        if self._paTopSheen then
                            self._paTopSheen:SetAlpha(0.04)
                        end
                        if self.Text then
                            self.Text:SetTextColor(0.50, 0.50, 0.53, 1)
                        end
                        self:SetAlpha(0.45)
                        return
                    end

                    if self._selected then
                        if self._kind == "on" then
                            if self._hovered then
                                self._paBg:SetVertexColor(0.13, 0.28, 0.15, 0.98)
                            else
                                self._paBg:SetVertexColor(0.11, 0.23, 0.13, 0.96)
                            end
                            if self._paGlow then
                                self._paGlow:SetVertexColor(0.28, 1.0, 0.36, self._hovered and 0.14 or 0.1)
                            end
                            if self._paBorder then
                                setSegmentBorderColor(self, 0.18, 0.55, 0.2, self._hovered and 0.95 or 0.82)
                            end
                            if self.Text then
                                self.Text:SetTextColor(0.72, 1, 0.74, 1)
                            end
                        else
                            self._paBg:SetVertexColor(OFF_RED_BG_R, OFF_RED_BG_G, OFF_RED_BG_B, OFF_RED_BG_A)
                            if self._paGlow then
                                self._paGlow:SetVertexColor(0.95, 0.22, 0.22, 0)
                            end
                            if self._paBorder then
                                setSegmentBorderColor(
                                    self,
                                    OFF_RED_BORDER_SELECTED_R,
                                    OFF_RED_BORDER_SELECTED_G,
                                    OFF_RED_BORDER_SELECTED_B,
                                    OFF_RED_BORDER_SELECTED_A
                                )
                            end
                            if self.Text then
                                self.Text:SetTextColor(OFF_RED_TEXT_R, OFF_RED_TEXT_G, OFF_RED_TEXT_B, OFF_RED_TEXT_A)
                            end
                        end
                        if self._paTopSheen then
                            if self._kind == "on" then
                                self._paTopSheen:SetAlpha(0.22)
                            else
                                self._paTopSheen:SetAlpha(OFF_RED_TOPSHEEN_ALPHA)
                            end
                        end
                        if self._kind == "on" then
                            self:SetAlpha(0.98)
                        else
                            self:SetAlpha(OFF_RED_SELECTED_ALPHA)
                        end
                    else
                        if self._hovered then
                            self._paBg:SetVertexColor(CONTROL_HOVER_BG_R, CONTROL_HOVER_BG_G, CONTROL_HOVER_BG_B, CONTROL_HOVER_BG_A)
                            if self._paBorder then
                                setSegmentBorderColor(self, CONTROL_HOVER_BORDER_R, CONTROL_HOVER_BORDER_G, CONTROL_HOVER_BORDER_B, CONTROL_HOVER_BORDER_A)
                            end
                        else
                            self._paBg:SetVertexColor(CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A)
                            if self._paBorder then
                                setSegmentBorderColor(self, CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A)
                            end
                        end
                        if self._paGlow then
                            self._paGlow:SetVertexColor(1, 1, 1, 0)
                        end
                        if self._paTopSheen then
                            self._paTopSheen:SetAlpha(self._hovered and 0.12 or 0.08)
                        end
                        if self.Text then
                            self.Text:SetTextColor(0.85, 0.85, 0.88, 1)
                        end
                        self:SetAlpha(0.96)
                    end
                end

                btn:SetScript("OnEnter", function(selfBtn)
                    selfBtn._hovered = true
                    selfBtn:ApplyVisual()
                end)
                btn:SetScript("OnLeave", function(selfBtn)
                    selfBtn._hovered = false
                    clearDockRowNativeOffset(selfBtn)
                    selfBtn:ApplyVisual()
                end)
                btn:HookScript("OnShow", function(selfBtn)
                    clearDockRowNativeOffset(selfBtn)
                end)
                btn:SetScript("OnMouseDown", function(selfBtn)
                    if selfBtn._disabled then
                        return
                    end
                    if not isDockRowHost(selfBtn) then
                        return
                    end
                    setDockRowNativeOffset(selfBtn, PRESS_TEXT_OFFSET_X, PRESS_TEXT_OFFSET_Y)
                end)
                btn:SetScript("OnMouseUp", function(selfBtn)
                    clearDockRowNativeOffset(selfBtn)
                end)
                function btn:SetEnabled(enabled)
                    local allow = enabled and true or false
                    self._disabled = not allow
                    self._hovered = false
                    self._pressed = false
                    clearDockRowNativeOffset(self)
                    self:EnableMouse(allow)
                    self:ApplyVisual()
                end
                btn:ApplyVisual()
                if isDockRowHost(btn) then
                    setDockRowNativeOffset(btn, 0, 0)
                end
                return btn
            end

            holder.disabledBtn = createToggleButton("Disabled")
            holder.disabledBtn._kind = "off"
            holder.enabledBtn = createToggleButton("Enabled")
            holder.enabledBtn._kind = "on"

            holder.disabledBtn:SetPoint("TOPLEFT", holder, "TOPLEFT", 0, 0)
            holder.enabledBtn:SetPoint("TOPRIGHT", holder, "TOPRIGHT", 0, 0)
            holder.disabledBtn:SetSize(FIXED_FOOTPRINTS.ROW_ENABLE_BUTTON_W, FIXED_FOOTPRINTS.ROW_ENABLE_BUTTON_H)
            holder.enabledBtn:SetSize(FIXED_FOOTPRINTS.ROW_ENABLE_BUTTON_W, FIXED_FOOTPRINTS.ROW_ENABLE_BUTTON_H)

            function holder:SetChecked(v)
                self._checked = PA_SafeBool(v)
                self.disabledBtn._selected = not self._checked
                self.enabledBtn._selected = self._checked
                self.disabledBtn:ApplyVisual()
                self.enabledBtn:ApplyVisual()
            end

            function holder:GetChecked()
                return self._checked
            end

            function holder:SetEnabled(enabled)
                local allow = enabled and true or false
                self._enabled = allow
                if self.disabledBtn and self.disabledBtn.SetEnabled then
                    self.disabledBtn:SetEnabled(allow)
                end
                if self.enabledBtn and self.enabledBtn.SetEnabled then
                    self.enabledBtn:SetEnabled(allow)
                end
                self:SetAlpha(allow and 1 or 0.55)
            end

            holder.disabledBtn:SetScript("OnClick", function()
                local slot = getDockSlot(slotIndex)
                if not slot.enabled then
                    return
                end
                slot.enabled = false
                setLoadoutPresetCustom()
                onUIChanged(false)
            end)
            holder.enabledBtn:SetScript("OnClick", function()
                local slot = getDockSlot(slotIndex)
                if slot.enabled then
                    return
                end
                slot.enabled = true
                setLoadoutPresetCustom()
                onUIChanged(false)
            end)

            holder:SetChecked(false)
            holder:SetEnabled(true)
            registerSettingsWindowVisualRefresh(holder, function(selfHolder)
                if selfHolder.disabledBtn and selfHolder.disabledBtn.ApplyVisual then
                    selfHolder.disabledBtn:ApplyVisual()
                end
                if selfHolder.enabledBtn and selfHolder.enabledBtn.ApplyVisual then
                    selfHolder.enabledBtn:ApplyVisual()
                end
            end)
            return holder
        end

        for i = 1, 10 do
            local slotIndex = i
            local row = { index = slotIndex }
            row.enabled = createRowEnableToggle(scrollChild, slotIndex)
            row.status = createStyledFontString(scrollChild, "ARTWORK", "GameFontHighlightSmall", 11)
            row.status:SetJustifyH("LEFT")
            row.status:SetText("")
            row.status:Hide()
            row.namePill = CreateFrame("Button", nil, scrollChild, "BackdropTemplate")
            ApplyPillStyle_Editable(row.namePill)
            row.namePill.label = createStyledFontString(row.namePill, "ARTWORK", "GameFontHighlightSmall", 11)
            row.namePill.label:SetPoint("LEFT", row.namePill, "LEFT", 8, 0)
            row.namePill.label:SetPoint("RIGHT", row.namePill, "RIGHT", -20, 0)
            row.namePill.label:SetJustifyH("LEFT")
            row.namePill.label:SetWordWrap(false)
            local function openNameEditor()
                if widgets.renamePopover and widgets.renamePopover.OpenForRow then
                    widgets.renamePopover:OpenForRow(slotIndex, row)
                end
            end
            row.namePill:SetScript("OnClick", openNameEditor)
            row.nameIBeam = CreateFrame("EditBox", nil, row.namePill)
            row.nameIBeam:SetAllPoints(row.namePill)
            row.nameIBeam:SetAutoFocus(false)
            row.nameIBeam:EnableMouse(true)
            if row.nameIBeam.SetMouseClickEnabled then
                row.nameIBeam:SetMouseClickEnabled(true)
            end
            if row.nameIBeam.SetMouseMotionEnabled then
                row.nameIBeam:SetMouseMotionEnabled(true)
            end
            if row.nameIBeam.EnableKeyboard then
                row.nameIBeam:EnableKeyboard(false)
            end
            row.nameIBeam:SetFrameLevel((row.namePill:GetFrameLevel() or 1) + 5)
            row.nameIBeam:SetText(" ")
            row.nameIBeam:SetTextInsets(0, 0, 0, 0)
            row.nameIBeam:SetAlpha(0)
            row.nameIBeam:SetScript("OnEditFocusGained", nil)
            row.nameIBeam:SetScript("OnEnterPressed", nil)
            row.nameIBeam:SetScript("OnEscapePressed", nil)
            row.nameIBeam:SetScript("OnMouseUp", function()
                openNameEditor()
            end)
            row.nameIBeam:SetScript("OnEnter", function()
                if row.namePill and row.namePill.SetPillState and not row.namePill._disabled then
                    row.namePill:SetPillState(row.namePill._focused and "focus" or "hover")
                end
            end)
            row.nameIBeam:SetScript("OnLeave", function()
                if row.namePill and row.namePill.SetPillState then
                    row.namePill:SetPillState(row.namePill._focused and "focus" or "normal")
                end
            end)
            row.spellIDPill = CreateFrame("Button", nil, scrollChild, "BackdropTemplate")
            row.spellIDPill:SetBackdrop({
                bgFile = "Interface\\Buttons\\WHITE8x8",
                edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
                edgeSize = 8,
                insets = { left = 1, right = 1, top = 1, bottom = 1 },
            })
            row.spellIDPill:SetBackdropColor(CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A)
            row.spellIDPill:SetBackdropBorderColor(CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A)
            row.spellIDPill.hover = row.spellIDPill:CreateTexture(nil, "HIGHLIGHT")
            row.spellIDPill.hover:SetAllPoints()
            row.spellIDPill.hover:SetTexture("Interface\\Buttons\\WHITE8x8")
            row.spellIDPill.hover:SetVertexColor(1, 1, 1, 0.06)
            row.spellIDPill.label = createStyledFontString(row.spellIDPill, "ARTWORK", "GameFontHighlightSmall", 11)
            row.spellIDPill.label:SetPoint("LEFT", row.spellIDPill, "LEFT", 8, 0)
            row.spellIDPill.label:SetPoint("RIGHT", row.spellIDPill, "RIGHT", -8, 0)
            row.spellIDPill.label:SetJustifyH("LEFT")
            row.spellIDPill.label:SetWordWrap(false)
            row.spellIDPill:SetScript("OnClick", function()
                if widgets.spellIDPopover and widgets.spellIDPopover.OpenForRow then
                    widgets.spellIDPopover:OpenForRow(slotIndex, row)
                end
            end)
            row.destination = CreateFrame("Button", nil, scrollChild, "BackdropTemplate")
            row.destination:SetHeight(FIXED_FOOTPRINTS.STANDARD_CONTROL_H)
            ApplyPillStyle_Dropdown(row.destination)
            row.destination.label = createStyledFontString(row.destination, "ARTWORK", "GameFontHighlightSmall", 11)
            row.destination.label:SetPoint("LEFT", row.destination, "LEFT", 8, 0)
            row.destination.label:SetPoint("RIGHT", row.destination, "RIGHT", -18, 0)
            row.destination.label:SetJustifyH("LEFT")
            row.destination.label:SetWordWrap(false)
            row.destination.chevron = createStyledFontString(row.destination, "ARTWORK", "GameFontHighlightSmall", 10)
            row.destination.chevron:SetPoint("RIGHT", row.destination, "RIGHT", -7, 0)
            row.destination.chevron:SetText("v")
            row.destination:SetScript("OnMouseDown", function(selfButton)
                selfButton._pressed = true
                selfButton:UpdateVisual()
            end)
            row.destination:SetScript("OnMouseUp", function(selfButton)
                selfButton._pressed = false
                selfButton:UpdateVisual()
            end)
            row.destination:UpdateVisual()
            row.destination:SetScript("OnClick", function(selfButton)
                if not selfButton:IsEnabled() then
                    return
                end
                if widgets.destinationPicker and widgets.destinationPicker.IsShown and widgets.destinationPicker:IsShown() and widgets.destinationPicker._ownerButton == selfButton then
                    widgets.destinationPicker:Close()
                    return
                end
                local slot = getDockSlot(slotIndex)
                local destinationChoices = getDestinationChoices()
                if widgets.destinationPicker._sourceOptionsRef ~= destinationChoices then
                    widgets.destinationPicker:SetOptions(destinationChoices)
                    widgets.destinationPicker._sourceOptionsRef = destinationChoices
                end
                widgets.destinationPicker:SetSelectedValue(slot.selection)
                widgets.destinationPicker:SetOnSelect(function(value, option, isClear)
                    if isClear then
                        slot.selection = "CUSTOM"
                        slot.spellID = 0
                        setDockSlotDefaultName(slot, 0)
                    elseif value == "CUSTOM" then
                        slot.selection = "CUSTOM"
                        setDockSlotDefaultName(slot, slot.spellID)
                    else
                        local spellID = math.floor(tonumber(value) or 0)
                        if spellID > 0 then
                            slot.selection = tostring(spellID)
                            slot.spellID = spellID
                            setDockSlotDefaultName(slot, spellID)
                        else
                            slot.selection = "CUSTOM"
                            slot.spellID = 0
                            setDockSlotDefaultName(slot, 0)
                        end
                    end
                    setLoadoutPresetCustom()
                    onUIChanged(false)
                end)
                if widgets.destinationPicker then
                    widgets.destinationPicker._ownerButton = selfButton
                end
                if selfButton.SetPillState then
                    selfButton:SetPillState("open")
                else
                    selfButton._open = true
                    selfButton:UpdateVisual()
                end
                widgets.destinationPicker:Open(selfButton)
            end)

            row.result = CreateFrame("Button", nil, scrollChild, "BackdropTemplate")
            row.result:SetBackdrop({
                bgFile = "Interface\\Buttons\\WHITE8x8",
                edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
                edgeSize = 10,
                insets = { left = 2, right = 2, top = 2, bottom = 2 },
            })
            row.result:SetBackdropColor(0, 0, 0, 0.45)
            row.result:SetBackdropBorderColor(0.25, 0.25, 0.25, 1)
            row.result.icon = row.result:CreateTexture(nil, "ARTWORK")
            row.result.icon:SetAllPoints()
            row.result:SetScript("OnEnter", function()
                local slot = getDockSlot(slotIndex)
                if slot.spellID <= 0 then
                    return
                end
                GameTooltip:SetOwner(row.result, "ANCHOR_RIGHT")
                if GameTooltip.SetSpellByID then
                    GameTooltip:SetSpellByID(slot.spellID)
                else
                    GameTooltip:SetText(resolveSlotName(slot.spellID, "Unknown"))
                end
                GameTooltip:Show()
            end)
            row.result:SetScript("OnLeave", function()
                GameTooltip:Hide()
            end)
            registerDockSlotRowSearch(row)
            slotRows[slotIndex] = row
        end

        local function refreshSwatch()
            local c = PortalAuthorityDB.dockFontColor
            widgets.color.swatch:SetBackdropColor(c.r, c.g, c.b, c.a)
        end
        widgets.color.swatch:SetScript("OnClick", function()
            openColorPicker(function()
                refreshSwatch()
                ApplyDockAppearanceSettings()
            end)
        end)

        local function syncCheckboxes()
            if widgets.lockDock and widgets.lockDock.Refresh then widgets.lockDock:Refresh() end
            if refreshLockDockVisualText then refreshLockDockVisualText() end
            if widgets.loadoutPresetSegment and widgets.loadoutPresetSegment.Refresh then widgets.loadoutPresetSegment:Refresh() end
            if widgets.dockEnabled and widgets.dockEnabled.Refresh then widgets.dockEnabled:Refresh() end
            if widgets.hideNames and widgets.hideNames.Refresh then widgets.hideNames:Refresh() end
            if widgets.gizmoMode and widgets.gizmoMode.Refresh then widgets.gizmoMode:Refresh() end
            if applyDockGizmoVisibilityOverrideState then applyDockGizmoVisibilityOverrideState() end
        end

        local function setDockObjectVisible(obj, visible)
            if not obj then
                return
            end
            if visible then
                if obj.Show then
                    obj:Show()
                end
            else
                if obj.Hide then
                    obj:Hide()
                end
            end
        end

        local function placeControl(spec, xLabel, xControl, y, controlW, anchorFrame)
            local anchorParent = anchorFrame or scrollChild
            local placement = PAGE.CONTROL_PLACEMENT
            setDockObjectVisible(spec.label, true)
            setDockObjectVisible(spec.control, true)
            spec.label:ClearAllPoints()
            spec.label:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", xLabel, y)
            if spec.kind == "dropdown" then
                spec.control:ClearAllPoints()
                spec.control:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", xControl + placement.DROPDOWN_X_SHIFT, y + placement.DROPDOWN_Y_SHIFT)
                UIDropDownMenu_SetWidth(spec.control, controlW)
            elseif spec.kind == "font_picker" then
                spec.control:ClearAllPoints()
                spec.control:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", xControl, y + placement.COMPACT_CONTROL_Y_SHIFT)
                if spec.SetWidth then
                    spec:SetWidth(controlW)
                else
                    spec.control:SetWidth(controlW)
                end
            elseif spec.kind == "premium_slider" then
                spec.control:ClearAllPoints()
                spec.control:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", xControl, y + placement.COMPACT_CONTROL_Y_SHIFT)
                if spec.SetWidth then
                    spec:SetWidth(controlW)
                else
                    spec.control:SetWidth(controlW)
                end
            elseif spec.kind == "stepper" then
                spec.control:ClearAllPoints()
                spec.control:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", xControl, y + placement.COMPACT_CONTROL_Y_SHIFT)
                if spec.SetWidth then
                    spec:SetWidth(controlW)
                else
                    spec.control:SetWidth(controlW)
                end
            elseif spec.kind == "segmented" then
                spec.control:ClearAllPoints()
                spec.control:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", xControl, y + placement.COMPACT_CONTROL_Y_SHIFT)
                if spec.Layout then
                    spec:Layout(controlW)
                else
                    spec.control:SetWidth(controlW)
                    spec.control:SetHeight(FIXED_FOOTPRINTS.STANDARD_CONTROL_H)
                end
            elseif spec.kind == "slider" then
                spec.control:ClearAllPoints()
                spec.control:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", xControl, y + placement.SLIDER_Y_SHIFT)
                spec.control:SetWidth(controlW)
                if spec.valueText then
                    setDockObjectVisible(spec.valueText, true)
                    spec.valueText:ClearAllPoints()
                    spec.valueText:SetPoint("LEFT", spec.control, "RIGHT", placement.VALUE_TEXT_GAP, 0)
                end
            else
                spec.control:ClearAllPoints()
                spec.control:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", xControl, y)
                spec.control:SetSize(controlW, FIXED_FOOTPRINTS.STANDARD_CONTROL_H)
            end
        end
        local function setSlotRowEnabledVisuals(row, slotEnabled, moduleEnabled)
            local allowModuleInteraction = moduleEnabled and true or false
            local allowSlotInteraction = allowModuleInteraction and (slotEnabled and true or false)

            setSettingsControlEnabled(row.enabled, allowModuleInteraction)

            if not allowSlotInteraction
                and widgets.destinationPicker
                and widgets.destinationPicker.Close
                and widgets.destinationPicker.IsShown
                and widgets.destinationPicker:IsShown()
                and widgets.destinationPicker._ownerButton == row.destination then
                widgets.destinationPicker:Close()
            end

            row.destination:SetEnabled(allowSlotInteraction)
            row.destination:SetAlpha(allowSlotInteraction and 1 or 0.86)
            if not allowSlotInteraction then
                if row.destination.SetPillState then
                    row.destination:SetPillState("disabled")
                else
                    row.destination._disabled = true
                    row.destination._hovered = false
                    row.destination._pressed = false
                    if row.destination.UpdateVisual then
                        row.destination:UpdateVisual()
                    end
                end
            else
                if row.destination.SetPillState and row.destination._disabled then
                    row.destination:SetPillState(row.destination._open and "open" or "normal")
                elseif row.destination.UpdateVisual then
                    row.destination._disabled = false
                    row.destination:UpdateVisual()
                end
            end

            row.spellIDPill:SetEnabled(allowSlotInteraction)
            row.spellIDPill:SetAlpha(allowSlotInteraction and 1 or 0.75)
            row.spellIDPill.label:SetTextColor(1, 1, 1, allowSlotInteraction and 1 or 0.6)

            row.namePill:SetEnabled(allowSlotInteraction)
            row.namePill:SetAlpha(allowSlotInteraction and 1 or 0.86)
            if row.nameIBeam then
                row.nameIBeam:EnableMouse(allowSlotInteraction)
                if row.nameIBeam.SetEnabled then
                    row.nameIBeam:SetEnabled(allowSlotInteraction)
                end
                if row.nameIBeam.SetMouseClickEnabled then
                    row.nameIBeam:SetMouseClickEnabled(allowSlotInteraction)
                end
                if not allowSlotInteraction and row.nameIBeam.ClearFocus then
                    row.nameIBeam:ClearFocus()
                end
            end
            if not allowSlotInteraction then
                if row.namePill.SetPillState then
                    row.namePill:SetPillState("disabled")
                else
                    row.namePill._disabled = true
                    row.namePill._hovered = false
                    row.namePill._pressed = false
                    if row.namePill.UpdateVisual then
                        row.namePill:UpdateVisual()
                    end
                end
            else
                if row.namePill.SetPillState and row.namePill._disabled then
                    row.namePill:SetPillState(row.namePill._focused and "focus" or "normal")
                elseif row.namePill.UpdateVisual then
                    row.namePill._disabled = false
                    row.namePill:UpdateVisual()
                else
                    row.namePill.label:SetTextColor(1, 1, 1, 1)
                end
            end

            row.result:EnableMouse(allowSlotInteraction)
            row.result:SetAlpha(allowSlotInteraction and 1 or 0.6)
            row.result.icon:SetDesaturated(not allowSlotInteraction)

            row.status:SetAlpha(0)
        end

        local function refreshSlotRows()
            local moduleEnabled = isDockModuleEnabled()
            for i = 1, 10 do
                local row = slotRows[i]
                local slot = getDockSlot(i)

                row.enabled:SetChecked(slot.enabled)
                local selected = getDestinationOptionByValue(slot.selection)
                if slot.selection == "CUSTOM" then
                    if slot.spellID > 0 then
                        row.destination.label:SetText(string.format("Custom (%d)", getVisualDockIDForSpell(slot.spellID)))
                    else
                        row.destination.label:SetText("Custom")
                    end
                else
                    row.destination.label:SetText((selected and selected.buttonLabel) or ("Spell " .. tostring(slot.selection)))
                end

                local displayName = getDockSlotDisplayName(slot)
                row.namePill.label:SetText(displayName ~= "" and displayName or "-")
                row.spellIDPill.label:SetText(tostring(getVisualDockIDForSpell(slot.spellID or 0)))

                local texture = getSpellTextureByID(slot.spellID) or "Interface\\ICONS\\INV_Misc_QuestionMark"
                row.result.icon:SetTexture(texture)
                row.result.icon:SetTexCoord(0, 1, 0, 1)
                if slot.spellID > 0 and (texture == nil or texture == "Interface\\ICONS\\INV_Misc_QuestionMark") then
                    requestSpellDataRefresh(i, slot.spellID)
                end

                row.status:SetText("")

                setSlotRowEnabledVisuals(row, slot.enabled, moduleEnabled)
                if widgets.renamePopover and widgets.renamePopover:IsShown() and widgets.renamePopover.slotIndex == i and not slot.enabled then
                    widgets.renamePopover:Close(false)
                end
                if widgets.spellIDPopover and widgets.spellIDPopover:IsShown() and widgets.spellIDPopover.slotIndex == i and not slot.enabled then
                    widgets.spellIDPopover:Close(false)
                end
            end
        end

        applyDockModuleEnabledState = function()
            local moduleEnabled = isDockModuleEnabled()

            setSettingsControlEnabled(widgets.lockDock, moduleEnabled)
            setSettingsControlEnabled(widgets.resetDockAnchor, moduleEnabled)
            setSettingsControlEnabled(widgets.loadoutPresetSegment, moduleEnabled)
            setSettingsControlEnabled(widgets.hideNames, moduleEnabled)
            setSettingsControlEnabled(widgets.gizmoMode, moduleEnabled)
            setSettingsControlEnabled(widgets.layoutModeSegment, moduleEnabled)
            setSettingsControlEnabled(widgets.iconsPerRow, moduleEnabled)
            setSettingsControlEnabled(widgets.layoutIconSize, moduleEnabled)
            setSettingsControlEnabled(widgets.labelMode, moduleEnabled)
            setSettingsControlEnabled(widgets.labelSide, moduleEnabled)
            setSettingsControlEnabled(widgets.labelSideInside, moduleEnabled)
            setSettingsControlEnabled(widgets.font, moduleEnabled)
            setSettingsControlEnabled(widgets.fontSize, moduleEnabled)
            setSettingsControlEnabled(widgets.color and widgets.color.swatch or nil, moduleEnabled)
            setSettingsControlEnabled(widgets.outlineToggle, moduleEnabled)
            setSettingsControlEnabled(widgets.hoverGlowSegment, moduleEnabled)
            if applyDockGizmoVisibilityOverrideState then
                applyDockGizmoVisibilityOverrideState()
            end

            if widgets.lowerSectionsContainer and widgets.lowerSectionsContainer.SetAlpha then
                widgets.lowerSectionsContainer:SetAlpha(moduleEnabled and 1 or 0.55)
            end
            if widgets.loadoutPresetSegment and widgets.loadoutPresetSegment.label and widgets.loadoutPresetSegment.label.SetAlpha then
                widgets.loadoutPresetSegment.label:SetAlpha(moduleEnabled and 1 or 0.55)
            end
            if widgets.lockRowContainer and widgets.lockRowContainer.SetAlpha then
                widgets.lockRowContainer:SetAlpha(moduleEnabled and 1 or 0.55)
            end

            for _, textWidget in ipairs({
                widgets.lockStateHelper,
                widgets.dockCustomizationHelper,
                widgets.dockCustomizationMatchStatus,
                widgets.enableLabel,
                widgets.layoutSectionHeader,
                widgets.labelsSectionHeader,
                widgets.styleSectionHeader,
                widgets.layoutSpacingHint,
                widgets.iconsPerRowGridOnlyHint,
                widgets.slotColDestination,
                widgets.slotColName,
                widgets.slotColSpellID,
                widgets.slotColResult,
            }) do
                setSettingsTextEnabled(textWidget, moduleEnabled)
            end

            if widgets.color and widgets.color.inlineLabel then
                setSettingsTextEnabled(widgets.color.inlineLabel, moduleEnabled)
            end

            if not moduleEnabled then
                if widgets.destinationPicker and widgets.destinationPicker.Close then
                    widgets.destinationPicker:Close()
                end
                if widgets.renamePopover and widgets.renamePopover.Close then
                    widgets.renamePopover:Close(false)
                end
                if widgets.spellIDPopover and widgets.spellIDPopover.Close then
                    widgets.spellIDPopover:Close(false)
                end
            end
        end

        relayout = function()
            syncCheckboxes()
            forceDockFontUiState()
            forceDockFontSizeUiState()
            forceStyleToggleUiState()
            do
                local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
                local resolvedLayoutMode = getDockSimpleLayoutModeForSettings(defaults)
                if widgets.layoutModeSegment and widgets.layoutModeSegment.SetVisualSelectedValue then
                    widgets.layoutModeSegment:SetVisualSelectedValue(resolvedLayoutMode)
                end
                forceDockLabelUiState()
            end
            refreshSwatch()
            refreshSlotRows()

            local viewportW = scrollFrame:GetWidth()
            local contentW = math.max(PAGE.MIN_CONTENT_W, viewportW - PAGE.SCROLL_WIDTH_FUDGE)
            local left, right = PAGE.LEFT, PAGE.RIGHT
            local narrow = contentW < PAGE.NARROW_BREAKPOINT
            local ROW_H, ROW_GAP = PAGE.ROW_H, PAGE.ROW_GAP
            local ROW_STEP = ROW_H + ROW_GAP

            local pairContent = contentW - left - right
            local colGap = narrow and 0 or PAGE.WIDE_COLUMN_GAP
            local colW = narrow and pairContent or math.floor((pairContent - colGap) / 2)
            local CHECK_X = PAGE.CHECK_LEFT - left
            local LOADOUT_ROW_SHIFT_Y = PRESET_AND_TABLE.LOADOUT_ROW_SHIFT_Y -- single knob: positive moves the Midnight Loadout row down
            local TABLE_BLOCK_SHIFT_Y = PRESET_AND_TABLE.TABLE_BLOCK_SHIFT_Y -- single knob: positive moves the entire destination table down (preset row is unaffected)
            local LOWER_SECTIONS_BLOCK_OFFSET_Y = LOWER_SECTIONS.BLOCK_OFFSET_Y -- single knob: positive moves divider above Layout + everything below up
            local DESTINATION_COLUMN_Y_SHIFT = PRESET_AND_TABLE.DESTINATION_COLUMN_Y_SHIFT -- single knob: positive moves the entire Destination column up
            local NAME_SPELL_COLUMNS_Y_SHIFT = PRESET_AND_TABLE.NAME_SPELL_COLUMNS_Y_SHIFT -- single knob: positive moves Name + Spell ID columns up together
            local RESULT_COLUMN_Y_SHIFT = PRESET_AND_TABLE.RESULT_COLUMN_Y_SHIFT -- single knob: positive moves the entire Result column up
            local STATUS_TEXT_Y_SHIFT = PRESET_AND_TABLE.STATUS_TEXT_Y_SHIFT -- single knob: positive moves ENABLED/DISABLED status text up
            local SLOT_ENABLE_CHECKBOX_Y_SHIFT = PRESET_AND_TABLE.SLOT_ENABLE_CHECKBOX_Y_SHIFT -- single knob: positive moves all slot-row enable checkboxes up
            local SLOT_HEADER_TEXT_X_SHIFT = PRESET_AND_TABLE.SLOT_HEADER_TEXT_X_SHIFT -- single knob: positive moves Destination/Name/Spell ID header text right
            local VISIBILITY_BLOCK_SHIFT_Y = LOWER_SECTIONS.VISIBILITY_BLOCK_SHIFT_Y -- single knob: positive moves Visibility Rules + everything below it down (divider stays fixed)
            local VISIBILITY_LABELS_SHIFT_Y = LOWER_SECTIONS.VISIBILITY_LABELS_SHIFT_Y -- single knob: positive moves only visibility labels down (toggles stay fixed)
            local LABEL_X = PAGE.LABEL_X
            local CONTROL_X = PAGE.CONTROL_X
            local presetDividerReferenceGap = PRESET_AND_TABLE.PRESET_DIVIDER_FALLBACK_GAP

            local dropdownW = math.max(PAGE.DROPDOWN_MIN_W, math.min(PAGE.DROPDOWN_MAX_W, colW - CONTROL_X - PAGE.CONTROL_WIDTH_RIGHT_PAD))
            local numericW = math.max(PAGE.NUMERIC_MIN_W, math.min(PAGE.NUMERIC_MAX_W, colW - CONTROL_X - PAGE.CONTROL_WIDTH_RIGHT_PAD))
            local sliderW = math.max(PAGE.SLIDER_MIN_W, math.min(PAGE.SLIDER_MAX_W, colW - CONTROL_X - PAGE.SLIDER_WIDTH_RIGHT_PAD))

            local function columnBase(col)
                if col == 2 then
                    return left + colW + colGap
                end
                return left
            end

            local function pair(leftSpec, rightSpec, rowY, leftWidth, rightWidth)
                local leftBase = columnBase(1)
                if narrow then
                    placeControl(leftSpec, leftBase + LABEL_X, leftBase + CONTROL_X, rowY, leftWidth or dropdownW)
                    rowY = rowY - PAGE.PAIR_ROW_STEP
                    placeControl(rightSpec, leftBase + LABEL_X, leftBase + CONTROL_X, rowY, rightWidth or dropdownW)
                    return rowY - PAGE.PAIR_ROW_STEP
                end
                local rightBase = columnBase(2)
                placeControl(leftSpec, leftBase + LABEL_X, leftBase + CONTROL_X, rowY, leftWidth or dropdownW)
                placeControl(rightSpec, rightBase + LABEL_X, rightBase + CONTROL_X, rowY, rightWidth or dropdownW)
                return rowY - PAGE.PAIR_ROW_STEP
            end

            scrollChild:SetWidth(contentW)
            widgets.playerHeader:ClearAllPoints()
            widgets.playerHeader:SetPoint("TOPRIGHT", self, "TOPRIGHT", -PAGE.PLAYER_HEADER_PAD, -PAGE.PLAYER_HEADER_PAD)

            local leftBase = columnBase(1)
            local tableLeft = leftBase + CHECK_X
            local dividerLeftX = tableLeft
            local loadoutPresetW = math.max(
                PRESET_AND_TABLE.LOADOUT_PRESET_MIN_W,
                math.min(PRESET_AND_TABLE.LOADOUT_PRESET_MAX_W, contentW - tableLeft - right - PRESET_AND_TABLE.LOADOUT_RIGHT_PAD)
            )

            widgets.positionLabel:ClearAllPoints()
            setDockObjectVisible(widgets.positionLabel, false)
            widgets.positionLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)

            local lockRowLeft = tableLeft
            local lockLabelToToggleGap = TOP_BLOCK.LABEL_TO_TOGGLE_GAP
            local lockToggleToResetGap = TOP_BLOCK.TOGGLE_TO_RESET_GAP
            local resetW, resetH = FIXED_FOOTPRINTS.RESET_ANCHOR_W, FIXED_FOOTPRINTS.RESET_ANCHOR_H
            local lockLabel = widgets.lockDock and widgets.lockDock.label or nil
            local lockLabelW = 0
            local lockLabelH = 0
            if lockLabel then
                lockLabelW = math.ceil(lockLabel:GetStringWidth() or 0)
                lockLabelH = math.ceil(lockLabel:GetStringHeight() or 0)
            end
            local masterLabel = widgets.dockEnabled and widgets.dockEnabled.label or nil
            local masterLabelH = 0
            if masterLabel then
                masterLabelH = math.ceil(masterLabel:GetStringHeight() or 0)
            end
            local referenceLabelH = lockLabelH
            if referenceLabelH <= 0 then
                referenceLabelH = masterLabelH
            end
            if referenceLabelH <= 0 and widgets.enableLabel and widgets.enableLabel.GetStringHeight then
                referenceLabelH = widgets.enableLabel:GetStringHeight() or 0
            end
            local U = math.max(1, referenceLabelH)
            local spacingAB = U * TOP_BLOCK.HELPER_GAP_RATIO -- A -> B and C -> D
            local spacingBC = U * TOP_BLOCK.DIVIDER_GAP_RATIO -- B -> C
            local spacingDHeader = U * TOP_BLOCK.PRESET_HEADER_GAP_RATIO -- D -> Destination header
            local useHostedChrome = isPanelUsingCustomSettingsWindowHost(self)
            local lockBtnTextW = 0
            if widgets.lockDock and type(widgets.lockDock.buttons) == "table" then
                for _, btn in ipairs(widgets.lockDock.buttons) do
                    if btn and btn.Text then
                        lockBtnTextW = math.max(lockBtnTextW, math.ceil(btn.Text:GetStringWidth() or 0))
                    end
                end
            end
            lockBtnTextW = math.max(lockBtnTextW, TOP_BLOCK.LOCK_BUTTON_MIN_TEXT_W)
            local lockPerBtnPad = TOP_BLOCK.LOCK_BUTTON_PAD_X
            local lockToggleWFromContent = ((lockBtnTextW + (lockPerBtnPad * 2)) * 2) + TOP_BLOCK.LOCK_SEGMENT_INNER_GAP
            local lockToggleWMax = math.max(
                TOP_BLOCK.LOCK_TOGGLE_MIN_W,
                (contentW - TOP_BLOCK.CONTENT_RIGHT) - (lockRowLeft + lockLabelW + lockLabelToToggleGap + lockToggleToResetGap + resetW)
            )
            local lockToggleW = math.max(TOP_BLOCK.LOCK_TOGGLE_MIN_W, math.min(lockToggleWFromContent, lockToggleWMax))
            local sharedToggleX = (lockLabelW > 0) and (lockLabelW + lockLabelToToggleGap) or 0
            if widgets.dockEnabled and widgets.dockEnabled.Layout then
                widgets.dockEnabled:Layout(math.max(lockToggleW, 150))
            end
            local masterToggleH = (widgets.dockEnabled and widgets.dockEnabled.control and math.floor(widgets.dockEnabled.control:GetHeight() or FIXED_FOOTPRINTS.STANDARD_CONTROL_H)) or FIXED_FOOTPRINTS.STANDARD_CONTROL_H
            local masterRowH = math.max(TOP_BLOCK.LOCK_ROW_FLOOR, masterToggleH, masterLabelH)
            local masterRowGap = 4
            if widgets.lockDock and widgets.lockDock.Layout then
                widgets.lockDock:Layout(lockToggleW)
            end
            local lockToggleH = (widgets.lockDock and widgets.lockDock.control and math.floor(widgets.lockDock.control:GetHeight() or FIXED_FOOTPRINTS.STANDARD_CONTROL_H)) or FIXED_FOOTPRINTS.STANDARD_CONTROL_H
            local lockRowH = math.max(TOP_BLOCK.LOCK_ROW_FLOOR, resetH, lockToggleH, lockLabelH)
            local lockRowW = math.max(1, contentW - TOP_BLOCK.CONTENT_RIGHT - lockRowLeft)
            local lockHelperLineH = math.max(1, (widgets.lockStateHelper and widgets.lockStateHelper.GetStringHeight and widgets.lockStateHelper:GetStringHeight()) or U)
            local topBlockHelperGap = U * TOP_BLOCK.HELPER_GAP_RATIO
            local topBlockDividerGap = U * TOP_BLOCK.DIVIDER_GAP_RATIO
            local topBlockDividerOffset = topBlockDividerGap * TOP_BLOCK.DIVIDER_OFFSET_RATIO
            -- Preserve the accepted handoff below the top block; only the top-block rhythm changes.
            local hostedTopNudge = useHostedChrome and SETTINGS_WINDOW_HOSTED_TOP_RHYTHM_NUDGE or 0
            local y = (-TOP_BLOCK.TOP_PAD - hostedTopNudge)
                - (useHostedChrome and 0 or TOP_BLOCK.PRESERVED_HEADER_ADVANCE)
                - (useHostedChrome and 0 or (masterRowH + masterRowGap))
                - lockRowH
                - spacingAB
                - lockHelperLineH
                - spacingBC
            local topBlockY = -TOP_BLOCK.TOP_PAD - hostedTopNudge

            if useHostedChrome then
                setDockObjectVisible(widgets.header, false)
                widgets.header:ClearAllPoints()
                widgets.header:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                setDockObjectVisible(widgets.sectionDescriptor, false)
                widgets.sectionDescriptor:ClearAllPoints()
                widgets.sectionDescriptor:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                setDockObjectVisible(widgets.masterRowContainer, false)
                widgets.masterRowContainer:ClearAllPoints()
                widgets.masterRowContainer:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                if masterLabel then
                    setDockObjectVisible(masterLabel, false)
                    masterLabel:ClearAllPoints()
                    masterLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                end
                if widgets.dockEnabled and widgets.dockEnabled.control then
                    setDockObjectVisible(widgets.dockEnabled.control, false)
                    widgets.dockEnabled.control:ClearAllPoints()
                    widgets.dockEnabled.control:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                end
            else
                setDockObjectVisible(widgets.header, true)
                setDockObjectVisible(widgets.sectionDescriptor, true)
                widgets.header:ClearAllPoints()
                widgets.header:SetWidth(math.max(1, contentW - TOP_BLOCK.HEADER_LEFT - TOP_BLOCK.CONTENT_RIGHT))
                widgets.header:SetWordWrap(false)
                if widgets.header.SetMaxLines then
                    widgets.header:SetMaxLines(1)
                end
                if widgets.header.SetJustifyH then
                    widgets.header:SetJustifyH("LEFT")
                end
                local headerH = math.max(
                    TOP_BLOCK.HEADER_FLOOR,
                    math.ceil(tonumber((widgets.header.GetStringHeight and widgets.header:GetStringHeight()) or 0) or 0)
                )
                widgets.header:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", TOP_BLOCK.HEADER_LEFT, topBlockY)
                topBlockY = topBlockY - headerH - TOP_BLOCK.HEADER_BOTTOM_GAP
                local topBlockAfterHeaderY = topBlockY
                topBlockY = layoutSettingsSectionDescriptor(
                    widgets.sectionDescriptor,
                    scrollChild,
                    TOP_BLOCK.HEADER_LEFT,
                    topBlockY,
                    math.max(1, contentW - TOP_BLOCK.HEADER_LEFT - TOP_BLOCK.CONTENT_RIGHT)
                )
                y = y - (topBlockAfterHeaderY - topBlockY)

                setDockObjectVisible(widgets.masterRowContainer, true)
                widgets.masterRowContainer:ClearAllPoints()
                widgets.masterRowContainer:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", lockRowLeft, topBlockY + TOP_BLOCK.LOCK_STACK_NUDGE_Y)
                widgets.masterRowContainer:SetSize(lockRowW, masterRowH)
                if masterLabel then
                    setDockObjectVisible(masterLabel, true)
                    masterLabel:ClearAllPoints()
                    masterLabel:SetPoint("LEFT", widgets.masterRowContainer, "LEFT", 0, 0)
                end
                if widgets.dockEnabled and widgets.dockEnabled.control then
                    setDockObjectVisible(widgets.dockEnabled.control, true)
                    widgets.dockEnabled.control:ClearAllPoints()
                    widgets.dockEnabled.control:SetPoint("LEFT", widgets.masterRowContainer, "LEFT", sharedToggleX, 0)
                end
                topBlockY = topBlockY - masterRowH - masterRowGap
            end

            widgets.lockRowContainer:ClearAllPoints()
            widgets.lockRowContainer:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", lockRowLeft, topBlockY + TOP_BLOCK.LOCK_STACK_NUDGE_Y)
            widgets.lockRowContainer:SetSize(lockRowW, lockRowH)

            if lockLabel then
                lockLabel:ClearAllPoints()
                lockLabel:SetPoint("LEFT", widgets.lockRowContainer, "LEFT", 0, 0)
            end
            widgets.lockDock.control:ClearAllPoints()
            widgets.lockDock.control:SetPoint("LEFT", widgets.lockRowContainer, "LEFT", sharedToggleX, 0)
            widgets.resetDockAnchor:ClearAllPoints()
            widgets.resetDockAnchor:SetSize(resetW, resetH)
            widgets.resetDockAnchor:SetPoint("LEFT", widgets.lockDock.control, "RIGHT", lockToggleToResetGap, 0)

            topBlockY = topBlockY - lockRowH
            widgets.lockStateHelper:ClearAllPoints()
            if widgets.lockStateHelper.SetJustifyH then
                widgets.lockStateHelper:SetJustifyH("LEFT")
            end
            widgets.lockStateHelper:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", lockRowLeft, topBlockY - topBlockHelperGap + TOP_BLOCK.LOCK_STACK_NUDGE_Y)
            topBlockY = topBlockY - topBlockHelperGap - lockHelperLineH

            widgets.lockDockDivider:ClearAllPoints()
            local lockDockDividerY = topBlockY - topBlockDividerOffset + TOP_BLOCK.LOCK_STACK_NUDGE_Y
            widgets.lockDockDivider:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", dividerLeftX, lockDockDividerY)
            widgets.lockDockDivider:SetPoint("TOPRIGHT", scrollChild, "TOPRIGHT", -TOP_BLOCK.CONTENT_RIGHT, lockDockDividerY)
            widgets.lockDockDivider:SetHeight(FIXED_FOOTPRINTS.DIVIDER_H)
            topBlockY = topBlockY - topBlockDividerGap

            widgets.dockCustomizationLabel:ClearAllPoints()
            setDockObjectVisible(widgets.dockCustomizationLabel, false)
            widgets.dockCustomizationLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
            widgets.loadoutPresetSegment.label:ClearAllPoints()
            setDockObjectVisible(widgets.loadoutPresetSegment.label, false)
            widgets.loadoutPresetSegment.label:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
            widgets.loadoutPresetSegment.control:ClearAllPoints()
            setDockObjectVisible(widgets.loadoutPresetSegment.control, true)
            local presetRowTopY = y - LOADOUT_ROW_SHIFT_Y
            widgets.loadoutPresetSegment.control:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", tableLeft, presetRowTopY)
            presetDividerReferenceGap = math.max(0, math.floor((lockDockDividerY - presetRowTopY) + 0.5))
            if presetDividerReferenceGap <= 0 then
                presetDividerReferenceGap = PRESET_AND_TABLE.PRESET_DIVIDER_FALLBACK_GAP
            end
            if widgets.loadoutPresetSegment.Layout then
                widgets.loadoutPresetSegment:Layout(loadoutPresetW)
            end
            local presetRowH = math.max(
                1,
                (widgets.loadoutPresetSegment and widgets.loadoutPresetSegment.control and widgets.loadoutPresetSegment.control:GetHeight())
                or FIXED_FOOTPRINTS.STANDARD_CONTROL_H
            )
            widgets.dockCustomizationHelper:ClearAllPoints()
            widgets.dockCustomizationHelper:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", tableLeft, y - presetRowH - spacingAB - LOADOUT_ROW_SHIFT_Y)
            local presetHelperH = math.max(1, (widgets.dockCustomizationHelper and widgets.dockCustomizationHelper.GetStringHeight and widgets.dockCustomizationHelper:GetStringHeight()) or U)

            local matchStatusText = ""
            local showMatchStatus = false
            local matchStatusVariant = nil
            local activePreset = normalizeLoadoutPresetValue(PortalAuthorityDB and PortalAuthorityDB.dockLoadoutPreset)
            if activePreset == "CUSTOM" then
                local matchesSeason01, canonicalOrder = getCustomSeason01MatchState()
                if matchesSeason01 then
                    showMatchStatus = true
                    if canonicalOrder then
                        matchStatusVariant = "season01_exact"
                        matchStatusText = "• Matches Midnight Season 01"
                    else
                        matchStatusVariant = "season01_custom_order"
                        matchStatusText = "• Matches Midnight Season 01 (custom order)"
                    end
                end
            end

            local tableWidth = contentW - tableLeft - right - PRESET_AND_TABLE.LOADOUT_RIGHT_PAD
            local matchStatusGap = PRESET_AND_TABLE.MATCH_STATUS_GAP
            local matchStatusLaneH = math.max(
                1,
                math.floor(
                    (widgets.dockCustomizationHelper and widgets.dockCustomizationHelper.GetStringHeight and widgets.dockCustomizationHelper:GetStringHeight())
                    or U
                )
            )
            if widgets.dockCustomizationMatchStatus then
                local statusLabel = widgets.dockCustomizationMatchStatus
                local originalText = tostring(statusLabel:GetText() or "")
                if statusLabel.SetWidth then
                    statusLabel:SetWidth(math.max(1, tableWidth))
                end
                for _, candidateText in ipairs({
                    "",
                    "• Matches Midnight Season 01",
                    "• Matches Midnight Season 01 (custom order)",
                }) do
                    statusLabel:SetText(candidateText)
                    matchStatusLaneH = math.max(
                        matchStatusLaneH,
                        math.ceil(tonumber((statusLabel.GetStringHeight and statusLabel:GetStringHeight()) or 0) or 0)
                    )
                end
                statusLabel:SetText(originalText)

                local function setMatchStatusTone(variantKey, pulse)
                    if variantKey == "season01_exact" or variantKey == "season01_custom_order" then
                        if pulse then
                            statusLabel:SetTextColor(1.00, 0.90, 0.62, 1.00)
                        else
                            statusLabel:SetTextColor(0.90, 0.78, 0.52, 0.98)
                        end
                    else
                        statusLabel:SetTextColor(0.66, 0.66, 0.66, 0.94)
                    end
                end

                statusLabel:ClearAllPoints()
                statusLabel:SetPoint(
                    "TOPLEFT",
                    scrollChild,
                    "TOPLEFT",
                    tableLeft,
                    y - presetRowH - spacingAB - presetHelperH - matchStatusGap - LOADOUT_ROW_SHIFT_Y
                )
                if statusLabel.SetWidth then
                    statusLabel:SetWidth(math.max(1, tableWidth))
                end
                if showMatchStatus then
                    local shouldPulse = (widgets._dockMatchStatusVisible ~= true) or (widgets._dockMatchStatusVariant ~= matchStatusVariant)
                    statusLabel:SetText(matchStatusText)
                    statusLabel:Show()
                    widgets._dockMatchStatusVisible = true
                    widgets._dockMatchStatusVariant = matchStatusVariant
                    if shouldPulse then
                        local pulseToken = (widgets._dockMatchStatusPulseToken or 0) + 1
                        widgets._dockMatchStatusPulseToken = pulseToken
                        setMatchStatusTone(matchStatusVariant, true)
                        if C_Timer and C_Timer.After then
                            local settleVariant = matchStatusVariant
                            C_Timer.After(0.22, function()
                                if not widgets or not widgets.dockCustomizationMatchStatus then
                                    return
                                end
                                if widgets._dockMatchStatusPulseToken ~= pulseToken then
                                    return
                                end
                                if not widgets.dockCustomizationMatchStatus:IsShown() then
                                    return
                                end
                                setMatchStatusTone(settleVariant, false)
                            end)
                        else
                            setMatchStatusTone(matchStatusVariant, false)
                        end
                    else
                        setMatchStatusTone(matchStatusVariant, false)
                    end
                else
                    statusLabel:SetText("")
                    statusLabel:Hide()
                    widgets._dockMatchStatusVisible = false
                    widgets._dockMatchStatusVariant = nil
                    widgets._dockMatchStatusPulseToken = (widgets._dockMatchStatusPulseToken or 0) + 1
                    setMatchStatusTone(nil, false)
                end
            end

            -- Reserve a fixed match-status lane so the table baseline never depends on label visibility.
            local matchStatusBlockH = matchStatusGap + matchStatusLaneH
            y = y - presetRowH - spacingAB - presetHelperH - matchStatusBlockH - spacingDHeader

            widgets.loadoutDivider:ClearAllPoints()
            setDockObjectVisible(widgets.loadoutDivider, false)
            widgets.loadoutDivider:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
            widgets.loadoutDivider:SetPoint("TOPRIGHT", scrollChild, "TOPRIGHT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
            widgets.loadoutDivider:SetHeight(FIXED_FOOTPRINTS.DIVIDER_H)

            y = y - TABLE_BLOCK_SHIFT_Y

            widgets.portalSlotsLabel:ClearAllPoints()
            setDockObjectVisible(widgets.portalSlotsLabel, false)
            widgets.portalSlotsLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)

            local slotSpellW = PRESET_AND_TABLE.SLOT_SPELL_W
            local slotResultW = PRESET_AND_TABLE.SLOT_RESULT_W
            local slotEnableW = FIXED_FOOTPRINTS.ROW_ENABLE_HOLDER_W
            local slotEnableGap = PRESET_AND_TABLE.SLOT_ENABLE_GAP
            local slotDestW = math.max(PRESET_AND_TABLE.SLOT_DEST_MIN_W, math.floor(tableWidth * PRESET_AND_TABLE.SLOT_DEST_RATIO))
            local slotNameW = math.max(
                PRESET_AND_TABLE.SLOT_NAME_MIN_W,
                tableWidth - slotEnableW - slotEnableGap - slotDestW - slotSpellW - slotResultW - (PRESET_AND_TABLE.TABLE_COLUMN_GAP * 2)
            )

            local colEnableX = tableLeft
            local colDestX = colEnableX + slotEnableW + slotEnableGap
            local colNameX = colDestX + slotDestW + PRESET_AND_TABLE.TABLE_COLUMN_GAP
            local colSpellX = colNameX + slotNameW + PRESET_AND_TABLE.TABLE_COLUMN_GAP
            local colResultX = colSpellX + slotSpellW + PRESET_AND_TABLE.TABLE_COLUMN_GAP

            widgets.slotColStatus:ClearAllPoints()
            setDockObjectVisible(widgets.slotColStatus, false)
            widgets.slotColStatus:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
            widgets.slotColDestination:ClearAllPoints()
            setDockObjectVisible(widgets.slotColDestination, true)
            widgets.slotColDestination:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", colDestX + SLOT_HEADER_TEXT_X_SHIFT, y + DESTINATION_COLUMN_Y_SHIFT)
            widgets.slotColName:ClearAllPoints()
            setDockObjectVisible(widgets.slotColName, true)
            widgets.slotColName:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", colNameX + SLOT_HEADER_TEXT_X_SHIFT, y + NAME_SPELL_COLUMNS_Y_SHIFT)
            widgets.slotColSpellID:ClearAllPoints()
            setDockObjectVisible(widgets.slotColSpellID, true)
            widgets.slotColSpellID:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", colSpellX + SLOT_HEADER_TEXT_X_SHIFT, y + NAME_SPELL_COLUMNS_Y_SHIFT)
            widgets.slotColResult:ClearAllPoints()
            setDockObjectVisible(widgets.slotColResult, true)
            widgets.slotColResult:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", colResultX, y + RESULT_COLUMN_Y_SHIFT)
            y = y - PRESET_AND_TABLE.TABLE_HEADER_BOTTOM_GAP

            for i = 1, 10 do
                local row = slotRows[i]
                row.enabled:ClearAllPoints()
                row.enabled:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", colEnableX, y - PRESET_AND_TABLE.SLOT_ROW_TOP_NUDGE + SLOT_ENABLE_CHECKBOX_Y_SHIFT)
                row.status:ClearAllPoints()
                row.status:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", PAGE.OFFSCREEN_X, y + STATUS_TEXT_Y_SHIFT)
                row.destination:ClearAllPoints()
                row.destination:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", colDestX, y - PRESET_AND_TABLE.SLOT_ROW_TOP_NUDGE + DESTINATION_COLUMN_Y_SHIFT)
                row.destination:SetSize(slotDestW, FIXED_FOOTPRINTS.STANDARD_CONTROL_H)
                row.namePill:ClearAllPoints()
                row.namePill:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", colNameX, y - PRESET_AND_TABLE.SLOT_ROW_TOP_NUDGE + NAME_SPELL_COLUMNS_Y_SHIFT)
                row.namePill:SetSize(slotNameW, FIXED_FOOTPRINTS.STANDARD_CONTROL_H)
                row.spellIDPill:ClearAllPoints()
                row.spellIDPill:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", colSpellX, y - PRESET_AND_TABLE.SLOT_ROW_TOP_NUDGE + NAME_SPELL_COLUMNS_Y_SHIFT)
                row.spellIDPill:SetSize(slotSpellW, FIXED_FOOTPRINTS.STANDARD_CONTROL_H)
                row.result:ClearAllPoints()
                row.result:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", colResultX, y - PRESET_AND_TABLE.SLOT_RESULT_TOP_NUDGE + RESULT_COLUMN_Y_SHIFT)
                row.result:SetSize(slotResultW, slotResultW)
                y = y - PRESET_AND_TABLE.SLOT_ROW_STEP
            end

            local lowerBlockBaseY = y + LOWER_SECTIONS_BLOCK_OFFSET_Y
            widgets.lowerSectionsContainer:ClearAllPoints()
            widgets.lowerSectionsContainer:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, lowerBlockBaseY)
            widgets.lowerSectionsContainer:SetPoint("TOPRIGHT", scrollChild, "TOPRIGHT", 0, lowerBlockBaseY)
            widgets.lowerSectionsContainer:SetHeight(FIXED_FOOTPRINTS.LOWER_SECTIONS_SEED_H)

            local lowerParent = widgets.lowerSectionsContainer
            local lowerY = 0
            local tableToDividerGap = LOWER_SECTIONS.TABLE_TO_DIVIDER_GAP
            local dividerToLayoutGap = LOWER_SECTIONS.DIVIDER_TO_LAYOUT_GAP

            widgets.hideDungeonDivider:ClearAllPoints()
            widgets.hideDungeonDivider:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", dividerLeftX, lowerY - tableToDividerGap)
            widgets.hideDungeonDivider:SetPoint("TOPRIGHT", lowerParent, "TOPRIGHT", -right, lowerY - tableToDividerGap)
            widgets.hideDungeonDivider:SetHeight(FIXED_FOOTPRINTS.DIVIDER_H)
            lowerY = lowerY - tableToDividerGap - dividerToLayoutGap

            local sectionLabelX = leftBase + LABEL_X
            local sectionControlX = leftBase + CONTROL_X
            local segmentedW = math.max(LOWER_SECTIONS.SEGMENTED_MIN_W, math.min(LOWER_SECTIONS.SEGMENTED_MAX_W, contentW - sectionControlX - right - LOWER_SECTIONS.CONTROL_RIGHT_PAD))
            local sliderControlW = math.max(LOWER_SECTIONS.SLIDER_CONTROL_MIN_W, math.min(LOWER_SECTIONS.SLIDER_CONTROL_MAX_W, segmentedW))
            local layoutStepperW = math.max(LOWER_SECTIONS.LAYOUT_STEPPER_MIN_W, math.min(LOWER_SECTIONS.LAYOUT_STEPPER_MAX_W, segmentedW))
            local fontSizeStepperW = math.max(LOWER_SECTIONS.FONT_SIZE_STEPPER_MIN_W, math.min(LOWER_SECTIONS.FONT_SIZE_STEPPER_MAX_W, segmentedW))
            local outlineRowW = math.max(LOWER_SECTIONS.OUTLINE_ROW_MIN_W, math.min(LOWER_SECTIONS.OUTLINE_ROW_MAX_W, segmentedW))
            local fontColorSwatchSize = FIXED_FOOTPRINTS.FONT_COLOR_SWATCH_SIZE

            local function placeSegment(spec, rowY, controlW, anchorFrame)
                local anchorParent = anchorFrame or scrollChild
                spec.label:ClearAllPoints()
                spec.label:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", sectionLabelX, rowY)
                spec.control:ClearAllPoints()
                spec.control:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", sectionControlX, rowY)
                if spec.Layout then
                    spec:Layout(controlW)
                end
                return rowY - LOWER_SECTIONS.ROW_STEP
            end

            local function moveSpecOffscreen(spec, anchorFrame)
                if not spec or not spec.control or not spec.label then
                    return
                end
                local anchorParent = anchorFrame or scrollChild
                setDockObjectVisible(spec.control, false)
                setDockObjectVisible(spec.label, false)
                if spec.valueText then
                    setDockObjectVisible(spec.valueText, false)
                end
                spec.control:ClearAllPoints()
                spec.control:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                spec.label:ClearAllPoints()
                spec.label:SetPoint("TOPLEFT", anchorParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
            end

            local function placeDropdownRow(spec, rowY, controlW, anchorFrame)
                placeControl(spec, sectionLabelX, sectionControlX, rowY, controlW, anchorFrame)
                return rowY - LOWER_SECTIONS.ROW_STEP
            end

            local function placeSliderRow(spec, rowY, controlW, anchorFrame)
                placeControl(spec, sectionLabelX, sectionControlX, rowY, controlW, anchorFrame)
                return rowY - LOWER_SECTIONS.ROW_STEP
            end

            local function placeStepperRow(spec, rowY, controlW, anchorFrame)
                placeControl(spec, sectionLabelX, sectionControlX, rowY, controlW, anchorFrame)
                return rowY - LOWER_SECTIONS.ROW_STEP
            end

            widgets.layoutSectionHeader:ClearAllPoints()
            widgets.layoutSectionHeader:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", leftBase + CHECK_X, lowerY)
            lowerY = lowerY - LOWER_SECTIONS.SECTION_HEADER_STEP

            lowerY = placeSegment(widgets.layoutModeSegment, lowerY, segmentedW, lowerParent)
            local currentLayoutMode = getDockSimpleLayoutModeForSettings((PortalAuthority and PortalAuthority.defaults) or {})
            local showWrap = (currentLayoutMode == "GRID")
            if showWrap then
                setDockObjectVisible(widgets.iconsPerRowGridOnlyHint, true)
                lowerY = placeStepperRow(widgets.iconsPerRow, lowerY, layoutStepperW, lowerParent)
            else
                setDockObjectVisible(widgets.iconsPerRowGridOnlyHint, false)
                if widgets.iconsPerRow and widgets.iconsPerRow.control and widgets.iconsPerRow.label then
                    setDockObjectVisible(widgets.iconsPerRow.control, false)
                    setDockObjectVisible(widgets.iconsPerRow.label, false)
                    widgets.iconsPerRow.control:ClearAllPoints()
                    widgets.iconsPerRow.control:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                    widgets.iconsPerRow.label:ClearAllPoints()
                    widgets.iconsPerRow.label:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                end
            end
            if widgets.iconsPerRowGridOnlyHint and widgets.iconsPerRow and widgets.iconsPerRow.control then
                widgets.iconsPerRowGridOnlyHint:ClearAllPoints()
                widgets.iconsPerRowGridOnlyHint:SetPoint("LEFT", widgets.iconsPerRow.control, "RIGHT", PAGE.CONTROL_PLACEMENT.VALUE_TEXT_GAP, 0)
            end
            lowerY = lowerY + (showWrap and LOWER_SECTIONS.WRAP_PRESENT_TIGHTEN or 0) -- tighten only when wrap row is present
            if widgets.layoutIconSize and widgets.layoutIconSize.Refresh then
                widgets.layoutIconSize.Refresh()
            end
            lowerY = placeSliderRow(widgets.layoutIconSize, lowerY, sliderControlW, lowerParent)
            widgets.layoutSpacingHint:ClearAllPoints()
            widgets.layoutSpacingHint:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", sectionControlX, lowerY + LOWER_SECTIONS.LAYOUT_HINT_TOP_NUDGE)
            lowerY = lowerY - LOWER_SECTIONS.LAYOUT_HINT_STEP

            lowerY = lowerY - LOWER_SECTIONS.SECTION_GAP_TIGHT
            widgets.labelsSectionHeader:ClearAllPoints()
            widgets.labelsSectionHeader:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", leftBase + CHECK_X, lowerY)
            lowerY = lowerY - LOWER_SECTIONS.SECTION_HEADER_STEP

            lowerY = placeSegment(widgets.labelMode, lowerY, segmentedW, lowerParent)
            if widgets.labelSide and widgets.labelSide.control and widgets.labelSide.control:IsShown() then
                lowerY = placeSegment(widgets.labelSide, lowerY, segmentedW, lowerParent)
                moveSpecOffscreen(widgets.labelSideInside, lowerParent)
            elseif widgets.labelSideInside and widgets.labelSideInside.control and widgets.labelSideInside.control:IsShown() then
                lowerY = placeSegment(widgets.labelSideInside, lowerY, segmentedW, lowerParent)
                moveSpecOffscreen(widgets.labelSide, lowerParent)
            else
                moveSpecOffscreen(widgets.labelSide, lowerParent)
                moveSpecOffscreen(widgets.labelSideInside, lowerParent)
            end

            lowerY = lowerY - LOWER_SECTIONS.SECTION_GAP_TIGHT
            widgets.styleSectionHeader:ClearAllPoints()
            widgets.styleSectionHeader:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", leftBase + CHECK_X, lowerY)
            lowerY = lowerY - LOWER_SECTIONS.SECTION_HEADER_STEP

            local labelsEnabled = (getDockLabelModeForSettings((PortalAuthority and PortalAuthority.defaults) or {}) ~= "OFF")
            setLabelStyleRowsVisible(labelsEnabled)

            if labelsEnabled then
                local fontRowY = lowerY
                local fontToSizeGap = LOWER_SECTIONS.FONT_TO_SIZE_GAP
                local sizeToSwatchGap = LOWER_SECTIONS.SIZE_TO_SWATCH_GAP
                local availableForControls = math.max(LOWER_SECTIONS.FONT_ROW_CONTROL_MIN_W, contentW - sectionControlX - right - LOWER_SECTIONS.CONTROL_RIGHT_PAD)
                local reservedForInline = fontSizeStepperW + fontToSizeGap + sizeToSwatchGap + fontColorSwatchSize
                local fontPickerW = math.max(LOWER_SECTIONS.FONT_PICKER_MIN_W, availableForControls - reservedForInline)

                placeControl(widgets.font, sectionLabelX, sectionControlX, fontRowY, fontPickerW, lowerParent)
                local fontRowAnchor = widgets.font.control

                widgets.fontSize.label:ClearAllPoints()
                setDockObjectVisible(widgets.fontSize.label, false)
                widgets.fontSize.label:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                widgets.fontSize.control:ClearAllPoints()
                setDockObjectVisible(widgets.fontSize.control, true)
                widgets.fontSize.control:SetPoint("LEFT", fontRowAnchor, "RIGHT", fontToSizeGap, 0)
                if widgets.fontSize.SetWidth then
                    widgets.fontSize:SetWidth(fontSizeStepperW)
                else
                    widgets.fontSize.control:SetWidth(fontSizeStepperW)
                end

                local sizeAnchor = widgets.fontSize.control

                widgets.color.inlineLabel:ClearAllPoints()
                setDockObjectVisible(widgets.color.inlineLabel, false)
                widgets.color.inlineLabel:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                widgets.color.swatch:ClearAllPoints()
                setDockObjectVisible(widgets.color.swatch, true)
                widgets.color.swatch:SetPoint("LEFT", sizeAnchor, "RIGHT", sizeToSwatchGap, 0)
                widgets.color.swatch:SetSize(fontColorSwatchSize, fontColorSwatchSize)

                lowerY = lowerY - LOWER_SECTIONS.FONT_ROW_STEP
                lowerY = placeSegment(widgets.outlineToggle, lowerY, outlineRowW, lowerParent)
            else
                if widgets.font and widgets.font.control and widgets.font.label then
                    setDockObjectVisible(widgets.font.control, false)
                    setDockObjectVisible(widgets.font.label, false)
                    widgets.font.control:ClearAllPoints()
                    widgets.font.control:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                    widgets.font.label:ClearAllPoints()
                    widgets.font.label:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                end
                if widgets.fontSize and widgets.fontSize.control then
                    setDockObjectVisible(widgets.fontSize.control, false)
                    widgets.fontSize.control:ClearAllPoints()
                    widgets.fontSize.control:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                end
                if widgets.color and widgets.color.swatch then
                    setDockObjectVisible(widgets.color.swatch, false)
                    widgets.color.swatch:ClearAllPoints()
                    widgets.color.swatch:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                end
                if widgets.outlineToggle and widgets.outlineToggle.control and widgets.outlineToggle.label then
                    setDockObjectVisible(widgets.outlineToggle.control, false)
                    setDockObjectVisible(widgets.outlineToggle.label, false)
                    widgets.outlineToggle.control:ClearAllPoints()
                    widgets.outlineToggle.control:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                    widgets.outlineToggle.label:ClearAllPoints()
                    widgets.outlineToggle.label:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                end
            end

            lowerY = placeSegment(widgets.hoverGlowSegment, lowerY, outlineRowW, lowerParent)
            lowerY = lowerY - LOWER_SECTIONS.SECTION_GAP_LOOSE
            widgets.visibilityDivider:ClearAllPoints()
            widgets.visibilityDivider:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", dividerLeftX, lowerY - LOWER_SECTIONS.VISIBILITY_DIVIDER_TOP_GAP)
            widgets.visibilityDivider:SetPoint("TOPRIGHT", lowerParent, "TOPRIGHT", -right, lowerY - LOWER_SECTIONS.VISIBILITY_DIVIDER_TOP_GAP)
            widgets.visibilityDivider:SetHeight(FIXED_FOOTPRINTS.DIVIDER_H)
            lowerY = lowerY - LOWER_SECTIONS.VISIBILITY_DIVIDER_STEP
            lowerY = lowerY - VISIBILITY_BLOCK_SHIFT_Y

            widgets.enableLabel:ClearAllPoints()
            widgets.enableLabel:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", leftBase + CHECK_X, lowerY)
            lowerY = lowerY - LOWER_SECTIONS.VISIBILITY_HEADER_STEP

            local visibilityToggleW = outlineRowW
            local visibilityRows = {
                widgets.hideCombat,
                widgets.hideCity,
                widgets.hideDungeon,
                widgets.gizmoMode,
            }
            for _, spec in ipairs(visibilityRows) do
                placeControl(spec, sectionLabelX, sectionControlX, lowerY, visibilityToggleW, lowerParent)
                if spec and spec.label then
                    spec.label:ClearAllPoints()
                    spec.label:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", sectionLabelX, lowerY - VISIBILITY_LABELS_SHIFT_Y)
                end
                lowerY = lowerY - LOWER_SECTIONS.ROW_STEP
            end
            lowerY = lowerY + math.max(0, LOWER_SECTIONS.ROW_STEP - FIXED_FOOTPRINTS.STANDARD_CONTROL_H)
            if widgets.hideNames and widgets.hideNames.control and widgets.hideNames.label then
                setDockObjectVisible(widgets.hideNames.control, false)
                setDockObjectVisible(widgets.hideNames.label, false)
                widgets.hideNames.control:ClearAllPoints()
                widgets.hideNames.control:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
                widgets.hideNames.label:ClearAllPoints()
                widgets.hideNames.label:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
            end
            widgets.visibilityLabelSpacer:ClearAllPoints()
            setDockObjectVisible(widgets.visibilityLabelSpacer, false)
            widgets.visibilityLabelSpacer:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
            widgets.visibilitySpacer:ClearAllPoints()
            setDockObjectVisible(widgets.visibilitySpacer, false)
            widgets.visibilitySpacer:SetPoint("TOPLEFT", lowerParent, "TOPLEFT", PAGE.OFFSCREEN_X, PAGE.OFFSCREEN_Y)
            local lowerSectionObjects = {
                widgets.layoutSectionHeader,
                widgets.layoutModeSegment and widgets.layoutModeSegment.control or nil,
                widgets.iconsPerRow and widgets.iconsPerRow.control or nil,
                widgets.iconsPerRowGridOnlyHint,
                widgets.layoutIconSize and widgets.layoutIconSize.control or nil,
                widgets.layoutSpacingHint,
                widgets.labelsSectionHeader,
                widgets.labelMode and widgets.labelMode.control or nil,
                widgets.labelSide and widgets.labelSide.control or nil,
                widgets.labelSideInside and widgets.labelSideInside.control or nil,
                widgets.styleSectionHeader,
                widgets.font and widgets.font.control or nil,
                widgets.fontSize and widgets.fontSize.control or nil,
                widgets.color and widgets.color.swatch or nil,
                widgets.outlineToggle and widgets.outlineToggle.control or nil,
                widgets.hoverGlowSegment and widgets.hoverGlowSegment.control or nil,
                widgets.visibilityDivider,
                widgets.enableLabel,
                widgets.hideCombat and widgets.hideCombat.control or nil,
                widgets.hideCombat and widgets.hideCombat.label or nil,
                widgets.hideCity and widgets.hideCity.control or nil,
                widgets.hideCity and widgets.hideCity.label or nil,
                widgets.hideDungeon and widgets.hideDungeon.control or nil,
                widgets.hideDungeon and widgets.hideDungeon.label or nil,
                widgets.gizmoMode and widgets.gizmoMode.control or nil,
                widgets.gizmoMode and widgets.gizmoMode.label or nil,
            }
            local lowerSectionExtent = measureSettingsVisibleContentExtent(lowerParent, lowerSectionObjects)
            local explicitLowerSectionH = math.max(FIXED_FOOTPRINTS.LOWER_SECTIONS_SEED_H, math.ceil(-lowerY))
            local measuredLowerSectionH = lowerSectionExtent and math.ceil(lowerSectionExtent) or nil
            local lowerSectionH = math.max(explicitLowerSectionH, measuredLowerSectionH or 0)
            widgets.lowerSectionsContainer:SetHeight(lowerSectionH)

            y = lowerBlockBaseY - lowerSectionH

            local panelExtentObjects = {
                widgets.header,
                widgets.masterRowContainer,
                widgets.lockRowContainer,
                widgets.lockStateHelper,
                widgets.lockDockDivider,
                widgets.loadoutPresetSegment and widgets.loadoutPresetSegment.control or nil,
                widgets.dockCustomizationHelper,
                widgets.dockCustomizationMatchStatus,
                widgets.slotColDestination,
                widgets.slotColName,
                widgets.slotColSpellID,
                widgets.slotColResult,
                slotRows[10] and slotRows[10].enabled or nil,
                slotRows[10] and slotRows[10].destination or nil,
                slotRows[10] and slotRows[10].namePill or nil,
                slotRows[10] and slotRows[10].spellIDPill or nil,
                slotRows[10] and slotRows[10].result or nil,
                widgets.lowerSectionsContainer,
            }
            local panelMeasuredExtent = measureSettingsVisibleContentExtent(scrollChild, panelExtentObjects)
            local explicitPanelExtent = math.max(0, -y)
            local measuredPanelExtent = (panelMeasuredExtent and math.ceil(panelMeasuredExtent)) or nil
            local used = math.max(explicitPanelExtent, measuredPanelExtent or 0) + PAGE.BOTTOM_PAD
            local contentHeight = math.max(scrollFrame:GetHeight(), used)
            panelFrame._dockScrollLayoutPassToken = (tonumber(panelFrame._dockScrollLayoutPassToken) or 0) + 1
            panelFrame._dockResolvedNativeScrollPassToken = nil
            panelFrame._dockResolvedNativeScrollMax = nil
            panelFrame._dockPendingPostRelayoutSyncPassToken = nil
            scrollChild:SetHeight(contentHeight)
            updateScrollBehavior(scrollFrame, scrollChild)
            ensureDockScrollInteractionBindings()
            self._dockExpectedScrollMax = math.max(0, contentHeight - (scrollFrame:GetHeight() or 0))
            self._dockObservedScrollMax = getDockObservedScrollMax()
            applyDockScrollState(nil, {
                allowFallback = false,
                allowPostRelayoutSync = true,
            })
        end

        self._refresh = function()
            if ensureDatabase then
                ensureDatabase()
            end
            refreshDockXYDisplay()
            relayout()
            applyDockModuleEnabledState()
            if applyDockGizmoVisibilityOverrideState then
                applyDockGizmoVisibilityOverrideState()
            end
            refreshSettingsWindowHeaderAccessoryIfHosted(self)
        end

        PortalAuthority.onDockPositionChanged = function(x, y)
            if widgets.x and widgets.x.SetValue then
                widgets.x:SetValue(x)
            end
            if widgets.y and widgets.y.SetValue then
                widgets.y:SetValue(y)
            end
        end
        PortalAuthority.lockTipText = nil

        self._refreshDockXYDisplay = refreshDockXYDisplay
        self._destinationPicker = widgets.destinationPicker
        self._fontPicker = widgets.font and widgets.font.picker or nil
        self._renamePopover = widgets.renamePopover
        self._spellIDPopover = widgets.spellIDPopover
        self._paCloseFloatingControlsOnScroll = function(panel)
            if panel and panel._renamePopover and panel._renamePopover.IsShown and panel._renamePopover:IsShown() and panel._renamePopover.Close then
                panel._renamePopover:Close(false)
            end
            if panel and panel._spellIDPopover and panel._spellIDPopover.IsShown and panel._spellIDPopover:IsShown() and panel._spellIDPopover.Close then
                panel._spellIDPopover:Close(false)
            end
        end
        self._layout = relayout
        self._forceDockFontUiState = forceDockFontUiState
        self._forceDockFontSizeUiState = forceDockFontSizeUiState
        self._forceStyleToggleUiState = forceStyleToggleUiState
        self._forceDockIconSizeVisual = function()
            if not widgets.layoutIconSize then
                return
            end
            local dbValue = tonumber(PortalAuthorityDB and PortalAuthorityDB.dockIconSize)
            local uiValue = tonumber(PortalAuthorityDB and PortalAuthorityDB.dockIconSizeUI)
            if uiValue == nil and PortalAuthority and PortalAuthority.ApplyDockLayoutSettings and (not InCombatLockdown or not InCombatLockdown()) then
                pcall(function()
                    PortalAuthority:ApplyDockLayoutSettings(false)
                end)
                uiValue = tonumber(PortalAuthorityDB and PortalAuthorityDB.dockIconSizeUI)
            end
            local defaultValue = tonumber(PortalAuthority and PortalAuthority.defaults and PortalAuthority.defaults.dockIconSize) or 36
            local n = math.floor((uiValue or dbValue or defaultValue) + 0.5)
            if n < 16 then n = 16 end
            if n > 72 then n = 72 end
            if PortalAuthorityDB then
                PortalAuthorityDB.dockIconSizeUI = n
            end
            local slider = widgets.layoutIconSize.slider
            if slider then
                slider:SetValue(n)
            end
            if widgets.layoutIconSize.valueEdit then
                widgets.layoutIconSize.valueEdit:SetText(tostring(n))
                widgets.layoutIconSize.valueEdit:Hide()
            end
            if widgets.layoutIconSize.valueText then
                widgets.layoutIconSize.valueText:SetText(tostring(n))
                widgets.layoutIconSize.valueText:Show()
            end
        end

        registerRefreshCallback(function()
            if self._refresh then
                self._refresh()
            end
        end, self)
        scrollFrame:SetScript("OnSizeChanged", function()
            local useHostedFirstOpen = isPanelUsingCustomSettingsWindowHost(self)
            if useHostedFirstOpen and handleSettingsFirstOpenSizeReady(self, "size_changed") then
                return
            end
            if useHostedFirstOpen and shouldSuppressSettingsFirstOpenExternalRefresh(self) then
                return
            end
            relayout()
        end)
        self._settingsFirstOpenKey = "dock"
        self._settingsFirstOpenSizeTarget = scrollFrame
        self._settingsFirstOpenRender = function(panel)
            local selectionToken = tonumber(panel and panel._paFirstOpenSelectionToken) or 0
            if selectionToken ~= (tonumber(panel and panel._dockFirstOpenRenderToken) or 0) then
                panel._dockFirstOpenRenderToken = selectionToken
                panel._dockFirstOpenRenderPasses = 0
            end
            panel._dockFirstOpenRenderPasses = (tonumber(panel._dockFirstOpenRenderPasses) or 0) + 1
            renderDockPanel(panel)
        end
        self._settingsFirstOpenIsReady = function(panel)
            local viewportHeight = tonumber(scrollFrame and scrollFrame:GetHeight()) or 0
            local contentHeight = tonumber(scrollChild and scrollChild:GetHeight()) or 0
            local expectedMax = math.max(0, contentHeight - viewportHeight)
            local observedMax = math.max(
                tonumber(panel and panel._dockObservedScrollMax) or 0,
                getDockObservedScrollMax()
            )
            return isPanelUsingCustomSettingsWindowHost(panel)
                and panelHasSizedViewport(panel and panel._settingsFirstOpenSizeTarget or nil)
                and panel ~= nil
                and panel._initialized == true
                and type(panel._layout) == "function"
                and type(panel._refresh) == "function"
                and (tonumber(panel and panel._dockFirstOpenRenderPasses) or 0) >= 2
                and contentHeight >= math.max(viewportHeight, 1)
                and observedMax >= (expectedMax - 1)
        end
        settingsSearchRuntime.endDescriptorCapture(self)
        self._initialized = true
    end

    ensureDockPanelAnchored = function(self)
        local p = self:GetParent()
        if not p then
            return false
        end
        if self._paAnchorParent ~= p or not self._paAnchored then
            self:ClearAllPoints()
            self:SetAllPoints(p)
            self._paAnchorParent = p
        end
        self._paAnchored = true
        return true
    end

    renderDockPanel = function(self)
        ensureDockPanelAnchored(self)
        if self._refresh then
            self._refresh()
        end
    end

    panel:SetScript("OnShow", function(self)
        debugSettingsLog("OnShow:", self.name)
        if ensureDatabase then
            ensureDatabase()
        end
        self:EnsureBuilt()
        local useHostedFirstOpen = isPanelUsingCustomSettingsWindowHost(self)
        if useHostedFirstOpen and self._paFirstOpenState == "pending" then
            resetSettingsFirstOpenPanelState(self)
        end
        if useHostedFirstOpen and handleSettingsFirstOpenPanelShow(self) then
            return
        end
        withPanelHydration(self, function()
            renderDockPanel(self)
        end)
    end)
    panel:SetScript("OnHide", function(self)
        self._dockPendingPostRelayoutSyncPassToken = nil
        if isPanelUsingCustomSettingsWindowHost(self) or shouldSuppressSettingsFirstOpenExternalRefresh(self) then
            handleSettingsFirstOpenPanelHide(self)
        end
        if self._destinationPicker and self._destinationPicker.Close then
            self._destinationPicker:Close()
        end
        if self._fontPicker and self._fontPicker.Close then
            self._fontPicker:Close()
        end
        if self._renamePopover and self._renamePopover.Close then
            self._renamePopover:Close(false)
        end
        if self._spellIDPopover and self._spellIDPopover.Close then
            self._spellIDPopover:Close(false)
        end
    end)

    return panel
end
OptionsInternal.createDockPanel = createDockPanel
