-- Portal Authority — options/shared_controls.lua
-- Shared control factories, active-picker ownership, early search capture, and scroll runtime helpers.
--
-- Depends on: PortalAuthority.OptionsInternal media helpers
-- Exposes:    PortalAuthority.OptionsInternal control helpers; PortalAuthority.OptionsInternal.SearchRuntime early slice; PortalAuthority:CreateSettingsWindowSegmentedControl
-- DB keys:    dockFontColor plus caller-owned panel keys
-- Events:     none
-- Search key: none

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 or {}
OptionsInternal.SearchRuntime = settingsSearchRuntime
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 refreshManagedFontObject = OptionsInternal.refreshManagedFontObject
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 IsPointerInsideActivePickerSafeZone = OptionsInternal.IsPointerInsideActivePickerSafeZone
local CloseActivePortalPicker = OptionsInternal.CloseActivePortalPicker

local function frameIsAlive(frame)
    return frame ~= nil and type(frame.GetObjectType) == "function"
end

local function isDescendedFromFrame(frame, ancestor)
    if not frame or not ancestor then
        return false
    end
    local current = frame
    while current do
        if current == ancestor then
            return true
        end
        current = current.GetParent and current:GetParent() or nil
    end
    return false
end

local function markSettingsWindowVisualOptIn(frame)
    if frameIsAlive(frame) then
        frame._paSettingsWindowVisualStyleOptIn = true
    end
    return frame
end

local function isSettingsWindowVisualOptedIn(frame)
    local current = frame
    while current do
        if current._paSettingsWindowVisualStyleOptIn == true then
            return true
        end
        current = current.GetParent and current:GetParent() or nil
    end
    return false
end

local function isUsingCustomSettingsWindowVisualStyle(frame)
    if not frameIsAlive(frame) or not PortalAuthority or not PortalAuthority.IsSettingsWindowHostEnabled then
        return false
    end
    if not PortalAuthority:IsSettingsWindowHostEnabled() then
        return false
    end
    if not isSettingsWindowVisualOptedIn(frame) then
        return false
    end

    local runtime = PortalAuthority._settingsWindowRuntime
    local window = runtime and runtime.window or nil
    if frameIsAlive(window) and isDescendedFromFrame(frame, window) then
        return true
    end

    local current = frame
    while current do
        if current._paHostedBySettingsWindow == true then
            return true
        end
        current = current.GetParent and current:GetParent() or nil
    end

    return false
end

local function registerSettingsWindowVisualRefresh(frame, callback)
    if frameIsAlive(frame) and type(callback) == "function" then
        frame._paSettingsWindowVisualRefresh = callback
    end
end

local function setTextureSolidColor(texture, color)
    if not texture then
        return
    end
    local r, g, b, a = unpackColor(color)
    if texture.SetColorTexture then
        texture:SetColorTexture(r, g, b, a)
    elseif texture.SetVertexColor then
        texture:SetVertexColor(r, g, b, a)
    end
end

local function hideDefaultScrollBarPart(part, scrollFrame)
    if not part then
        return
    end
    if part.SetAlpha then
        part:SetAlpha(0)
    end
    if part.Hide then
        part:Hide()
    end
    if part.EnableMouse then
        part:EnableMouse(false)
    end
    if part.SetSize then
        part:SetSize(1, 1)
    elseif part.SetWidth and part.SetHeight then
        part:SetWidth(1)
        part:SetHeight(1)
    end
    if part.HookScript and not part._paSettingsWindowHideHook then
        part._paSettingsWindowHideHook = true
        part:HookScript("OnShow", function(selfPart)
            if isUsingCustomSettingsWindowVisualStyle(scrollFrame) then
                if selfPart.SetAlpha then
                    selfPart:SetAlpha(0)
                end
                if selfPart.Hide then
                    selfPart:Hide()
                end
            end
        end)
    end
end

local function getTemplateScrollBar(scrollFrame)
    if not frameIsAlive(scrollFrame) then
        return nil
    end
    if frameIsAlive(scrollFrame.ScrollBar) then
        return scrollFrame.ScrollBar
    end
    local frameName = scrollFrame.GetName and scrollFrame:GetName() or nil
    if frameName and _G then
        local named = _G[frameName .. "ScrollBar"]
        if frameIsAlive(named) then
            return named
        end
    end
    return nil
end

local function hideDefaultScrollBarChrome(scrollFrame)
    local scrollBar = getTemplateScrollBar(scrollFrame)
    if not frameIsAlive(scrollBar) then
        return nil
    end

    if scrollBar.SetAlpha then
        scrollBar:SetAlpha(0)
    end
    if scrollBar.Hide then
        scrollBar:Hide()
    end
    if scrollBar.EnableMouse then
        scrollBar:EnableMouse(false)
    end
    scrollBar:ClearAllPoints()
    scrollBar:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", SETTINGS_WINDOW_SCROLLBAR_GAP, 0)
    scrollBar:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", SETTINGS_WINDOW_SCROLLBAR_GAP, 0)
    scrollBar:SetWidth(SETTINGS_WINDOW_SCROLLBAR_WIDTH)

    if scrollBar.GetRegions then
        for i = 1, select("#", scrollBar:GetRegions()) do
            local region = select(i, scrollBar:GetRegions())
            if region then
                if region.SetTexture then
                    region:SetTexture(nil)
                end
                if region.SetAlpha then
                    region:SetAlpha(0)
                end
                if region.Hide then
                    region:Hide()
                end
            end
        end
    end

    if scrollBar.GetChildren then
        for i = 1, select("#", scrollBar:GetChildren()) do
            hideDefaultScrollBarPart(select(i, scrollBar:GetChildren()), scrollFrame)
        end
    end

    hideDefaultScrollBarPart(scrollBar.ScrollUpButton, scrollFrame)
    hideDefaultScrollBarPart(scrollBar.ScrollDownButton, scrollFrame)
    hideDefaultScrollBarPart(scrollBar.UpButton, scrollFrame)
    hideDefaultScrollBarPart(scrollBar.DownButton, scrollFrame)

    return scrollBar
end

local function refreshCustomSettingsScrollBarThumb(customBar)
    if not frameIsAlive(customBar) then
        return
    end
    local thumb = customBar._paThumb
    if not thumb then
        return
    end

    local color = customBar._paHovered and SETTINGS_WINDOW_SCROLLBAR_THUMB_HOVER or SETTINGS_WINDOW_SCROLLBAR_THUMB
    local r, g, b, a = unpackColor(color)
    local visibility = tonumber(customBar._paVisibilityMultiplier)
    if visibility == nil then
        visibility = 0
    end
    if visibility < 0 then
        visibility = 0
    elseif visibility > 1 then
        visibility = 1
    end
    a = a * visibility
    if thumb.SetColorTexture then
        thumb:SetColorTexture(r, g, b, a)
    elseif thumb.SetVertexColor then
        thumb:SetVertexColor(r, g, b, a)
    end
end

PortalAuthority._settingsScrollRuntime = PortalAuthority._settingsScrollRuntime or {}
local settingsScrollRuntime = PortalAuthority._settingsScrollRuntime
local ACTIVE_PORTAL_PICKER

settingsScrollRuntime.numbersDiffer = function(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

settingsScrollRuntime.fadeDelaySeconds = 1.5
settingsScrollRuntime.fadeDurationSeconds = 0.4

settingsScrollRuntime.getAdapter = function(scrollFrame, scrollChild, viewportHeight, contentHeight)
    local adapter = scrollFrame and scrollFrame._paCustomScrollAdapter or nil
    local resolved = {}

    resolved.panelId = type(adapter) == "table" and adapter.panelId or "settings"

    if type(adapter) == "table" and type(adapter.resolveCanonicalMax) == "function" then
        resolved.resolveCanonicalMax = adapter.resolveCanonicalMax
    else
        resolved.resolveCanonicalMax = function()
            return math.max(0, (tonumber(contentHeight) or 0) - (tonumber(viewportHeight) or 0)), "derived"
        end
    end

    if type(adapter) == "table" and type(adapter.getCurrentValue) == "function" then
        resolved.getCurrentValue = adapter.getCurrentValue
    else
        resolved.getCurrentValue = function()
            if not scrollFrame or type(scrollFrame.GetVerticalScroll) ~= "function" then
                return 0
            end
            return math.max(0, tonumber(scrollFrame:GetVerticalScroll()) or 0)
        end
    end

    if type(adapter) == "table" and type(adapter.applyValue) == "function" then
        resolved.applyValue = adapter.applyValue
    else
        resolved.applyValue = function(value)
            if not scrollFrame or type(scrollFrame.SetVerticalScroll) ~= "function" then
                return false
            end
            scrollFrame._paSkipNextCustomScrollBarHookSync = true
            scrollFrame:SetVerticalScroll(math.max(0, tonumber(value) or 0))
            return true
        end
    end

    return resolved
end

settingsScrollRuntime.canReveal = function(customBar)
    if not frameIsAlive(customBar) then
        return false
    end
    if not customBar.IsShown or not customBar:IsShown() then
        return false
    end
    local maxScroll = math.max(
        tonumber(customBar._paBoundMax) or 0,
        tonumber(customBar._paRenderedMaxScroll) or 0
    )
    return maxScroll > 0
end

settingsScrollRuntime.stopFadeTween = function(customBar)
    if not frameIsAlive(customBar) then
        return
    end
    local fadeDriver = customBar._paFadeDriver
    if frameIsAlive(fadeDriver) and fadeDriver.GetScript and fadeDriver:GetScript("OnUpdate") ~= nil then
        fadeDriver:SetScript("OnUpdate", nil)
    end
    customBar._paFadeTweenActive = nil
end

settingsScrollRuntime.cancelFade = function(customBar)
    if not frameIsAlive(customBar) then
        return
    end
    customBar._paFadeToken = (tonumber(customBar._paFadeToken) or 0) + 1
    settingsScrollRuntime.stopFadeTween(customBar)
end

settingsScrollRuntime.setThumbVisibility = function(customBar, alpha)
    if not frameIsAlive(customBar) then
        return
    end
    alpha = tonumber(alpha) or 0
    if alpha < 0 then
        alpha = 0
    elseif alpha > 1 then
        alpha = 1
    end
    customBar._paVisibilityMultiplier = alpha

    local thumbButton = customBar._paThumbButton
    if frameIsAlive(thumbButton) and thumbButton.EnableMouse then
        thumbButton:EnableMouse(customBar._paDragging == true or alpha > 0.01)
    end

    refreshCustomSettingsScrollBarThumb(customBar)
end

settingsScrollRuntime.hideImmediately = function(customBar)
    if not frameIsAlive(customBar) then
        return
    end
    settingsScrollRuntime.cancelFade(customBar)
    settingsScrollRuntime.setThumbVisibility(customBar, 0)
end

settingsScrollRuntime.beginFade = function(customBar, token)
    if not frameIsAlive(customBar) then
        return
    end
    if customBar._paDragging == true or customBar._paThumbHovered == true or customBar._paTrackHovered == true then
        return
    end
    if not settingsScrollRuntime.canReveal(customBar) then
        settingsScrollRuntime.setThumbVisibility(customBar, 0)
        return
    end

    local fadeDriver = customBar._paFadeDriver
    if not frameIsAlive(fadeDriver) then
        settingsScrollRuntime.setThumbVisibility(customBar, 0)
        return
    end

    local startAlpha = tonumber(customBar._paVisibilityMultiplier)
    if startAlpha == nil then
        startAlpha = 0
    end
    if startAlpha <= 0 then
        settingsScrollRuntime.setThumbVisibility(customBar, 0)
        return
    end

    settingsScrollRuntime.stopFadeTween(customBar)
    customBar._paFadeTweenActive = true
    local elapsedTime = 0
    fadeDriver:SetScript("OnUpdate", function(_, elapsed)
        if not frameIsAlive(customBar) then
            return
        end
        if customBar._paFadeToken ~= token then
            settingsScrollRuntime.stopFadeTween(customBar)
            return
        end
        if customBar._paDragging == true or customBar._paThumbHovered == true or customBar._paTrackHovered == true then
            settingsScrollRuntime.stopFadeTween(customBar)
            settingsScrollRuntime.setThumbVisibility(customBar, 1)
            return
        end
        if not settingsScrollRuntime.canReveal(customBar) then
            settingsScrollRuntime.stopFadeTween(customBar)
            settingsScrollRuntime.setThumbVisibility(customBar, 0)
            return
        end

        elapsedTime = elapsedTime + (tonumber(elapsed) or 0)
        local duration = settingsScrollRuntime.fadeDurationSeconds
        local progress = duration > 0 and math.max(0, math.min(1, elapsedTime / duration)) or 1
        settingsScrollRuntime.setThumbVisibility(customBar, startAlpha * (1 - progress))
        if progress >= 1 then
            settingsScrollRuntime.stopFadeTween(customBar)
            settingsScrollRuntime.setThumbVisibility(customBar, 0)
        end
    end)
end

settingsScrollRuntime.armFade = function(customBar)
    if not frameIsAlive(customBar) then
        return
    end
    if customBar._paDragging == true or customBar._paThumbHovered == true or customBar._paTrackHovered == true then
        return
    end
    if not settingsScrollRuntime.canReveal(customBar) then
        settingsScrollRuntime.hideImmediately(customBar)
        return
    end

    settingsScrollRuntime.cancelFade(customBar)
    local token = tonumber(customBar._paFadeToken) or 0
    if not (C_Timer and C_Timer.After) then
        settingsScrollRuntime.beginFade(customBar, token)
        return
    end

    C_Timer.After(settingsScrollRuntime.fadeDelaySeconds, function()
        if not frameIsAlive(customBar) then
            return
        end
        if customBar._paFadeToken ~= token then
            return
        end
        settingsScrollRuntime.beginFade(customBar, token)
    end)
end

settingsScrollRuntime.showThumb = function(customBar)
    if not frameIsAlive(customBar) then
        return
    end
    settingsScrollRuntime.cancelFade(customBar)
    if not settingsScrollRuntime.canReveal(customBar) then
        settingsScrollRuntime.setThumbVisibility(customBar, 0)
        return
    end
    settingsScrollRuntime.setThumbVisibility(customBar, 1)
end

settingsScrollRuntime.noteInteraction = function(customBar)
    if not frameIsAlive(customBar) then
        return
    end
    settingsScrollRuntime.showThumb(customBar)
    if customBar._paDragging ~= true
        and customBar._paThumbHovered ~= true
        and customBar._paTrackHovered ~= true then
        settingsScrollRuntime.armFade(customBar)
    end
end

settingsScrollRuntime.noteFrameInteraction = function(scrollFrame)
    if not frameIsAlive(scrollFrame) then
        return
    end
    local customBar = scrollFrame._paCustomScrollBar
    if not frameIsAlive(customBar) then
        return
    end
    settingsScrollRuntime.noteInteraction(customBar)
end

local function isPointerInsideActivePickerSafeZone()
    if type(MouseIsOver) ~= "function" then
        return false
    end

    local picker = ACTIVE_PORTAL_PICKER
    if not frameIsAlive(picker) or not picker.IsShown or not picker:IsShown() then
        return false
    end

    local function isPointerInside(frame)
        if not frameIsAlive(frame) or not frame.IsShown or not frame:IsShown() then
            return false
        end
        return MouseIsOver(frame) and true or false
    end

    if isPointerInside(picker) then
        return true
    end

    for _, frame in ipairs({
        picker.scroll,
        picker.search,
        picker.clearButton,
        picker.closeButton,
    }) do
        if isPointerInside(frame) then
            return true
        end
    end

    return false
end

settingsScrollRuntime.closeFloatingControlsForParentScroll = function(scrollFrame)
    if not frameIsAlive(scrollFrame) or scrollFrame._paCloseFloatingControlsOnUserScroll ~= true then
        return false
    end

    if isPointerInsideActivePickerSafeZone() then
        return true
    end

    local panel = scrollFrame._paCloseFloatingControlsPanel
        or scrollFrame._paSearchPanel
        or scrollFrame._paOwnerPanel
        or nil

    if ACTIVE_PORTAL_PICKER
        and ACTIVE_PORTAL_PICKER.IsShown
        and ACTIVE_PORTAL_PICKER:IsShown()
        and ACTIVE_PORTAL_PICKER.Close
    then
        ACTIVE_PORTAL_PICKER:Close()
    end

    if type(CloseDropDownMenus) == "function" then
        CloseDropDownMenus()
    end

    if panel and type(panel._paCloseFloatingControlsOnScroll) == "function" then
        panel._paCloseFloatingControlsOnScroll(panel)
    end

    return false
end

settingsScrollRuntime.suppressHoverRevealForCurrentFrame = function(customBar)
    if not frameIsAlive(customBar) then
        return
    end
    customBar._paHoverRevealSuppressed = true
    local token = (tonumber(customBar._paHoverRevealToken) or 0) + 1
    customBar._paHoverRevealToken = token
    if C_Timer and C_Timer.After then
        C_Timer.After(0, function()
            if not frameIsAlive(customBar) then
                return
            end
            if customBar._paHoverRevealToken ~= token then
                return
            end
            customBar._paHoverRevealSuppressed = nil
        end)
    else
        customBar._paHoverRevealSuppressed = nil
    end
end

settingsScrollRuntime.clearDrag = function(customBar)
    if not frameIsAlive(customBar) then
        return
    end

    customBar._paDragSession = nil
    customBar._paDragging = nil
    if customBar.GetScript and customBar:GetScript("OnUpdate") ~= nil then
        customBar:SetScript("OnUpdate", nil)
    end
    customBar._paHovered = customBar._paThumbHovered == true or customBar._paTrackHovered == true
    refreshCustomSettingsScrollBarThumb(customBar)
    if customBar._paHovered == true then
        settingsScrollRuntime.showThumb(customBar)
    else
        settingsScrollRuntime.armFade(customBar)
    end
end

settingsScrollRuntime.setHover = function(customBar, key, isHovered)
    if not frameIsAlive(customBar) then
        return
    end

    if key == "thumb" then
        customBar._paThumbHovered = isHovered and true or nil
    else
        customBar._paTrackHovered = isHovered and true or nil
    end
    customBar._paHovered = customBar._paDragging == true
        or customBar._paThumbHovered == true
        or customBar._paTrackHovered == true
    refreshCustomSettingsScrollBarThumb(customBar)

    if customBar._paHovered == true then
        if settingsScrollRuntime.canReveal(customBar) then
            settingsScrollRuntime.cancelFade(customBar)
            if customBar._paHoverRevealSuppressed ~= true
                or (tonumber(customBar._paVisibilityMultiplier) or 0) > 0 then
                settingsScrollRuntime.setThumbVisibility(customBar, 1)
            end
        end
    elseif customBar._paDragging ~= true then
        settingsScrollRuntime.armFade(customBar)
    end
end

settingsScrollRuntime.layoutThumb = function(customBar, viewportHeight, contentHeight, maxScroll, currentValue)
    if not frameIsAlive(customBar) then
        return
    end

    local thumb = customBar._paThumb
    local thumbButton = customBar._paThumbButton
    if not thumb or not frameIsAlive(thumbButton) then
        return
    end

    local barHeight = math.max(
        tonumber(customBar:GetHeight()) or 0,
        tonumber(viewportHeight) or 0,
        SETTINGS_WINDOW_SCROLLBAR_MIN_THUMB_HEIGHT
    )
    local ratio = (tonumber(contentHeight) or 0) > 0
            and math.max(0, math.min(1, (tonumber(viewportHeight) or 0) / math.max(tonumber(contentHeight) or 1, 1)))
        or 1
    local thumbHeight = math.max(SETTINGS_WINDOW_SCROLLBAR_MIN_THUMB_HEIGHT, math.floor(barHeight * ratio))
    local maxThumbHeight = math.max(1, math.floor(barHeight * 0.60))
    thumbHeight = math.min(barHeight, thumbHeight, maxThumbHeight)

    local clampedMax = math.max(0, tonumber(maxScroll) or 0)
    local clampedValue = math.max(0, math.min(clampedMax, tonumber(currentValue) or 0))
    local usableTravel = math.max(0, barHeight - thumbHeight)
    local pct = clampedMax > 0 and math.max(0, math.min(1, clampedValue / clampedMax)) or 0
    local topOffset = usableTravel * pct
    local roundedOffset = math.floor(topOffset + 0.5)

    if thumb.SetHeight then
        thumb:SetHeight(thumbHeight)
    end
    if thumb.SetWidth then
        thumb:SetWidth(SETTINGS_WINDOW_SCROLLBAR_WIDTH)
    end
    thumb:ClearAllPoints()
    thumb:SetPoint("TOPLEFT", customBar, "TOPLEFT", 0, -roundedOffset)
    thumb:SetPoint("TOPRIGHT", customBar, "TOPRIGHT", 0, -roundedOffset)

    thumbButton:ClearAllPoints()
    thumbButton:SetPoint("TOPLEFT", customBar, "TOPLEFT", 0, -roundedOffset)
    thumbButton:SetSize(SETTINGS_WINDOW_SCROLLBAR_WIDTH, thumbHeight)

    local trackTop = tonumber(customBar.GetTop and customBar:GetTop()) or 0
    local trackBottom = tonumber(customBar.GetBottom and customBar:GetBottom()) or 0
    if settingsScrollRuntime.numbersDiffer(customBar._paGeometryTrackTop, trackTop)
        or settingsScrollRuntime.numbersDiffer(customBar._paGeometryTrackBottom, trackBottom)
        or settingsScrollRuntime.numbersDiffer(customBar._paThumbHeight, thumbHeight)
        or settingsScrollRuntime.numbersDiffer(customBar._paUsableThumbTravel, usableTravel)
        or settingsScrollRuntime.numbersDiffer(customBar._paRenderedMaxScroll, clampedMax) then
        customBar._paGeometryRevision = (tonumber(customBar._paGeometryRevision) or 0) + 1
    end

    customBar._paGeometryTrackTop = trackTop
    customBar._paGeometryTrackBottom = trackBottom
    customBar._paThumbHeight = thumbHeight
    customBar._paUsableThumbTravel = usableTravel
    customBar._paRenderedMaxScroll = clampedMax
    customBar._paRenderedScrollValue = clampedValue
end

settingsScrollRuntime.applyValue = function(customBar, requestedValue, options)
    if not frameIsAlive(customBar) then
        return false
    end

    local scrollFrame = customBar._paOwnerScrollFrame
    if not frameIsAlive(scrollFrame) then
        return false
    end

    local scrollChild = customBar._paOwnerScrollChild
        or (scrollFrame.GetScrollChild and scrollFrame:GetScrollChild())
    local viewportHeight = tonumber(scrollFrame.GetHeight and scrollFrame:GetHeight()) or 0
    local contentHeight = scrollChild and (tonumber(scrollChild.GetHeight and scrollChild:GetHeight()) or 0) or 0
    local adapter = settingsScrollRuntime.getAdapter(scrollFrame, scrollChild, viewportHeight, contentHeight)
    local maxScroll = adapter.resolveCanonicalMax(options, scrollFrame, scrollChild, viewportHeight, contentHeight)
    if type(maxScroll) ~= "number" then
        return false
    end
    maxScroll = math.max(0, maxScroll)
    local currentValue = math.max(0, tonumber(adapter.getCurrentValue(options, scrollFrame, scrollChild, viewportHeight, contentHeight)) or 0)
    local clampedValue = math.max(0, math.min(maxScroll, tonumber(requestedValue) or currentValue))
    if adapter.applyValue(clampedValue, options, scrollFrame, scrollChild, viewportHeight, contentHeight) == false then
        return false
    end

    settingsScrollRuntime.sync(
        scrollFrame,
        scrollChild,
        tonumber(scrollFrame.GetHeight and scrollFrame:GetHeight()) or viewportHeight,
        scrollChild and (tonumber(scrollChild.GetHeight and scrollChild:GetHeight()) or contentHeight) or contentHeight
    )
    return true, clampedValue, maxScroll
end

settingsScrollRuntime.beginThumbDrag = function(customBar)
    if not frameIsAlive(customBar) then
        return
    end

    if settingsScrollRuntime.closeFloatingControlsForParentScroll(customBar._paOwnerScrollFrame) then
        return
    end
    settingsScrollRuntime.clearDrag(customBar)

    local maxScroll = math.max(0, tonumber(customBar._paRenderedMaxScroll) or 0)
    local usableTravel = math.max(0, tonumber(customBar._paUsableThumbTravel) or 0)
    local thumbHeight = math.max(0, tonumber(customBar._paThumbHeight) or 0)
    if maxScroll <= 0 or usableTravel <= 0 or thumbHeight <= 0 then
        return
    end

    local track = customBar._paTrackButton
    local scale = tonumber(track and track.GetEffectiveScale and track:GetEffectiveScale()) or 0
    local trackTop = tonumber(track and track.GetTop and track:GetTop()) or nil
    local trackBottom = tonumber(track and track.GetBottom and track:GetBottom()) or nil
    local cursorY = select(2, GetCursorPosition())
    if not trackTop or not trackBottom or trackTop <= trackBottom or scale <= 0 or not cursorY then
        return
    end

    cursorY = cursorY / scale
    local currentValue = math.max(0, math.min(maxScroll, tonumber(customBar._paRenderedScrollValue) or 0))
    local currentTopOffset = usableTravel * (maxScroll > 0 and (currentValue / maxScroll) or 0)
    local thumbTop = trackTop - currentTopOffset

    customBar._paDragSession = {
        mode = "thumb-drag",
        geometryRevision = tonumber(customBar._paGeometryRevision) or 0,
        trackTop = trackTop,
        usableTravel = usableTravel,
        maxScroll = maxScroll,
        thumbHeight = thumbHeight,
        scale = scale,
        grabOffset = thumbTop - cursorY,
    }
    customBar._paDragging = true
    customBar._paHovered = true
    settingsScrollRuntime.showThumb(customBar)

    customBar:SetScript("OnUpdate", function(selfBar)
        local session = selfBar._paDragSession
        if not session then
            settingsScrollRuntime.clearDrag(selfBar)
            return
        end
        if not IsMouseButtonDown or not IsMouseButtonDown("LeftButton") then
            settingsScrollRuntime.clearDrag(selfBar)
            return
        end
        if session.geometryRevision ~= (tonumber(selfBar._paGeometryRevision) or 0) then
            settingsScrollRuntime.clearDrag(selfBar)
            return
        end

        local dragCursorY = select(2, GetCursorPosition())
        if not dragCursorY or session.scale <= 0 then
            return
        end
        dragCursorY = dragCursorY / session.scale

        local desiredThumbTop = dragCursorY + session.grabOffset
        local topOffset = session.trackTop - desiredThumbTop
        if topOffset < 0 then
            topOffset = 0
        elseif topOffset > session.usableTravel then
            topOffset = session.usableTravel
        end

        local pct = session.usableTravel > 0 and (topOffset / session.usableTravel) or 0
        local targetValue = session.maxScroll * pct
        settingsScrollRuntime.applyValue(selfBar, targetValue, {
            source = "thumb-drag",
            allowFallback = false,
            allowPostRelayoutSync = false,
        })
    end)
end

settingsScrollRuntime.handleTrackClick = function(customBar)
    if not frameIsAlive(customBar) or customBar._paDragSession ~= nil then
        return
    end

    if settingsScrollRuntime.closeFloatingControlsForParentScroll(customBar._paOwnerScrollFrame) then
        return
    end
    local maxScroll = math.max(0, tonumber(customBar._paRenderedMaxScroll) or 0)
    local usableTravel = math.max(0, tonumber(customBar._paUsableThumbTravel) or 0)
    local thumbHeight = math.max(0, tonumber(customBar._paThumbHeight) or 0)
    if maxScroll <= 0 or usableTravel <= 0 or thumbHeight <= 0 then
        return
    end

    local track = customBar._paTrackButton
    local scale = tonumber(track and track.GetEffectiveScale and track:GetEffectiveScale()) or 0
    local trackTop = tonumber(track and track.GetTop and track:GetTop()) or nil
    local cursorY = select(2, GetCursorPosition())
    if not trackTop or scale <= 0 or not cursorY then
        return
    end

    cursorY = cursorY / scale
    local topOffset = trackTop - (cursorY + (thumbHeight * 0.5))
    if topOffset < 0 then
        topOffset = 0
    elseif topOffset > usableTravel then
        topOffset = usableTravel
    end

    local pct = usableTravel > 0 and (topOffset / usableTravel) or 0
    local targetValue = maxScroll * pct
    settingsScrollRuntime.noteInteraction(customBar)
    settingsScrollRuntime.applyValue(customBar, targetValue, {
        source = "track-click",
        allowFallback = false,
        allowPostRelayoutSync = false,
    })
end

settingsScrollRuntime.sync = function(scrollFrame, scrollChild, viewportHeight, contentHeight)
    if not frameIsAlive(scrollFrame) then
        return
    end

    scrollFrame._paOwnerScrollChild = scrollChild
    local isCustomHosted = isUsingCustomSettingsWindowVisualStyle(scrollFrame)
    local customBar = scrollFrame._paCustomScrollBar
    if not isCustomHosted then
        if frameIsAlive(customBar) then
            settingsScrollRuntime.clearDrag(customBar)
            customBar:Hide()
        end
        return
    end

    local defaultScrollBar = hideDefaultScrollBarChrome(scrollFrame)
    if defaultScrollBar then
        scrollFrame.ScrollBar = defaultScrollBar
    end

    if not frameIsAlive(customBar) then
        customBar = CreateFrame("Slider", nil, scrollFrame:GetParent() or scrollFrame)
        customBar:SetOrientation("VERTICAL")
        customBar:SetThumbTexture("Interface\\Buttons\\WHITE8x8")
        customBar:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", SETTINGS_WINDOW_SCROLLBAR_GAP, 0)
        customBar:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", SETTINGS_WINDOW_SCROLLBAR_GAP, 0)
        customBar:SetWidth(SETTINGS_WINDOW_SCROLLBAR_WIDTH)
        customBar:SetMinMaxValues(0, 0)
        customBar:SetValueStep(1)
        customBar:EnableMouse(false)
        customBar._paOwnerScrollFrame = scrollFrame
        customBar._paOwnerScrollChild = scrollChild
        customBar._paVisibilityMultiplier = 0

        local track = customBar:CreateTexture(nil, "BACKGROUND")
        track:SetAllPoints()
        setTextureSolidColor(track, SETTINGS_WINDOW_SCROLLBAR_TRACK)
        customBar._paTrack = track

        local nativeThumb = customBar:GetThumbTexture()
        if nativeThumb then
            nativeThumb:SetTexture(nil)
            nativeThumb:SetAlpha(0)
            nativeThumb:Hide()
        end

        local thumb = customBar:CreateTexture(nil, "ARTWORK")
        thumb:SetTexture("Interface\\Buttons\\WHITE8x8")
        thumb:SetPoint("TOPLEFT", customBar, "TOPLEFT", 0, 0)
        thumb:SetWidth(SETTINGS_WINDOW_SCROLLBAR_WIDTH)
        customBar._paThumb = thumb

        local trackButton = CreateFrame("Button", nil, customBar)
        trackButton:SetAllPoints()
        trackButton:RegisterForClicks("LeftButtonUp")
        trackButton:EnableMouse(true)
        trackButton:SetFrameLevel((customBar:GetFrameLevel() or 0) + 1)
        customBar._paTrackButton = trackButton

        local thumbButton = CreateFrame("Button", nil, customBar)
        thumbButton:RegisterForClicks("LeftButtonDown", "LeftButtonUp")
        thumbButton:EnableMouse(false)
        thumbButton:SetFrameLevel((customBar:GetFrameLevel() or 0) + 2)
        customBar._paThumbButton = thumbButton
        customBar._paFadeDriver = trackButton

        trackButton:SetScript("OnEnter", function()
            settingsScrollRuntime.setHover(customBar, "track", true)
        end)
        trackButton:SetScript("OnLeave", function()
            settingsScrollRuntime.setHover(customBar, "track", false)
        end)
        trackButton:SetScript("OnClick", function()
            settingsScrollRuntime.handleTrackClick(customBar)
        end)

        thumbButton:SetScript("OnEnter", function()
            settingsScrollRuntime.setHover(customBar, "thumb", true)
        end)
        thumbButton:SetScript("OnLeave", function()
            settingsScrollRuntime.setHover(customBar, "thumb", false)
        end)
        thumbButton:SetScript("OnMouseDown", function(_, mouseButton)
            if mouseButton ~= "LeftButton" then
                return
            end
            settingsScrollRuntime.noteInteraction(customBar)
            settingsScrollRuntime.beginThumbDrag(customBar)
        end)
        thumbButton:SetScript("OnMouseUp", function(_, mouseButton)
            if mouseButton ~= "LeftButton" then
                return
            end
            settingsScrollRuntime.clearDrag(customBar)
        end)

        customBar:SetScript("OnValueChanged", function() end)
        customBar:SetScript("OnHide", function(selfBar)
            selfBar._paThumbHovered = nil
            selfBar._paTrackHovered = nil
            selfBar._paHoverRevealSuppressed = nil
            selfBar._paHoverRevealToken = nil
            settingsScrollRuntime.hideImmediately(selfBar)
            settingsScrollRuntime.clearDrag(selfBar)
        end)

        scrollFrame._paCustomScrollBar = customBar

        if scrollFrame.HookScript and not scrollFrame._paCustomScrollBarHooked then
            scrollFrame._paCustomScrollBarHooked = true
            scrollFrame:HookScript("OnVerticalScroll", function(selfFrame)
                if selfFrame._paSkipNextCustomScrollBarHookSync then
                    selfFrame._paSkipNextCustomScrollBarHookSync = nil
                    return
                end
                local slider = selfFrame._paCustomScrollBar
                if not frameIsAlive(slider) then
                    return
                end
                local child = slider._paOwnerScrollChild or (selfFrame.GetScrollChild and selfFrame:GetScrollChild())
                settingsScrollRuntime.sync(
                    selfFrame,
                    child,
                    tonumber(selfFrame.GetHeight and selfFrame:GetHeight()) or 0,
                    child and (tonumber(child.GetHeight and child:GetHeight()) or 0) or 0
                )
            end)
        end
    end

    customBar._paOwnerScrollChild = scrollChild
    local adapter = settingsScrollRuntime.getAdapter(scrollFrame, scrollChild, viewportHeight, contentHeight)
    local range = adapter.resolveCanonicalMax({
        source = "sync",
        allowFallback = true,
        allowPostRelayoutSync = false,
    }, scrollFrame, scrollChild, viewportHeight, contentHeight)
    range = math.max(0, tonumber(range) or 0)
    if range <= 0 or not scrollFrame.IsShown or not scrollFrame:IsShown() then
        customBar._paThumbHovered = nil
        customBar._paTrackHovered = nil
        customBar._paHoverRevealSuppressed = nil
        customBar._paHoverRevealToken = nil
        settingsScrollRuntime.clearDrag(customBar)
        settingsScrollRuntime.hideImmediately(customBar)
        customBar:Hide()
        customBar._paSyncing = true
        customBar:SetMinMaxValues(0, 0)
        customBar:SetValue(0)
        customBar._paSyncing = false
        customBar._paBoundMax = 0
        settingsScrollRuntime.layoutThumb(customBar, viewportHeight, contentHeight, 0, 0)
        return
    end

    local wasShown = customBar.IsShown and customBar:IsShown()
    customBar:SetShown(true)
    if not wasShown then
        settingsScrollRuntime.suppressHoverRevealForCurrentFrame(customBar)
        settingsScrollRuntime.setThumbVisibility(customBar, 0)
    end
    if settingsScrollRuntime.numbersDiffer(customBar._paBoundMax, range) then
        customBar:SetMinMaxValues(0, range)
        customBar._paBoundMax = range
    end
    local currentScroll = adapter.getCurrentValue({
        source = "sync",
    }, scrollFrame, scrollChild, viewportHeight, contentHeight)
    currentScroll = math.max(0, math.min(range, tonumber(currentScroll) or 0))
    customBar._paSyncing = true
    if settingsScrollRuntime.numbersDiffer(customBar:GetValue(), currentScroll) then
        customBar:SetValue(currentScroll)
    end
    customBar._paSyncing = false
    settingsScrollRuntime.layoutThumb(customBar, viewportHeight, contentHeight, range, currentScroll)
    refreshCustomSettingsScrollBarThumb(customBar)
end
local function restoreColorPickerPrevious(previous, fallback)
    fallback = type(fallback) == "table" and fallback or {}

    local alpha = tonumber(previous and previous.a)
    if alpha == nil then
        local opacity = tonumber(previous and previous.opacity)
        if opacity ~= nil then
            alpha = 1 - opacity
        end
    end
    if alpha == nil then
        alpha = tonumber(fallback.a) or 1
    end

    return {
        r = tonumber(previous and previous.r) or tonumber(fallback.r) or 1,
        g = tonumber(previous and previous.g) or tonumber(fallback.g) or 1,
        b = tonumber(previous and previous.b) or tonumber(fallback.b) or 1,
        a = math.max(0, math.min(1, alpha)),
    }
end

local function openColorPicker(onChanged)
    local c = PortalAuthorityDB.dockFontColor
    if ColorPickerFrame and ColorPickerFrame.SetupColorPickerAndShow then
        ColorPickerFrame:SetupColorPickerAndShow({
            r = c.r,
            g = c.g,
            b = c.b,
            opacity = 1 - c.a,
            hasOpacity = true,
            swatchFunc = function()
                local r, g, b = ColorPickerFrame:GetColorRGB()
                local a = 1 - (OpacitySliderFrame and OpacitySliderFrame:GetValue() or 0)
                PortalAuthorityDB.dockFontColor = { r = r, g = g, b = b, a = a }
                onChanged()
            end,
            opacityFunc = function()
                local r, g, b = ColorPickerFrame:GetColorRGB()
                local a = 1 - (OpacitySliderFrame and OpacitySliderFrame:GetValue() or 0)
                PortalAuthorityDB.dockFontColor = { r = r, g = g, b = b, a = a }
                onChanged()
            end,
            cancelFunc = function(prev)
                PortalAuthorityDB.dockFontColor = restoreColorPickerPrevious(prev, c)
                onChanged()
            end,
        })
    end
end

local function applyGlobalFontToObject(object, size, flags)
    if not object or not object.SetFont then
        return
    end

    object._paGlobalFontManaged = true
    object._paGlobalFontSize = size
    object._paGlobalFontFlags = flags
    refreshManagedFontObject(object)
end

local function createStyledFontString(parent, layer, template, size)
    local fontString = parent:CreateFontString(nil, layer, template)
    applyGlobalFontToObject(fontString, size)
    if parent then
        local current = parent
        local panel = nil
        while current do
            if current._paSearchSectionKey and current._paSearchScrollChild then
                panel = current
                break
            end
            current = current.GetParent and current:GetParent() or nil
        end

        if panel
            and (tonumber(panel._paSearchDescriptorCaptureDepth) or 0) > 0
            and (parent == panel._paSearchScrollChild or parent._paSettingsSearchTextContainer == true)
        then
            panel._paSearchDescriptorState = panel._paSearchDescriptorState or {
                descriptors = {},
                nextOrderIndex = 1,
            }
            local state = panel._paSearchDescriptorState
            local descriptor = {
                kind = "text",
                sectionKey = panel._paSearchSectionKey,
                sectionName = panel._paSearchSectionName or panel.name or "",
                displayRef = fontString,
                anchorRef = fontString,
                controlRef = fontString,
                searchRefs = { fontString },
                searchWidgetRef = nil,
                searchLabelRef = fontString,
                searchHelperRefs = {},
                searchRowFrame = nil,
                orderIndex = state.nextOrderIndex,
                fontSize = tonumber(size) or 0,
                fontTemplate = template,
                parentRef = parent,
            }
            state.nextOrderIndex = state.nextOrderIndex + 1
            state.descriptors[#state.descriptors + 1] = descriptor
            fontString._paSearchDescriptor = descriptor
            panel._paSearchRegistryDirty = true
            if not panel._paSearchTopTarget then
                panel._paSearchTopTarget = fontString
            end
        end
    end
    return fontString
end

function settingsSearchRuntime.markTextContainer(frame)
    if frameIsAlive(frame) then
        frame._paSettingsSearchTextContainer = true
    end
    return frame
end

function settingsSearchRuntime.getPanelFromObject(object)
    local current = object
    while current do
        if current._paSearchSectionKey and current._paSearchScrollChild then
            return current
        end
        current = current.GetParent and current:GetParent() or nil
    end
    return nil
end

function settingsSearchRuntime.ensureDescriptorState(panel)
    if not panel then
        return nil
    end
    panel._paSearchDescriptorState = panel._paSearchDescriptorState or {
        descriptors = {},
        nextOrderIndex = 1,
    }
    return panel._paSearchDescriptorState
end

function settingsSearchRuntime.beginDescriptorCapture(panel)
    if not panel then
        return
    end
    panel._paSearchDescriptorCaptureDepth = (tonumber(panel._paSearchDescriptorCaptureDepth) or 0) + 1
    panel._paSearchRegistryDirty = true
end

function settingsSearchRuntime.endDescriptorCapture(panel)
    if not panel then
        return
    end
    panel._paSearchDescriptorCaptureDepth = math.max((tonumber(panel._paSearchDescriptorCaptureDepth) or 1) - 1, 0)
end

function settingsSearchRuntime.ensureDescriptorForTextRef(textRef, controlRef)
    if not frameIsAlive(textRef) then
        return nil, nil
    end
    local panel = settingsSearchRuntime.getPanelFromObject(textRef)
    if not panel or (tonumber(panel._paSearchDescriptorCaptureDepth) or 0) <= 0 then
        return nil, panel
    end

    local descriptor = textRef._paSearchDescriptor
    if descriptor then
        if frameIsAlive(controlRef) then
            descriptor.controlRef = controlRef
            descriptor.anchorRef = controlRef
            descriptor.searchWidgetRef = controlRef
        end
        descriptor.searchLabelRef = frameIsAlive(textRef) and textRef or descriptor.searchLabelRef
        descriptor.searchHelperRefs = descriptor.searchHelperRefs or {}
        return descriptor, panel
    end

    local state = settingsSearchRuntime.ensureDescriptorState(panel)
    descriptor = {
        kind = "text",
        sectionKey = panel._paSearchSectionKey,
        sectionName = panel._paSearchSectionName or panel.name or "",
        displayRef = textRef,
        anchorRef = frameIsAlive(controlRef) and controlRef or textRef,
        controlRef = frameIsAlive(controlRef) and controlRef or textRef,
        searchRefs = { textRef },
        searchWidgetRef = frameIsAlive(controlRef) and controlRef or nil,
        searchLabelRef = textRef,
        searchHelperRefs = {},
        searchRowFrame = nil,
        orderIndex = state.nextOrderIndex,
        fontSize = tonumber(textRef._paGlobalFontSize) or 0,
        fontTemplate = nil,
        parentRef = textRef.GetParent and textRef:GetParent() or nil,
    }
    state.nextOrderIndex = state.nextOrderIndex + 1
    state.descriptors[#state.descriptors + 1] = descriptor
    textRef._paSearchDescriptor = descriptor
    panel._paSearchRegistryDirty = true
    if not panel._paSearchTopTarget then
        panel._paSearchTopTarget = textRef
    end
    return descriptor, panel
end

function settingsSearchRuntime.resolveExplicitSearchRowFrame(target)
    if type(target) ~= "table" then
        return nil
    end

    local candidates = {
        target.searchRowFrame,
        target.rowFrame,
        target.rowContainer,
        target.row,
    }
    for i = 1, #candidates do
        if frameIsAlive(candidates[i]) then
            return candidates[i]
        end
    end
    return nil
end

function settingsSearchRuntime.appendUniqueSearchRef(refs, ref)
    if type(refs) ~= "table" or not frameIsAlive(ref) then
        return
    end
    for i = 1, #refs do
        if refs[i] == ref then
            return
        end
    end
    refs[#refs + 1] = ref
end

function settingsSearchRuntime.getIconTypeForKind(kind)
    kind = type(kind) == "string" and string.lower(kind) or ""
    if kind == "toggle" or kind == "checkbox" then
        return "toggle"
    end
    if kind == "dropdown" or kind == "picker" or kind == "font_picker" or kind == "sound_picker" or kind == "texture_picker" then
        return "dropdown"
    end
    if kind == "slider" or kind == "premium_slider" or kind == "stepper" then
        return "slider"
    end
    return nil
end

function settingsSearchRuntime.registerControlText(textRef, controlRef, kind, extraRefs, searchRowFrame)
    local descriptor, panel = settingsSearchRuntime.ensureDescriptorForTextRef(textRef, controlRef)
    if not descriptor then
        return nil
    end

    descriptor.kind = kind or descriptor.kind or "control"
    descriptor.controlRef = frameIsAlive(controlRef) and controlRef or descriptor.controlRef
    descriptor.anchorRef = frameIsAlive(controlRef) and controlRef or descriptor.anchorRef
    descriptor.searchWidgetRef = frameIsAlive(controlRef) and controlRef or descriptor.searchWidgetRef
    descriptor.searchLabelRef = frameIsAlive(textRef) and textRef or descriptor.searchLabelRef
    descriptor.searchRowFrame = frameIsAlive(searchRowFrame) and searchRowFrame or descriptor.searchRowFrame
    descriptor.iconType = settingsSearchRuntime.getIconTypeForKind(kind) or descriptor.iconType
    descriptor.searchRefs = descriptor.searchRefs or {}
    descriptor.searchHelperRefs = descriptor.searchHelperRefs or {}
    if frameIsAlive(textRef) then
        descriptor.searchRefs[1] = textRef
    end
    if type(extraRefs) == "table" then
        for i = 1, #extraRefs do
            settingsSearchRuntime.appendUniqueSearchRef(descriptor.searchRefs, extraRefs[i])
            settingsSearchRuntime.appendUniqueSearchRef(descriptor.searchHelperRefs, extraRefs[i])
        end
    end
    if panel then
        panel._paSearchRegistryDirty = true
    end
    return descriptor
end

function settingsSearchRuntime.registerSpec(spec, explicitKind)
    if type(spec) ~= "table" then
        return spec
    end
    local label = spec.label
    local control = spec.control or spec.slider or spec.button or spec.valueButton or nil
    local extraRefs = {}
    local function addExtraRef(ref)
        if frameIsAlive(ref) then
            extraRefs[#extraRefs + 1] = ref
        end
    end
    addExtraRef(spec.helper)
    addExtraRef(spec.description)
    addExtraRef(spec.desc)
    addExtraRef(spec.hint)
    addExtraRef(spec.helperText)
    addExtraRef(spec.secondaryText)
    addExtraRef(spec.subLabel)
    addExtraRef(spec.valueText)
    addExtraRef(spec.valueEdit)
    addExtraRef(spec.text)
    addExtraRef(spec.caret)
    if type(spec.explainers) == "table" then
        for i = 1, #spec.explainers do
            addExtraRef(spec.explainers[i])
        end
    end
    if type(spec.extraSearchRefs) == "table" then
        for i = 1, #spec.extraSearchRefs do
            addExtraRef(spec.extraSearchRefs[i])
        end
    end
    local searchRowFrame = settingsSearchRuntime.resolveExplicitSearchRowFrame(spec)
    if frameIsAlive(label) then
        settingsSearchRuntime.registerControlText(label, control, explicitKind or spec.kind, extraRefs, searchRowFrame)
    elseif control and control.Text then
        settingsSearchRuntime.registerControlText(control.Text, control, explicitKind or spec.kind, extraRefs, searchRowFrame)
    end
    return spec
end

function settingsSearchRuntime.attachExtraSearchRefs(target, extraRefs)
    if type(target) ~= "table" or type(extraRefs) ~= "table" or #extraRefs <= 0 then
        return nil
    end
    local label = target.label
    local control = target.control or target.slider or target.button or target.valueButton or nil
    local searchRowFrame = settingsSearchRuntime.resolveExplicitSearchRowFrame(target)
    if frameIsAlive(label) then
        return settingsSearchRuntime.registerControlText(label, control, target.kind, extraRefs, searchRowFrame)
    end
    if control and control.Text then
        return settingsSearchRuntime.registerControlText(control.Text, control, target.kind, extraRefs, searchRowFrame)
    end
    return nil
end

local function getClassHex(classFile)
    local color = RAID_CLASS_COLORS and RAID_CLASS_COLORS[classFile]
    if color and color.colorStr then
        return color.colorStr
    end
    return "ffffffff"
end

local function createTopRightHeader(parent, yOffset)
    local header = createStyledFontString(parent, "ARTWORK", "GameFontHighlight", 12)
    header:SetJustifyH("RIGHT")
    header:SetText("")
    header:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -20, yOffset or -20)
    header:Hide()
    return header
end

local function PA_SafeBool(v)
    local ok, s = pcall(tostring, v)
    if not ok or s == nil then return false end
    local okb, b = pcall(string.byte, s, 1)
    if not okb or b == nil then return false end
    local nb = tonumber(tostring(b)) or 0
    return nb == 116
end

local function createCheckbox(parent, text, key, onApply)
    local check = CreateFrame("CheckButton", nil, parent, "UICheckButtonTemplate")
    check.text:SetText(text)
    applyGlobalFontToObject(check.text, 12)
    check:SetChecked(PA_SafeBool(PortalAuthorityDB and PortalAuthorityDB[key]))
    check:SetScript("OnClick", function(btn)
        if PortalAuthorityDB then
            PortalAuthorityDB[key] = PA_SafeBool(btn:GetChecked())
        end
        if key == "dockHideInCombat" then
            debugSettingsLog("dockHideInCombat updated:", tostring(PortalAuthorityDB[key]))
        end
        if onApply then onApply() end
    end)
    settingsSearchRuntime.registerControlText(check.text, check, "checkbox")
    return check
end

local function createActionButton(parent, text, onClick, width, searchOpts)
    local btn = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
    btn:SetText(text)
    applyGlobalFontToObject(btn.Text, 12)
    searchOpts = type(searchOpts) == "table" and searchOpts or nil
    if btn.Text and searchOpts and searchOpts.textOrigin then
        settingsSearchRuntime.markSearchTextOrigin(btn.Text, searchOpts.textOrigin, searchOpts)
    end
    btn:SetSize(width or 120, 22)
    btn:SetScript("OnClick", function() if onClick then onClick() end end)
    settingsSearchRuntime.registerControlText(btn.Text, btn, "button")
    return btn
end

local function styleSecondaryActionButtonShared(btn, styleOpts)
    if not btn then
        return
    end
    styleOpts = type(styleOpts) == "table" and styleOpts or {}
    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 baseBg = styleOpts.baseBg or { CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A }
    local hoverBg = styleOpts.hoverBg or { CONTROL_HOVER_BG_R, CONTROL_HOVER_BG_G, CONTROL_HOVER_BG_B, CONTROL_HOVER_BG_A }
    local pressedBg = styleOpts.pressedBg or { 0.08, 0.09, 0.12, 0.96 }
    local baseBorder = styleOpts.baseBorder or { CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, 0.90 }
    local hoverBorder = styleOpts.hoverBorder or { CONTROL_HOVER_BORDER_R, CONTROL_HOVER_BORDER_G, CONTROL_HOVER_BORDER_B, 0.95 }
    local pressedBorder = styleOpts.pressedBorder or { CONTROL_HOVER_BORDER_R, CONTROL_HOVER_BORDER_G, CONTROL_HOVER_BORDER_B, 0.90 }
    local textColor = styleOpts.textColor or { 0.86, 0.86, 0.89, 1 }

    if not btn._paSecondaryBg then
        btn._paSecondaryBg = btn:CreateTexture(nil, "ARTWORK")
        btn._paSecondaryBg:SetAllPoints(btn)
        btn._paSecondaryBg:SetTexture("Interface\\Buttons\\WHITE8x8")

        btn._paSecondaryBorder = CreateFrame("Frame", nil, btn, "BackdropTemplate")
        btn._paSecondaryBorder:SetPoint("TOPLEFT", btn, "TOPLEFT", -1, 1)
        btn._paSecondaryBorder:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", 1, -1)
        btn._paSecondaryBorder:SetBackdrop({
            edgeFile = "Interface\\Buttons\\WHITE8x8",
            edgeSize = 1,
        })
    end

    local function hideTemplateChrome(selfBtn)
        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
        for _, part in ipairs({
            "Left", "Middle", "Right",
            "LeftDisabled", "MiddleDisabled", "RightDisabled",
            "LeftHighlight", "MiddleHighlight", "RightHighlight",
            "LeftPressed", "MiddlePressed", "RightPressed",
        }) 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
    end

    local function applyVisual(selfBtn)
        hideTemplateChrome(selfBtn)
        local hovered = selfBtn._paSecondaryHover and true or false
        local pressed = selfBtn._paSecondaryPressed and true or false
        if pressed then
            selfBtn._paSecondaryBg:SetVertexColor(pressedBg[1], pressedBg[2], pressedBg[3], pressedBg[4])
            selfBtn._paSecondaryBorder:SetBackdropBorderColor(pressedBorder[1], pressedBorder[2], pressedBorder[3], pressedBorder[4])
        elseif hovered then
            selfBtn._paSecondaryBg:SetVertexColor(hoverBg[1], hoverBg[2], hoverBg[3], hoverBg[4])
            selfBtn._paSecondaryBorder:SetBackdropBorderColor(hoverBorder[1], hoverBorder[2], hoverBorder[3], hoverBorder[4])
        else
            selfBtn._paSecondaryBg:SetVertexColor(baseBg[1], baseBg[2], baseBg[3], baseBg[4])
            selfBtn._paSecondaryBorder:SetBackdropBorderColor(baseBorder[1], baseBorder[2], baseBorder[3], baseBorder[4])
        end
        if selfBtn.Text then
            selfBtn.Text:SetTextColor(textColor[1], textColor[2], textColor[3], textColor[4])
        end
    end

    btn:SetScript("OnEnter", function(selfBtn)
        selfBtn._paSecondaryHover = true
        applyVisual(selfBtn)
    end)
    btn:SetScript("OnLeave", function(selfBtn)
        selfBtn._paSecondaryHover = false
        selfBtn._paSecondaryPressed = false
        applyVisual(selfBtn)
    end)
    btn:SetScript("OnMouseDown", function(selfBtn)
        selfBtn._paSecondaryPressed = true
        applyVisual(selfBtn)
    end)
    btn:SetScript("OnMouseUp", function(selfBtn)
        selfBtn._paSecondaryPressed = false
        applyVisual(selfBtn)
    end)
    btn:HookScript("OnShow", function(selfBtn)
        applyVisual(selfBtn)
    end)
    applyVisual(btn)
end

local function createNumeric(parent, labelText, key, min, max, onApply)
    local label = createStyledFontString(parent, "ARTWORK", "GameFontHighlight", 12)
    label:SetText(labelText)

    local box = CreateFrame("EditBox", nil, parent, "InputBoxTemplate")
    box:SetAutoFocus(false)
    box:SetTextInsets(8, 8, 0, 0)
    box:SetTextColor(1, 1, 1, 1)
    box:SetHighlightColor(1, 1, 1, 0.25)
    applyGlobalFontToObject(box, 12)

    local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
    local defaultValue = defaults[key]
    if defaultValue == nil then
        defaultValue = 0
    end

    local isRefreshing = false
    local isProgrammaticSetText = false
    local didUserEdit = false
    local didInitialHydrate = false

    local function setEditBoxText(text)
        if box:GetText() == text then
            return
        end
        isProgrammaticSetText = true
        box:SetText(text)
        isProgrammaticSetText = false
    end

    local function refreshFromDB()
        local value = PortalAuthorityDB[key]
        if value == nil then
            value = defaultValue
        end
        local text = tostring(value or defaultValue)
        isRefreshing = true
        setEditBoxText(text)
        box:ClearFocus()
        box:HighlightText(0, 0)
        box:SetCursorPosition(0)
        didUserEdit = false
        isRefreshing = false
    end

    refreshFromDB()

    local function apply()
        if isRefreshing or isProgrammaticSetText or not didUserEdit then
            return
        end

        local v = tonumber(box:GetText())
        if not v then
            refreshFromDB()
            return
        end
        if min and v < min then v = min end
        if max and v > max then v = max end
        v = math.floor(v)
        local priorValue = tonumber(PortalAuthorityDB[key])
        local changed = priorValue ~= v
        PortalAuthorityDB[key] = v
        refreshFromDB()
        if changed and onApply then onApply() end
    end

    box:SetScript("OnEditFocusGained", function()
        didUserEdit = false
    end)
    box:SetScript("OnTextChanged", function(_, userInput)
        if isRefreshing or isProgrammaticSetText then
            return
        end
        if userInput then
            didUserEdit = true
        end
    end)

    box:SetScript("OnEnterPressed", function(self)
        apply()
        self:ClearFocus()
    end)
    box:SetScript("OnEditFocusLost", apply)
    box:HookScript("OnShow", function()
        if didInitialHydrate then
            return
        end
        didInitialHydrate = true
        refreshFromDB()
    end)

    local spec = {
        kind = "numeric",
        label = label,
        control = box,
        SetValue = function(_, value)
            if box:HasFocus() then return end
            local numericValue = tonumber(value)
            if numericValue == nil then
                refreshFromDB()
                return
            end
            local text = tostring(math.floor(numericValue))
            isRefreshing = true
            setEditBoxText(text)
            box:ClearFocus()
            box:HighlightText(0, 0)
            box:SetCursorPosition(0)
            didUserEdit = false
            isRefreshing = false
        end,
        Refresh = refreshFromDB,
    }
    settingsSearchRuntime.registerSpec(spec)
    return spec
end

local function setSettingsBackdropColors(frame, bgColor, borderColor)
    if frame and frame.SetBackdropColor then
        frame:SetBackdropColor(unpackColor(bgColor))
    end
    if frame and frame.SetBackdropBorderColor then
        frame:SetBackdropBorderColor(unpackColor(borderColor))
    end
end

local function getSettingsWindowSelectVisual(button, hovered, pressed, enabled)
    if not isUsingCustomSettingsWindowVisualStyle(button) then
        return nil
    end
    if not enabled then
        return {
            bg = SETTINGS_WINDOW_HOST_COLORS.disabledBg,
            border = SETTINGS_WINDOW_HOST_COLORS.disabledBorder,
            text = SETTINGS_WINDOW_HOST_COLORS.disabledText,
        }
    end
    if pressed then
        return {
            bg = SETTINGS_WINDOW_HOST_COLORS.hoverBg,
            border = SETTINGS_WINDOW_HOST_COLORS.onBorder,
            text = SETTINGS_WINDOW_HOST_COLORS.hoverText,
        }
    end
    if hovered then
        return {
            bg = SETTINGS_WINDOW_HOST_COLORS.hoverBg,
            border = SETTINGS_WINDOW_HOST_COLORS.hoverBorder,
            text = SETTINGS_WINDOW_HOST_COLORS.hoverText,
        }
    end
    return {
        bg = SETTINGS_WINDOW_HOST_COLORS.idleBg,
        border = SETTINGS_WINDOW_HOST_COLORS.idleBorder,
        text = SETTINGS_WINDOW_HOST_COLORS.idleText,
    }
end

local function getDropdownTemplateButton(dropdown)
    if not dropdown then
        return nil
    end
    if dropdown.Button then
        return dropdown.Button
    end
    local name = dropdown.GetName and dropdown:GetName() or nil
    if type(name) == "string" and name ~= "" then
        return _G[name .. "Button"]
    end
    return nil
end

local function getDropdownTemplateText(dropdown)
    if not dropdown then
        return nil
    end
    if dropdown.Text then
        return dropdown.Text
    end
    local name = dropdown.GetName and dropdown:GetName() or nil
    if type(name) == "string" and name ~= "" then
        return _G[name .. "Text"]
    end
    return nil
end

local function setDropdownTemplateTexturesVisible(dropdown, visible)
    if not dropdown then
        return
    end

    local alpha = visible and 1 or 0
    for _, key in ipairs({
        "Left", "Middle", "Right",
        "LeftHighlight", "MiddleHighlight", "RightHighlight",
        "LeftDisabled", "MiddleDisabled", "RightDisabled",
    }) do
        local tex = dropdown[key]
        if tex and tex.SetAlpha then
            tex:SetAlpha(alpha)
        end
        if tex and tex.SetShown then
            tex:SetShown(visible)
        elseif tex and visible then
            tex:Show()
        elseif tex then
            tex:Hide()
        end
    end

    local button = getDropdownTemplateButton(dropdown)
    if button then
        for _, getterName in ipairs({ "GetNormalTexture", "GetPushedTexture", "GetHighlightTexture", "GetDisabledTexture" }) do
            local getter = button[getterName]
            local tex = getter and getter(button) or nil
            if tex and tex.SetAlpha then
                tex:SetAlpha(alpha)
            end
            if tex and tex.SetShown then
                tex:SetShown(visible)
            elseif tex and visible then
                tex:Show()
            elseif tex then
                tex:Hide()
            end
        end
    end
end

local function ensureSettingsWindowDropdownChrome(dropdown)
    if not dropdown or dropdown._paHostDropdownReady then
        return
    end
    dropdown._paHostDropdownReady = true

    local chrome = CreateFrame("Frame", nil, dropdown, "BackdropTemplate")
    chrome:SetBackdrop(FLAT_SETTINGS_BACKDROP)
    chrome:SetPoint("TOPLEFT", dropdown, "TOPLEFT", 18, -3)
    chrome:SetPoint("BOTTOMRIGHT", dropdown, "BOTTOMRIGHT", -18, 9)
    chrome:SetFrameStrata(dropdown:GetFrameStrata())
    chrome:SetFrameLevel(math.max(1, (dropdown:GetFrameLevel() or 1) - 1))
    dropdown._paHostDropdownChrome = chrome

    local text = getDropdownTemplateText(dropdown)
    if text then
        applyGlobalFontToObject(text, 11)
    end

    local function applyVisual(selfDropdown)
        local button = getDropdownTemplateButton(selfDropdown)
        local hoverSource = button or selfDropdown
        local hovered = hoverSource and hoverSource.IsMouseOver and hoverSource:IsMouseOver() or false
        local enabled = true
        if button and button.IsEnabled then
            enabled = button:IsEnabled()
        end

        local themed = getSettingsWindowSelectVisual(selfDropdown, hovered, false, enabled)
        if not themed then
            chrome:Hide()
            setDropdownTemplateTexturesVisible(selfDropdown, true)
            return
        end

        chrome:Show()
        setDropdownTemplateTexturesVisible(selfDropdown, false)
        setSettingsBackdropColors(chrome, themed.bg, themed.border)
        if text and text.SetTextColor then
            text:SetTextColor(unpackColor(themed.text))
        end
    end

    registerSettingsWindowVisualRefresh(dropdown, applyVisual)
    dropdown:HookScript("OnShow", applyVisual)
    dropdown:HookScript("OnHide", applyVisual)

    local button = getDropdownTemplateButton(dropdown)
    if button then
        button:HookScript("OnEnter", function()
            applyVisual(dropdown)
        end)
        button:HookScript("OnLeave", function()
            applyVisual(dropdown)
        end)
    end

    applyVisual(dropdown)
end

local function applySettingsWindowSelectButtonChrome(button, text, caret, hovered, pressed, enabled, legacyVisual)
    local themed = getSettingsWindowSelectVisual(button, hovered, pressed, enabled)
    if themed then
        setSettingsBackdropColors(button, themed.bg, themed.border)
        if text and text.SetTextColor then
            text:SetTextColor(unpackColor(themed.text))
        end
        if caret and caret.SetTextColor then
            caret:SetTextColor(unpackColor(themed.text))
        end
        return true
    end
    if type(legacyVisual) == "function" then
        legacyVisual()
    end
    return false
end

local function createDropdown(parent, labelText, values, key, onApply)
    local label = createStyledFontString(parent, "ARTWORK", "GameFontHighlight", 12)
    label:SetText(labelText)

    local dropdown = CreateFrame("Frame", nil, parent, "UIDropDownMenuTemplate")
    ensureSettingsWindowDropdownChrome(dropdown)

    UIDropDownMenu_Initialize(dropdown, function(_, level)
        for _, option in ipairs(values) do
            local info = UIDropDownMenu_CreateInfo()
            info.text = option.label
            info.checked = PortalAuthorityDB[key] == option.value
            info.func = function()
                PortalAuthorityDB[key] = option.value
                UIDropDownMenu_SetSelectedValue(dropdown, option.value)
                UIDropDownMenu_SetText(dropdown, option.label)
                if onApply then onApply() end
            end
            UIDropDownMenu_AddButton(info, level)
        end
    end)

    local spec = { kind = "dropdown", label = label, control = dropdown }
    spec.Refresh = function()
        local selectedLabel, selectedValue = values[1].label, values[1].value
        for _, option in ipairs(values) do
            if option.value == PortalAuthorityDB[key] then
                selectedLabel, selectedValue = option.label, option.value
                break
            end
        end
        PortalAuthorityDB[key] = selectedValue
        UIDropDownMenu_SetSelectedValue(dropdown, selectedValue)
        UIDropDownMenu_SetText(dropdown, selectedLabel)
    end

    spec.Refresh()
    return settingsSearchRuntime.registerSpec(spec)
end

local function createDynamicDropdown(parent, labelText, optionsProvider, getter, setter, onApply)
    local label = createStyledFontString(parent, "ARTWORK", "GameFontHighlight", 12)
    label:SetText(labelText)

    local dropdown = CreateFrame("Frame", nil, parent, "UIDropDownMenuTemplate")
    ensureSettingsWindowDropdownChrome(dropdown)

    local function getOptions()
        local options = optionsProvider and optionsProvider() or {}
        if #options == 0 then
            options = { { label = "None", value = "" } }
        end
        return options
    end

    local function refreshSelection(options)
        local selectedValue = getter and getter() or ""
        local selectedLabel = nil
        for _, option in ipairs(options) do
            if option.value == selectedValue then
                selectedLabel = option.label
                break
            end
        end

        if not selectedLabel then
            local first = options[1]
            selectedValue = first and first.value or ""
            selectedLabel = first and first.label or ""
            if setter then setter(selectedValue) end
        end

        UIDropDownMenu_SetSelectedValue(dropdown, selectedValue)
        UIDropDownMenu_SetText(dropdown, selectedLabel)
    end

    UIDropDownMenu_Initialize(dropdown, function(_, level)
        local options = getOptions()
        local selectedValue = getter and getter() or ""
        for _, option in ipairs(options) do
            local info = UIDropDownMenu_CreateInfo()
            info.text = option.label
            info.checked = selectedValue == option.value
            info.func = function()
                if setter then setter(option.value) end
                UIDropDownMenu_SetSelectedValue(dropdown, option.value)
                UIDropDownMenu_SetText(dropdown, option.label)
                if onApply then onApply() end
            end
            UIDropDownMenu_AddButton(info, level)
        end
    end)

    local spec = { kind = "dropdown", label = label, control = dropdown }
    spec.Refresh = function()
        refreshSelection(getOptions())
    end

    spec.Refresh()
    return settingsSearchRuntime.registerSpec(spec)
end

local function createPremiumSegmentedChoice(parent, labelText, key, choices, onApply, opts)
    opts = type(opts) == "table" and opts or {}
    local semantic = string.lower(trim(tostring(opts.semantic or "")))
    if semantic ~= "toggle" and semantic ~= "selection" then
        semantic = "selection"
    end
    local searchChoiceLabelMode = settingsSearchRuntime.normalizeChoiceLabelMode(opts.searchChoiceLabelMode)
    local selectionVariant = tostring(opts.selectionVariant or "")
    local controlHeight = math.max(16, tonumber(opts.controlHeight) or 22)
    local controlTextSize = math.max(9, tonumber(opts.textSize) or 11)
    local textEllipsis = opts.textEllipsis and true or false
    local controlMinWidth = math.max(0, tonumber(opts.minWidth) or 120)
    local segmentGap = math.max(0, tonumber(opts.segmentGap) or 4)
    local segmentMinWidth = math.max(20, tonumber(opts.segmentMinWidth) or 48)
    local normalizeValue = type(opts.normalizeValue) == "function" and opts.normalizeValue or nil
    local getValue = type(opts.getValue) == "function" and opts.getValue or nil
    local setValue = type(opts.setValue) == "function" and opts.setValue or nil

    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 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_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 label = createStyledFontString(parent, "ARTWORK", "GameFontHighlight", 12)
    label:SetText(labelText or "")
    local container = CreateFrame("Frame", nil, parent)
    local buttons = {}
    local updateButtonVisual
    local controlEnabled = true
    local choiceEnabledByValue = {}
    local function useSettingsWindowHostTheme()
        return isUsingCustomSettingsWindowVisualStyle(container)
    end

    local function setSegmentBorderColor(btn, r, g, b, a)
        if not btn or not btn._paBorder then
            return
        end
        local colorKey = string.format("%.4f:%.4f:%.4f:%.4f", tonumber(r) or 0, tonumber(g) or 0, tonumber(b) or 0, tonumber(a) or 0)
        if btn._paBorder._paLastBorderColorKey ~= colorKey then
            btn._paBorder:SetBackdropBorderColor(r, g, b, a)
            btn._paBorder._paLastBorderColorKey = colorKey
        end
        btn._paBorder:SetAlpha(1)
        btn._paBorder:Show()
    end

    local function normalizeSegmentValue(v)
        if normalizeValue then
            return normalizeValue(v)
        end
        return v
    end

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

        local n = btn.GetNormalTexture and btn:GetNormalTexture() or nil
        local p = btn.GetPushedTexture and btn:GetPushedTexture() or nil
        local h = btn.GetHighlightTexture and btn:GetHighlightTexture() or nil
        local d = btn.GetDisabledTexture and btn: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

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

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

    local function scalePressOffset(btn, value)
        local v = tonumber(value) or 0
        if v == 0 then
            return 0
        end
        if not btn or not btn.GetEffectiveScale then
            return v
        end
        local scale = btn:GetEffectiveScale() or 1
        if scale == 0 then
            return v
        end
        local absV = math.abs(v)
        if absV < 0.5 then
            absV = 0.5
        end
        local sign = (v < 0) and -1 or 1
        return (absV / scale) * sign
    end

    local function setSegmentTextOffset(btn, x, y)
        if not btn or not btn.Text then
            return
        end
        local offsetX = scalePressOffset(btn, x)
        local offsetY = scalePressOffset(btn, y)
        btn.Text:ClearAllPoints()
        btn.Text:SetPoint("CENTER", btn, "CENTER", offsetX, offsetY)
    end

    local function resolvePressTextOffsetX()
        local override = tonumber(opts.pressTextOffsetX)
        if override then
            return override
        end
        if semantic == "selection" and selectionVariant == "lock_meta" then
            return 0.30
        end
        return 0.30
    end

    local function resolvePressTextOffsetY()
        local override = tonumber(opts.pressTextOffsetY)
        if override then
            return override
        end
        if semantic == "selection" and selectionVariant == "lock_meta" then
            return -0.30
        end
        return -0.30
    end

    local PRESS_TEXT_OFFSET_X = resolvePressTextOffsetX()
    local PRESS_TEXT_OFFSET_Y = resolvePressTextOffsetY()

    local function clearSegmentPressState(selfBtn)
        if not selfBtn then
            return
        end
        selfBtn._paPressed = false
        setSegmentTextOffset(selfBtn, 0, 0)
        if useSettingsWindowHostTheme() and selfBtn.SetPushedTextOffset then
            selfBtn:SetPushedTextOffset(0, 0)
        end
    end

    updateButtonVisual = function(btn, selected)
        if not btn then return end
        local hovered = btn._paHovered and true or false
        local toggleState = btn._toggleState or "on"
        local choiceEnabled = (choiceEnabledByValue[btn._value] ~= false)
        local useHostVisuals = useSettingsWindowHostTheme()
        local useHostToggleTheme = useHostVisuals and semantic == "toggle"
        ensureFlatSkin(btn)

        if useHostToggleTheme then
            local bgColor = hovered and SETTINGS_WINDOW_HOST_COLORS.hoverBg or SETTINGS_WINDOW_HOST_COLORS.idleBg
            local borderColor = hovered and SETTINGS_WINDOW_HOST_COLORS.hoverBorder or SETTINGS_WINDOW_HOST_COLORS.idleBorder
            local textColor = hovered and SETTINGS_WINDOW_HOST_COLORS.hoverText or SETTINGS_WINDOW_HOST_COLORS.idleText

            if (not controlEnabled) or (not choiceEnabled) then
                bgColor = SETTINGS_WINDOW_HOST_COLORS.disabledBg
                borderColor = SETTINGS_WINDOW_HOST_COLORS.disabledBorder
                textColor = SETTINGS_WINDOW_HOST_COLORS.disabledText
                clearSegmentPressState(btn)
                if btn._paGlow then
                    btn._paGlow:SetVertexColor(1, 1, 1, 0)
                end
                if btn._paTopSheen then
                    btn._paTopSheen:SetAlpha(0.05)
                end
                btn:SetAlpha(controlEnabled and 0.72 or 0.58)
            elseif selected then
                if toggleState == "off" then
                    bgColor = SETTINGS_WINDOW_HOST_COLORS.offBg
                    borderColor = SETTINGS_WINDOW_HOST_COLORS.offBorder
                    textColor = SETTINGS_WINDOW_HOST_COLORS.offText
                    if btn._paGlow then
                        btn._paGlow:SetVertexColor(unpackColor(SETTINGS_WINDOW_HOST_COLORS.offBorder))
                        btn._paGlow:SetAlpha(hovered and 0.12 or 0.08)
                    end
                else
                    bgColor = SETTINGS_WINDOW_HOST_COLORS.onBg
                    borderColor = SETTINGS_WINDOW_HOST_COLORS.onBorder
                    textColor = SETTINGS_WINDOW_HOST_COLORS.onText
                    if btn._paGlow then
                        btn._paGlow:SetVertexColor(unpackColor(SETTINGS_WINDOW_HOST_COLORS.onBorder))
                        btn._paGlow:SetAlpha(hovered and 0.16 or 0.10)
                    end
                end
                if btn._paTopSheen then
                    btn._paTopSheen:SetAlpha(hovered and 0.18 or 0.12)
                end
                btn:SetAlpha(1)
            else
                if btn._paGlow then
                    btn._paGlow:SetVertexColor(1, 1, 1, 0)
                end
                if btn._paTopSheen then
                    btn._paTopSheen:SetAlpha(hovered and 0.12 or 0.08)
                end
                btn:SetAlpha(0.98)
            end

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

        if not choiceEnabled then
            clearSegmentPressState(btn)
            btn._paBg:SetVertexColor(CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A)
            if btn._paGlow then
                btn._paGlow:SetVertexColor(1, 1, 1, 0)
            end
            if btn._paTopSheen then
                btn._paTopSheen:SetAlpha(0.04)
            end
            setSegmentBorderColor(btn, CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, 0.72)
            if btn.Text then
                btn.Text:SetTextColor(0.50, 0.50, 0.53, 1)
            end
            btn:SetAlpha(controlEnabled and 0.52 or 0.45)
            return
        end

        if semantic == "toggle" then
            if selected then
                if toggleState == "on" then
                    if hovered then
                        btn._paBg:SetVertexColor(0.13, 0.28, 0.15, 0.98)
                    else
                        btn._paBg:SetVertexColor(0.11, 0.23, 0.13, 0.96)
                    end
                    if btn._paGlow then
                        btn._paGlow:SetVertexColor(0.28, 1.0, 0.36, hovered and 0.14 or 0.1)
                    end
                    if btn.Text then
                        btn.Text:SetTextColor(0.72, 1.0, 0.74, 1)
                    end
                    setSegmentBorderColor(btn, 0.18, 0.55, 0.2, hovered and 0.95 or 0.82)
                    if btn._paTopSheen then
                        btn._paTopSheen:SetAlpha(0.22)
                    end
                else
                    btn._paBg:SetVertexColor(OFF_RED_BG_R, OFF_RED_BG_G, OFF_RED_BG_B, OFF_RED_BG_A)
                    if btn._paGlow then
                        btn._paGlow:SetVertexColor(0.95, 0.22, 0.22, 0)
                    end
                    if btn.Text then
                        btn.Text:SetTextColor(OFF_RED_TEXT_R, OFF_RED_TEXT_G, OFF_RED_TEXT_B, OFF_RED_TEXT_A)
                    end
                    setSegmentBorderColor(btn, OFF_RED_BORDER_SELECTED_R, OFF_RED_BORDER_SELECTED_G, OFF_RED_BORDER_SELECTED_B, OFF_RED_BORDER_SELECTED_A)
                    if btn._paTopSheen then
                        btn._paTopSheen:SetAlpha(OFF_RED_TOPSHEEN_ALPHA)
                    end
                end
            else
                if hovered then
                    btn._paBg:SetVertexColor(CONTROL_HOVER_BG_R, CONTROL_HOVER_BG_G, CONTROL_HOVER_BG_B, CONTROL_HOVER_BG_A)
                else
                    btn._paBg:SetVertexColor(CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A)
                end
                if btn._paGlow then
                    btn._paGlow:SetVertexColor(1, 1, 1, 0)
                end
                if btn.Text then
                    btn.Text:SetTextColor(0.85, 0.85, 0.88, 1)
                end
                if hovered then
                    setSegmentBorderColor(btn, CONTROL_HOVER_BORDER_R, CONTROL_HOVER_BORDER_G, CONTROL_HOVER_BORDER_B, CONTROL_HOVER_BORDER_A)
                else
                    setSegmentBorderColor(btn, CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A)
                end
                if btn._paTopSheen then
                    btn._paTopSheen:SetAlpha(hovered and 0.12 or 0.08)
                end
            end
            if selected and toggleState ~= "on" then
                btn:SetAlpha(OFF_RED_SELECTED_ALPHA)
            else
                btn:SetAlpha(0.98)
            end
        else
            if selected then
                local useLockMetaVariant = (selectionVariant == "lock_meta")
                if useHostVisuals then
                    useLockMetaVariant = false
                end
                if hovered then
                    if useLockMetaVariant then
                        btn._paBg:SetVertexColor(0.28, 0.29, 0.33, 0.98)
                    else
                        btn._paBg:SetVertexColor(0.25, 0.26, 0.30, 0.98)
                    end
                else
                    if useLockMetaVariant then
                        btn._paBg:SetVertexColor(0.25, 0.26, 0.30, 0.96)
                    else
                        btn._paBg:SetVertexColor(0.22, 0.23, 0.27, 0.96)
                    end
                end
                if btn._paGlow then
                    if useLockMetaVariant then
                        btn._paGlow:SetVertexColor(1.0, 0.82, 0.1, 0)
                    else
                        btn._paGlow:SetVertexColor(1.0, 0.82, 0.1, hovered and 0.1 or 0.06)
                    end
                end
                if useLockMetaVariant then
                    if hovered then
                        setSegmentBorderColor(btn, 0.38, 0.41, 0.46, 0.96)
                    else
                        setSegmentBorderColor(btn, 0.34, 0.37, 0.42, 0.90)
                    end
                else
                    setSegmentBorderColor(btn, 0.95, 0.75, 0.2, hovered and 0.95 or 0.82)
                end
                if btn._paTopSheen then
                    if useLockMetaVariant then
                        btn._paTopSheen:SetAlpha(0.28)
                    else
                        btn._paTopSheen:SetAlpha(0.26)
                    end
                end
                btn:SetAlpha(1)
                if btn.Text then
                    if useLockMetaVariant then
                        btn.Text:SetTextColor(0.94, 0.95, 0.97, 1)
                    else
                        btn.Text:SetTextColor(0.99, 0.95, 0.82, 1)
                    end
                end
            else
                if hovered then
                    btn._paBg:SetVertexColor(CONTROL_HOVER_BG_R, CONTROL_HOVER_BG_G, CONTROL_HOVER_BG_B, CONTROL_HOVER_BG_A)
                else
                    btn._paBg:SetVertexColor(CONTROL_BASE_BG_R, CONTROL_BASE_BG_G, CONTROL_BASE_BG_B, CONTROL_BASE_BG_A)
                end
                if btn._paGlow then
                    btn._paGlow:SetVertexColor(1.0, 0.82, 0.1, 0)
                end
                if hovered then
                    setSegmentBorderColor(btn, CONTROL_HOVER_BORDER_R, CONTROL_HOVER_BORDER_G, CONTROL_HOVER_BORDER_B, CONTROL_HOVER_BORDER_A)
                else
                    setSegmentBorderColor(btn, CONTROL_BASE_BORDER_R, CONTROL_BASE_BORDER_G, CONTROL_BASE_BORDER_B, CONTROL_BASE_BORDER_A)
                end
                if btn._paTopSheen then
                    btn._paTopSheen:SetAlpha(hovered and 0.12 or 0.08)
                end
                btn:SetAlpha(0.96)
                if btn.Text then
                    btn.Text:SetTextColor(0.82, 0.83, 0.86, 1)
                end
            end
        end

        if not controlEnabled then
            clearSegmentPressState(btn)
            btn:SetAlpha(0.55)
            if btn.Text then
                btn.Text:SetTextColor(0.62, 0.62, 0.64, 1)
            end
            if btn._paGlow then
                btn._paGlow:SetVertexColor(1, 1, 1, 0)
            end
        end
    end

    local function applySelectionVisual(value)
        local normalizedValue = normalizeSegmentValue(value)
        for _, btn in ipairs(buttons) do
            local selected = (btn._value == normalizedValue)
            btn._selected = selected and true or false
            updateButtonVisual(btn, selected)
        end
    end

    local function setSelectedVisual(value)
        applySelectionVisual(normalizeSegmentValue(value))
    end

    local function setSelected(value, triggerApply)
        local normalizedValue = normalizeSegmentValue(value)
        if setValue then
            setValue(normalizedValue)
        elseif key ~= nil then
            PortalAuthorityDB[key] = normalizedValue
        end
        setSelectedVisual(normalizedValue)
        if triggerApply and onApply then
            onApply(normalizedValue)
        end
    end

    for i, choice in ipairs(choices or {}) do
        local choiceValue = normalizeSegmentValue(choice.value)
        local choiceLabel = choice.label
        local choiceToggleState = choice.toggleState
        local btn = CreateFrame("Button", nil, container)
        btn:SetSize(72, controlHeight)
        btn.Text = createStyledFontString(btn, "ARTWORK", "GameFontHighlightSmall", controlTextSize)
        settingsSearchRuntime.markSearchTextOrigin(btn.Text, "choice_caption", {
            searchChoiceLabelMode = searchChoiceLabelMode,
        })
        btn.Text:SetPoint("CENTER", btn, "CENTER", 0, 0)
        btn.Text:SetJustifyH("CENTER")
        btn.Text:SetJustifyV("MIDDLE")
        btn.Text:SetWordWrap(false)
        if btn.Text.SetMaxLines then
            btn.Text:SetMaxLines(1)
        end
        btn._baseLabel = tostring(choiceLabel or "")
        btn.Text:SetText(btn._baseLabel)
        applyGlobalFontToObject(btn.Text, controlTextSize)
        btn._value = choiceValue
        btn._toggleState = tostring(choiceToggleState or "on")
        btn._paHovered = false
        btn._selected = false
        btn._paPressed = false
        if choiceEnabledByValue[choiceValue] == nil then
            choiceEnabledByValue[choiceValue] = true
        end
        ensureFlatSkin(btn)
        btn:HookScript("OnShow", function(selfBtn)
            ensureFlatSkin(selfBtn)
            clearSegmentPressState(selfBtn)
            updateButtonVisual(selfBtn, selfBtn._selected and true or false)
        end)
        btn:HookScript("OnHide", function(selfBtn)
            clearSegmentPressState(selfBtn)
        end)
        btn:SetScript("OnEnter", function(selfBtn)
            if not controlEnabled or choiceEnabledByValue[selfBtn._value] == false then
                selfBtn._paHovered = false
                updateButtonVisual(selfBtn, selfBtn._selected and true or false)
                return
            end
            selfBtn._paHovered = true
            updateButtonVisual(selfBtn, selfBtn._selected and true or false)
        end)
        btn:SetScript("OnLeave", function(selfBtn)
            selfBtn._paHovered = false
            clearSegmentPressState(selfBtn)
            updateButtonVisual(selfBtn, selfBtn._selected and true or false)
        end)
        btn:SetScript("OnMouseDown", function(selfBtn)
            if not controlEnabled then
                return
            end
            if choiceEnabledByValue[selfBtn._value] == false then
                return
            end
            if not useSettingsWindowHostTheme() then
                return
            end
            selfBtn._paPressed = true
            setSegmentTextOffset(selfBtn, PRESS_TEXT_OFFSET_X, PRESS_TEXT_OFFSET_Y)
            updateButtonVisual(selfBtn, selfBtn._selected and true or false)
        end)
        btn:SetScript("OnMouseUp", function(selfBtn)
            clearSegmentPressState(selfBtn)
            updateButtonVisual(selfBtn, selfBtn._selected and true or false)
        end)
        btn:SetScript("OnClick", function()
            if not controlEnabled then
                return
            end
            if choiceEnabledByValue[choiceValue] == false then
                return
            end
            if btn._selected then
                return
            end
            setSelected(choiceValue, true)
        end)
        buttons[i] = btn
    end

    local spec = { kind = "segmented", label = label, control = container, buttons = buttons }
    spec.Layout = function(_, width)
        local count = #buttons
        if count <= 0 then
            container:SetSize(width or 0, controlHeight)
            return
        end
        local gap = segmentGap
        local w = math.max(controlMinWidth, tonumber(width) or controlMinWidth)
        local btnW = math.max(segmentMinWidth, (w - ((count - 1) * gap)) / count)
        local x = 0
        container:SetSize(w, controlHeight)
        for _, btn in ipairs(buttons) do
            btn:ClearAllPoints()
            btn:SetPoint("TOPLEFT", container, "TOPLEFT", x, 0)
            btn:SetSize(btnW, controlHeight)
            if btn.Text then
                btn.Text:ClearAllPoints()
                btn.Text:SetPoint("CENTER", btn, "CENTER", 0, 0)
                btn.Text:SetJustifyH("CENTER")
                btn.Text:SetJustifyV("MIDDLE")
                btn.Text:SetWidth(math.max(1, btnW - 8))
                if textEllipsis and type(setSingleLineEllipsis) == "function" then
                    setSingleLineEllipsis(btn.Text, btn._baseLabel or "", math.max(1, btnW - 8))
                else
                    btn.Text:SetText(btn._baseLabel or "")
                end
            end
            updateButtonVisual(btn, btn._selected and true or false)
            x = x + btnW + gap
        end
    end
    spec.Refresh = function()
        local selectedRaw
        if getValue then
            selectedRaw = getValue()
        elseif key ~= nil then
            selectedRaw = PortalAuthorityDB[key]
        end
        local selected = normalizeSegmentValue(selectedRaw)
        local valid = false
        local function isChoiceValue(candidate)
            local normalizedCandidate = normalizeSegmentValue(candidate)
            for _, choice in ipairs(choices or {}) do
                if normalizeSegmentValue(choice.value) == normalizedCandidate then
                    return true
                end
            end
            return false
        end
        for _, choice in ipairs(choices or {}) do
            if normalizeSegmentValue(choice.value) == selected then
                valid = true
                break
            end
        end
        if not valid then
            local defaults = (PortalAuthority and PortalAuthority.defaults) or {}
            local fallback = defaults[key]
            if isChoiceValue(fallback) then
                selected = normalizeSegmentValue(fallback)
            else
                selected = choices and choices[1] and normalizeSegmentValue(choices[1].value) or nil
            end
            if selected ~= nil then
                if setValue then
                    setValue(selected)
                elseif key ~= nil then
                    PortalAuthorityDB[key] = selected
                end
            end
        end
        if selected ~= nil then
            setSelectedVisual(selected)
        end
    end
    spec.SetVisualSelectedValue = function(_, value)
        setSelectedVisual(value)
    end
    spec.GetSelectedValue = function()
        for _, btn in ipairs(buttons) do
            if btn and btn._selected then
                return btn._value
            end
        end
        if getValue then
            return normalizeSegmentValue(getValue())
        end
        if key ~= nil then
            return normalizeSegmentValue(PortalAuthorityDB[key])
        end
        return nil
    end
    spec.SetSelectedValue = function(_, value, triggerApply)
        setSelected(value, triggerApply and true or false)
    end
    spec.SetChoiceEnabled = function(_, value, enabled)
        local normalizedValue = normalizeSegmentValue(value)
        choiceEnabledByValue[normalizedValue] = enabled and true or false
        for _, btn in ipairs(buttons) do
            if btn._value == normalizedValue then
                btn._paHovered = false
                clearSegmentPressState(btn)
            end
            updateButtonVisual(btn, btn._selected and true or false)
        end
    end
    spec.IsChoiceEnabled = function(_, value)
        local normalizedValue = normalizeSegmentValue(value)
        return choiceEnabledByValue[normalizedValue] ~= false
    end
    spec.SetEnabled = function(_, enabled)
        controlEnabled = enabled and true or false
        for _, btn in ipairs(buttons) do
            btn:SetEnabled(controlEnabled)
            clearSegmentPressState(btn)
        end
        local selected = nil
        for _, btn in ipairs(buttons) do
            if btn._selected then
                selected = btn._value
                break
            end
        end
        if selected ~= nil then
            setSelectedVisual(selected)
        else
            spec.Refresh()
        end
        label:SetAlpha(controlEnabled and 1 or 0.65)
    end
    spec.IsEnabled = function()
        return controlEnabled
    end
    spec.RefreshVisual = function()
        for _, btn in ipairs(buttons) do
            updateButtonVisual(btn, btn._selected and true or false)
        end
        label:SetAlpha(controlEnabled and 1 or 0.65)
    end
    registerSettingsWindowVisualRefresh(container, function()
        spec:RefreshVisual()
    end)
    spec.Refresh()
    return settingsSearchRuntime.registerSpec(spec, semantic == "toggle" and "toggle" or spec.kind)
end

ACTIVE_PORTAL_PICKER = nil

local function CreatePicker(parent, options, onSelectCallback, uiConfig)
    uiConfig = type(uiConfig) == "table" and uiConfig or {}
    local preserveOrder = uiConfig.preserveOrder and true or false
    local noSearch = uiConfig.noSearch and true or false
    local hideTitle = uiConfig.hideTitle and true or false
    local hideScrollBar = uiConfig.hideScrollBar and true or false
    local showClearButton = uiConfig.showClearButton ~= false
    local showCloseButton = uiConfig.showCloseButton ~= false
    local footerFlowWithRows = uiConfig.footerFlowWithRows and true or false
    local onOptionRightClick = type(uiConfig.onOptionRightClick) == "function" and uiConfig.onOptionRightClick or nil
    local footerHintText = tostring(uiConfig.footerHint or "")
    local minPickerHeight = math.max(120, math.floor(tonumber(uiConfig.minHeight) or 220))

    local picker = CreateFrame("Frame", nil, parent or UIParent, "BackdropTemplate")
    picker:SetFrameStrata("DIALOG")
    picker:SetToplevel(true)
    picker:SetClampedToScreen(true)
    picker:EnableMouseWheel(true)
    picker:SetSize(
        math.max(260, math.floor(tonumber(uiConfig.width) or 520)),
        math.max(minPickerHeight, math.floor(tonumber(uiConfig.height) or 420))
    )
    picker:Hide()
    picker:SetBackdrop({
        bgFile = "Interface\\Buttons\\WHITE8x8",
        edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
        edgeSize = 12,
        insets = { left = 3, right = 3, top = 3, bottom = 3 },
    })
    picker:SetBackdropColor(0.05, 0.06, 0.08, 0.98)
    picker:SetBackdropBorderColor(0.32, 0.35, 0.4, 1)

    local title = createStyledFontString(picker, "ARTWORK", "GameFontHighlight", 12)
    title:SetText(tostring(uiConfig.title or "Select Destination"))
    title:SetPoint("TOPLEFT", picker, "TOPLEFT", 16, -12)

    local search = CreateFrame("EditBox", nil, picker, "InputBoxTemplate")
    search:SetAutoFocus(false)
    search:SetTextInsets(8, 8, 0, 0)
    search:SetTextColor(1, 1, 1, 1)
    search:SetHighlightColor(1, 1, 1, 0.25)
    search:SetPoint("TOPLEFT", picker, "TOPLEFT", 14, -34)
    search:SetPoint("TOPRIGHT", picker, "TOPRIGHT", -14, -34)
    search:SetHeight(22)
    search:EnableMouseWheel(true)
    applyGlobalFontToObject(search, 12)

    local searchHint = createStyledFontString(picker, "ARTWORK", "GameFontHighlightSmall", 11)
    searchHint:SetText(tostring(uiConfig.searchHint or "Search destinations (name, short, spell ID)"))
    searchHint:SetTextColor(0.72, 0.72, 0.72, 1)
    searchHint:SetPoint("TOPLEFT", search, "BOTTOMLEFT", 2, -4)

    local footerHint = createStyledFontString(picker, "ARTWORK", "GameFontHighlightSmall", 10)
    footerHint:SetJustifyH("LEFT")
    footerHint:SetTextColor(0.72, 0.72, 0.72, 0.92)
    footerHint:SetWordWrap(false)
    if footerHint.SetMaxLines then
        footerHint:SetMaxLines(1)
    end
    footerHint:SetText(footerHintText)
    if footerHintText == "" then
        footerHint:Hide()
    else
        footerHint:Show()
    end

    local scroll = CreateFrame("ScrollFrame", nil, picker, "UIPanelScrollFrameTemplate")
    local scrollTopOffset = -72
    if noSearch and hideTitle then
        scrollTopOffset = -14
    elseif noSearch then
        scrollTopOffset = -34
    end
    local scrollChildRightInset = hideScrollBar and 4 or 20
    local scrollRightInset = hideScrollBar and 14 or 34
    scroll:SetPoint("TOPLEFT", picker, "TOPLEFT", 14, scrollTopOffset)
    scroll:SetPoint("BOTTOMRIGHT", picker, "BOTTOMRIGHT", -scrollRightInset, 48)
    scroll:EnableMouseWheel(true)
    if hideScrollBar and scroll.ScrollBar then
        scroll.ScrollBar:Hide()
        scroll.ScrollBar:SetAlpha(0)
        scroll.ScrollBar:EnableMouse(false)
        scroll.ScrollBar:SetScript("OnShow", function(selfBar)
            selfBar:Hide()
        end)
    end

    local scrollChild = CreateFrame("Frame", nil, scroll)
    scrollChild:SetSize(1, 1)
    scroll:SetScrollChild(scrollChild)
    scroll:SetScript("OnSizeChanged", function(selfScroll, width)
        local childWidth = math.max(1, math.floor((width or 1) - scrollChildRightInset))
        scrollChild:SetWidth(childWidth)
        if picker and picker.RenderRows then
            picker:RenderRows()
        end
    end)
    scrollChild:SetWidth(math.max(1, math.floor((scroll:GetWidth() or 1) - scrollChildRightInset)))

    local clearButton = createActionButton(picker, tostring(uiConfig.clearButtonText or "Clear"), nil, 80)
    clearButton:SetPoint("BOTTOMLEFT", picker, "BOTTOMLEFT", 14, 14)
    clearButton:EnableMouseWheel(true)
    local closeButton = createActionButton(picker, tostring(uiConfig.closeButtonText or "Close"), nil, 80)
    closeButton:SetPoint("BOTTOMRIGHT", picker, "BOTTOMRIGHT", -14, 14)
    closeButton:EnableMouseWheel(true)

    if not showClearButton then
        clearButton:Hide()
    end
    if not showCloseButton then
        closeButton:Hide()
    end

    local function applyPickerFooterButtonStyle(btn)
        if not btn then
            return
        end
        if not btn._paPickerFooterStyled then
            btn._paPickerFooterStyled = true
            btn._paBg = btn:CreateTexture(nil, "ARTWORK")
            btn._paBg:SetAllPoints(btn)
            btn._paBg:SetTexture("Interface\\Buttons\\WHITE8x8")
            btn._paBg:SetVertexColor(0.11, 0.12, 0.15, 0.95)
            btn._paHovered = false
            btn._paPressed = false

            if btn.Text then
                applyGlobalFontToObject(btn.Text, 11)
                btn.Text:SetTextColor(0.92, 0.92, 0.92, 1)
            end
        end

        local function hideTemplateChrome()
            local n = btn.GetNormalTexture and btn:GetNormalTexture() or nil
            local p = btn.GetPushedTexture and btn:GetPushedTexture() or nil
            local h = btn.GetHighlightTexture and btn:GetHighlightTexture() or nil
            local d = btn.GetDisabledTexture and btn: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 = btn[part]
                if tex and tex.SetAlpha then tex:SetAlpha(0) end
                if tex and tex.Hide then tex:Hide() end
            end
        end

        local function refreshVisual()
            hideTemplateChrome()
            if not btn:IsEnabled() then
                btn._paBg:SetVertexColor(0.08, 0.09, 0.11, 0.7)
                if btn.Text then
                    btn.Text:SetTextColor(0.55, 0.55, 0.58, 1)
                end
                return
            end

            if btn._paPressed then
                btn._paBg:SetVertexColor(0.08, 0.09, 0.12, 0.98)
                if btn.Text then
                    btn.Text:SetTextColor(1, 0.85, 0.18, 1)
                end
            elseif btn._paHovered then
                btn._paBg:SetVertexColor(0.14, 0.15, 0.19, 0.98)
                if btn.Text then
                    btn.Text:SetTextColor(1, 0.9, 0.35, 1)
                end
            else
                btn._paBg:SetVertexColor(0.11, 0.12, 0.15, 0.95)
                if btn.Text then
                    btn.Text:SetTextColor(0.92, 0.92, 0.92, 1)
                end
            end
        end

        btn:HookScript("OnEnter", function(selfBtn)
            selfBtn._paHovered = true
            refreshVisual()
        end)
        btn:HookScript("OnLeave", function(selfBtn)
            selfBtn._paHovered = false
            selfBtn._paPressed = false
            refreshVisual()
        end)
        btn:HookScript("OnMouseDown", function(selfBtn)
            selfBtn._paPressed = true
            refreshVisual()
        end)
        btn:HookScript("OnMouseUp", function(selfBtn)
            selfBtn._paPressed = false
            refreshVisual()
        end)
        btn:HookScript("OnShow", function()
            refreshVisual()
        end)
        refreshVisual()
    end

    applyPickerFooterButtonStyle(clearButton)
    applyPickerFooterButtonStyle(closeButton)

    picker.search = search
    picker.scroll = scroll
    picker.scrollChild = scrollChild
    picker.title = title
    picker.searchHint = searchHint
    picker.footerHint = footerHint
    picker.clearButton = clearButton
    picker.closeButton = closeButton
    picker._onSelect = onSelectCallback
    picker._allOptions = {}
    picker._filteredOptions = {}
    picker._rows = {}
    picker._selectedValue = nil
    picker._highlightedIndex = 0
    picker._query = ""
    picker._showSecondary = uiConfig.showSecondary ~= false
    picker._useOptionFontPreview = uiConfig.useOptionFontPreview and true or false
    picker._useOptionTexturePreview = uiConfig.useOptionTexturePreview and true or false
    picker._previewFontSize = math.max(8, math.floor(tonumber(uiConfig.previewFontSize) or 12))
    picker._rowHeight = math.max(20, math.floor(tonumber(uiConfig.rowHeight) or (picker._showSecondary and 34 or 26)))
    picker._minHeight = minPickerHeight
    picker._filterToken = 0
    picker._noSearch = noSearch
    picker._onOptionRightClick = onOptionRightClick
    picker._footerPulseActive = false
    picker._footerPulseElapsed = 0
    picker._footerPulseCycles = 2
    picker._footerPulseCycleDuration = 0.65
    picker._footerPulseMinAlpha = 0.78
    picker._footerPulseMaxAlpha = 1.00
    picker._footerHintIdleAlpha = 0.95
    picker._previewFlashDuration = 0.24
    picker._hasPreviewFlash = false
    picker._transientTickerActive = false
    picker._anchorFrame = nil
    picker._anchorHideHookFrames = {}
    picker._showClearButton = showClearButton
    picker._showCloseButton = showCloseButton
    picker._footerFlowWithRows = footerFlowWithRows
    picker._scrollTopOffset = scrollTopOffset
    picker._scrollRightInset = scrollRightInset
    picker._scrollChildRightInset = scrollChildRightInset
    picker._ellipsizeRows = uiConfig.ellipsizeRows and true or false

    if hideTitle then
        title:SetText("")
        title:Hide()
    else
        title:Show()
    end
    if noSearch then
        search:SetText("")
        search:ClearFocus()
        search:Hide()
        searchHint:Hide()
    else
        search:Show()
        searchHint:Show()
    end

    function picker:LayoutChrome(contentHeight)
        local footerVisible = footerHint and footerHint:IsShown()
        local topOffset = self._scrollTopOffset or -72
        local rightInset = self._scrollRightInset or 34
        local footerGap = 8
        local bottomPad = 14
        local footerHeight = footerVisible and math.max(10, footerHint:GetStringHeight() or 0) or 0
        local hasFooterButtons = self._showClearButton or self._showCloseButton

        scroll:ClearAllPoints()
        scroll:SetPoint("TOPLEFT", picker, "TOPLEFT", 14, topOffset)
        scroll:SetPoint("TOPRIGHT", picker, "TOPRIGHT", -rightInset, topOffset)

        if footerVisible then
            footerHint:ClearAllPoints()
        end

        if footerVisible and self._footerFlowWithRows and not hasFooterButtons then
            local pickerHeight = picker:GetHeight() or minPickerHeight
            local topInset = math.abs(topOffset)
            local maxListHeight = math.max(self._rowHeight, pickerHeight - topInset - bottomPad - footerGap - footerHeight)
            local usedListHeight = math.max(self._rowHeight, math.min(contentHeight or maxListHeight, maxListHeight))
            scroll:SetHeight(usedListHeight)
            footerHint:SetPoint("TOPLEFT", picker, "TOPLEFT", 14, topOffset - usedListHeight - footerGap)
            footerHint:SetPoint("TOPRIGHT", picker, "TOPRIGHT", -14, topOffset - usedListHeight - footerGap)
        else
            local bottomOffset = 14
            if hasFooterButtons then
                bottomOffset = 48
            elseif footerVisible then
                bottomOffset = 32
            end
            scroll:SetPoint("BOTTOMRIGHT", picker, "BOTTOMRIGHT", -rightInset, bottomOffset)
            if footerVisible then
                local footerBottom = hasFooterButtons and 39 or 14
                footerHint:SetPoint("BOTTOMLEFT", picker, "BOTTOMLEFT", 14, footerBottom)
                footerHint:SetPoint("BOTTOMRIGHT", picker, "BOTTOMRIGHT", -14, footerBottom)
            end
        end
    end

    local function clearTable(t)
        for k in pairs(t) do
            t[k] = nil
        end
    end

    local function lowerString(text)
        return string.lower(tostring(text or ""))
    end

    local function trimString(text)
        if type(text) ~= "string" then
            return ""
        end
        local cleaned = text:gsub("^%s+", ""):gsub("%s+$", "")
        return cleaned
    end

    local function minScore(current, nextScore)
        if nextScore == nil then
            return current
        end
        if current == nil or nextScore < current then
            return nextScore
        end
        return current
    end

    local function textMatchScore(haystack, needle, prefixScore, substringScore)
        if haystack == nil or haystack == "" or needle == nil or needle == "" then
            return nil
        end
        if haystack:sub(1, #needle) == needle then
            return prefixScore
        end
        if haystack:find(needle, 1, true) then
            return substringScore
        end
        return nil
    end

    local function highlightMatch(text, query)
        text = tostring(text or "")
        query = tostring(query or "")
        if query == "" then
            return text
        end
        local lowerText = lowerString(text)
        local startPos = lowerText:find(query, 1, true)
        if not startPos then
            return text
        end
        local endPos = startPos + #query - 1
        return string.format("%s|cffffd100%s|r%s", text:sub(1, startPos - 1), text:sub(startPos, endPos), text:sub(endPos + 1))
    end

    local function scoreOption(option, query)
        if query == "" then
            return 0
        end
        local best = nil
        best = minScore(best, textMatchScore(option._labelLower, query, 0, 20))
        best = minScore(best, textMatchScore(option._secondaryLower, query, 30, 45))
        for _, alias in ipairs(option._aliasesLower) do
            best = minScore(best, textMatchScore(alias, query, 10, 25))
        end
        return best
    end

    picker.rowPool = CreateFramePool("Button", scrollChild, nil, function(_, row)
        row:Hide()
        row:ClearAllPoints()
        row._index = nil
        row._option = nil
        row._hovered = false
        row._isNoMatch = false
        row._disabled = false
        row._previewFlashUntil = nil
        row:EnableMouse(true)
        row:SetAlpha(1)
        if row.primary then
            row.primary:SetText("")
            row.primary:SetTextColor(1, 1, 1, 1)
            applyGlobalFontToObject(row.primary, picker._previewFontSize)
        end
        if row.secondary then
            row.secondary:SetText("")
            row.secondary:SetTextColor(0.72, 0.72, 0.72, 1)
            if picker._showSecondary then
                row.secondary:Show()
            else
                row.secondary:Hide()
            end
        end
        if row.previewFrame then
            row.previewFrame:Hide()
        end
        if row.bg then
            row.bg:SetVertexColor(1, 1, 1, 0)
        end
    end)

    local function updateRowVisual(row)
        if not row or not row.bg then
            return
        end
        local now = (GetTime and GetTime()) or 0
        if row._previewFlashUntil and now < row._previewFlashUntil then
            local flashDuration = picker._previewFlashDuration or 0.24
            local remaining = math.max(0, row._previewFlashUntil - now)
            local t = (flashDuration > 0) and (remaining / flashDuration) or 0
            local alpha = 0.10 + (0.18 * t)
            row.bg:SetVertexColor(1.00, 0.82, 0.20, alpha)
            return
        elseif row._previewFlashUntil then
            row._previewFlashUntil = nil
        end
        if row._isNoMatch then
            row.bg:SetVertexColor(1, 1, 1, 0)
            return
        end
        if row._disabled then
            if row._hovered then
                row.bg:SetVertexColor(1, 1, 1, 0.06)
            else
                row.bg:SetVertexColor(1, 1, 1, 0)
            end
            return
        end
        local isSelected = row._index == picker._highlightedIndex
        if isSelected then
            row.bg:SetVertexColor(0.18, 0.55, 1, 0.25)
        elseif row._hovered then
            row.bg:SetVertexColor(1, 1, 1, 0.12)
        else
            row.bg:SetVertexColor(1, 1, 1, 0)
        end
    end

    local function refreshAllRowVisuals()
        for _, row in pairs(picker._rows) do
            updateRowVisual(row)
        end
    end

    local function setFooterHintAlpha(alpha)
        if not footerHint or not footerHint:IsShown() then
            return
        end
        footerHint:SetTextColor(0.72, 0.72, 0.72, alpha or 0.92)
    end

    local function stopFooterPulse()
        picker._footerPulseActive = false
        picker._footerPulseElapsed = 0
        setFooterHintAlpha(picker._footerHintIdleAlpha or 0.95)
    end

    local function ensureTransientTicker()
        if picker._transientTickerActive then
            return
        end
        picker._transientTickerActive = true
        picker:SetScript("OnUpdate", function(selfPicker, elapsed)
            local keepTicking = false

            if selfPicker._footerPulseActive then
                local cycleDuration = selfPicker._footerPulseCycleDuration or 0.65
                local totalCycles = selfPicker._footerPulseCycles or 2
                local totalDuration = math.max(0.01, cycleDuration * totalCycles)
                selfPicker._footerPulseElapsed = (selfPicker._footerPulseElapsed or 0) + (elapsed or 0)
                if selfPicker._footerPulseElapsed >= totalDuration then
                    stopFooterPulse()
                else
                    local inCycle = (selfPicker._footerPulseElapsed % cycleDuration) / cycleDuration
                    local wave = 0.5 - (0.5 * math.cos(inCycle * (math.pi * 2)))
                    local minAlpha = selfPicker._footerPulseMinAlpha or 0.78
                    local maxAlpha = selfPicker._footerPulseMaxAlpha or 1.00
                    local alpha = minAlpha + ((maxAlpha - minAlpha) * wave)
                    setFooterHintAlpha(alpha)
                    keepTicking = true
                end
            end

            if selfPicker._hasPreviewFlash then
                local now = (GetTime and GetTime()) or 0
                local anyFlashActive = false
                for _, row in pairs(selfPicker._rows) do
                    if row and row._previewFlashUntil and now < row._previewFlashUntil then
                        anyFlashActive = true
                        break
                    end
                end
                refreshAllRowVisuals()
                if anyFlashActive then
                    keepTicking = true
                else
                    selfPicker._hasPreviewFlash = false
                end
            end

            if not keepTicking then
                selfPicker._transientTickerActive = false
                selfPicker:SetScript("OnUpdate", nil)
            end
        end)
    end

    local function ensureAnchorHideHook(anchorFrame)
        if not anchorFrame or not anchorFrame.HookScript then
            return
        end
        if picker._anchorHideHookFrames[anchorFrame] then
            return
        end
        picker._anchorHideHookFrames[anchorFrame] = true
        anchorFrame:HookScript("OnHide", function(hiddenAnchor)
            if picker._anchorFrame ~= hiddenAnchor then
                return
            end
            if picker and picker.IsShown and picker:IsShown() and picker.Close then
                picker:Close()
            end
        end)
    end

    local PICKER_TEXTURE_PREVIEW_WIDTH = 92
    local PICKER_TEXTURE_PREVIEW_HEIGHT = 12

    local function layoutPickerRowContent(row, hasTexturePreview)
        if not row or not row.primary or not row.secondary then
            return
        end

        row.primary:ClearAllPoints()
        row.secondary:ClearAllPoints()

        if hasTexturePreview and row.previewFrame then
            row.previewFrame:ClearAllPoints()
            row.previewFrame:SetPoint("RIGHT", row, "RIGHT", -8, 0)
            row.previewFrame:SetSize(PICKER_TEXTURE_PREVIEW_WIDTH, PICKER_TEXTURE_PREVIEW_HEIGHT)
            row.previewFrame:Show()
            row.primary:SetPoint("TOPLEFT", row, "TOPLEFT", 8, picker._showSecondary and -4 or -5)
            row.primary:SetPoint("TOPRIGHT", row.previewFrame, "TOPLEFT", -8, picker._showSecondary and -4 or -5)
        else
            if row.previewFrame then
                row.previewFrame:Hide()
            end
            row.primary:SetPoint("TOPLEFT", row, "TOPLEFT", 8, picker._showSecondary and -4 or -5)
            row.primary:SetPoint("TOPRIGHT", row, "TOPRIGHT", -8, picker._showSecondary and -4 or -5)
        end

        row.secondary:SetPoint("TOPLEFT", row.primary, "BOTTOMLEFT", 0, -2)
        if hasTexturePreview and row.previewFrame then
            row.secondary:SetPoint("TOPRIGHT", row.previewFrame, "TOPLEFT", -8, -2)
        else
            row.secondary:SetPoint("TOPRIGHT", row, "TOPRIGHT", -8, -2)
        end
    end

    local function ensureRowWidgets(row)
        if row._paBuilt then
            return
        end
        row._paBuilt = true
        row:SetHeight(picker._rowHeight)

        row.bg = row:CreateTexture(nil, "BACKGROUND")
        row.bg:SetAllPoints()
        row.bg:SetTexture("Interface\\Buttons\\WHITE8x8")
        row.bg:SetVertexColor(1, 1, 1, 0)

        row.primary = createStyledFontString(row, "ARTWORK", "GameFontHighlight", 12)
        row.primary:SetPoint("TOPLEFT", row, "TOPLEFT", 8, picker._showSecondary and -4 or -5)
        row.primary:SetPoint("TOPRIGHT", row, "TOPRIGHT", -8, picker._showSecondary and -4 or -5)
        row.primary:SetJustifyH("LEFT")
        row.primary:SetMaxLines(1)

        row.secondary = createStyledFontString(row, "ARTWORK", "GameFontHighlightSmall", 10)
        row.secondary:SetPoint("TOPLEFT", row.primary, "BOTTOMLEFT", 0, -2)
        row.secondary:SetPoint("TOPRIGHT", row, "TOPRIGHT", -8, -2)
        row.secondary:SetJustifyH("LEFT")
        row.secondary:SetTextColor(0.72, 0.72, 0.72, 1)
        row.secondary:SetMaxLines(1)
        if not picker._showSecondary then
            row.secondary:Hide()
        end

        row.previewFrame = CreateFrame("Frame", nil, row, "BackdropTemplate")
        row.previewFrame:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8x8",
            edgeFile = "Interface\\Buttons\\WHITE8x8",
            edgeSize = 1,
            insets = { left = 0, right = 0, top = 0, bottom = 0 },
        })
        row.previewFrame:EnableMouse(false)
        row.previewFrame:SetBackdropColor(0.09, 0.10, 0.12, 0.98)
        row.previewFrame:SetBackdropBorderColor(0.25, 0.28, 0.33, 0.92)
        row.previewFrame:Hide()

        row.previewBar = CreateFrame("StatusBar", nil, row.previewFrame)
        row.previewBar:EnableMouse(false)
        row.previewBar:SetPoint("TOPLEFT", row.previewFrame, "TOPLEFT", 1, -1)
        row.previewBar:SetPoint("BOTTOMRIGHT", row.previewFrame, "BOTTOMRIGHT", -1, 1)
        row.previewBar:SetMinMaxValues(0, 1)
        row.previewBar:SetValue(0.72)
        row.previewBar:SetStatusBarColor(1.00, 0.82, 0.20, 0.95)
        if row.previewBar.SetReverseFill then
            row.previewBar:SetReverseFill(false)
        end

        row.previewBarBg = row.previewBar:CreateTexture(nil, "BACKGROUND")
        row.previewBarBg:SetAllPoints(row.previewBar)
        row.previewBarBg:SetTexture("Interface\\Buttons\\WHITE8x8")
        row.previewBarBg:SetVertexColor(0.16, 0.18, 0.22, 0.92)

        layoutPickerRowContent(row, false)

        row:SetScript("OnEnter", function(selfRow)
            selfRow._hovered = true
            if not selfRow._isNoMatch and not selfRow._disabled then
                picker._highlightedIndex = selfRow._index or picker._highlightedIndex
            end
            refreshAllRowVisuals()
        end)
        row:SetScript("OnLeave", function(selfRow)
            selfRow._hovered = false
            refreshAllRowVisuals()
        end)
        row:RegisterForClicks("LeftButtonUp", "RightButtonUp")
        row:SetScript("OnClick", function(selfRow, mouseButton)
            if selfRow._isNoMatch then
                return
            end
            local option = selfRow._option
            if not option or option.disabled then
                return
            end
            if mouseButton == "RightButton" and picker._onOptionRightClick then
                if picker._onOptionRightClick then
                    picker._onOptionRightClick(option.value, option, selfRow)
                end
                local now = (GetTime and GetTime()) or 0
                selfRow._previewFlashUntil = now + (picker._previewFlashDuration or 0.24)
                picker._hasPreviewFlash = true
                refreshAllRowVisuals()
                ensureTransientTicker()
                return
            end
            if picker._onSelect and option then
                picker._onSelect(option.value, option, false)
            end
            picker:Close()
        end)
    end

    function picker:SetOnSelect(callback)
        self._onSelect = callback
    end

    function picker:SetSelectedValue(value)
        if value == nil then
            self._selectedValue = nil
            self._selectedValueKey = nil
        else
            self._selectedValue = value
            self._selectedValueKey = tostring(value)
        end
    end

    function picker:RenderRows()
        self.rowPool:ReleaseAll()
        clearTable(self._rows)

        local rowHeight = self._rowHeight
        local y = 0
        local query = self._query

        if #self._filteredOptions == 0 then
            local row = self.rowPool:Acquire()
            ensureRowWidgets(row)
            row:SetPoint("TOPLEFT", self.scrollChild, "TOPLEFT", 0, -y)
            row:SetPoint("TOPRIGHT", self.scrollChild, "TOPRIGHT", 0, -y)
            row:SetHeight(rowHeight)
            row._isNoMatch = true
            row._disabled = false
            row._index = 0
            row.primary:SetText("|cff9a9a9aNo matches|r")
            row.secondary:SetText("")
            layoutPickerRowContent(row, false)
            row:EnableMouse(false)
            row:Show()
            self._rows[1] = row
            y = y + rowHeight
        else
            for index, option in ipairs(self._filteredOptions) do
                local row = self.rowPool:Acquire()
                ensureRowWidgets(row)
                row:SetPoint("TOPLEFT", self.scrollChild, "TOPLEFT", 0, -y)
                row:SetPoint("TOPRIGHT", self.scrollChild, "TOPRIGHT", 0, -y)
                row:SetHeight(rowHeight)
                row._isNoMatch = false
                row._disabled = option.disabled and true or false
                row._index = index
                row._option = option
                if picker._showSecondary then
                    row.secondary:SetText(option.secondary or "")
                    row.secondary:Show()
                else
                    row.secondary:SetText("")
                    row.secondary:Hide()
                end
                if picker._useOptionFontPreview then
                    local previewPath = type(option.previewFontPath) == "string" and option.previewFontPath or ""
                    local applied = false
                    if row.primary.SetFont then
                        applied = row.primary:SetFont(previewPath, picker._previewFontSize, "")
                    end
                    if not applied then
                        applyGlobalFontToObject(row.primary, picker._previewFontSize)
                    end
                else
                    applyGlobalFontToObject(row.primary, 12)
                end
                local hasTexturePreview = picker._useOptionTexturePreview
                    and type(option.previewTexturePath) == "string"
                    and option.previewTexturePath ~= ""
                layoutPickerRowContent(row, hasTexturePreview)
                if picker._ellipsizeRows and query == "" and type(setSingleLineEllipsis) == "function" then
                    local previewInset = hasTexturePreview and (PICKER_TEXTURE_PREVIEW_WIDTH + 8) or 0
                    local primaryWidth = math.max(20, (self.scrollChild:GetWidth() or 0) - 16 - previewInset)
                    row.primary:SetWidth(primaryWidth)
                    setSingleLineEllipsis(row.primary, option.label or "", primaryWidth)
                else
                    row.primary:SetText(highlightMatch(option.label, query))
                end
                if hasTexturePreview and row.previewBar and row.previewBarBg then
                    row.previewBar:SetStatusBarTexture(option.previewTexturePath)
                    row.previewBarBg:SetTexture(option.previewTexturePath)
                    row.previewBar:SetMinMaxValues(0, 1)
                    row.previewBar:SetValue(0.72)
                    row.previewBar:SetStatusBarColor(1.00, 0.82, 0.20, 0.95)
                    row.previewBarBg:SetVertexColor(0.16, 0.18, 0.22, 0.92)
                    if row.previewBar.SetReverseFill then
                        row.previewBar:SetReverseFill(false)
                    end
                end
                row:EnableMouse(true)
                if row._disabled then
                    row:SetAlpha(0.72)
                    row.primary:SetTextColor(0.6, 0.6, 0.6, 1)
                    if picker._showSecondary then
                        row.secondary:SetTextColor(0.5, 0.5, 0.5, 1)
                    end
                else
                    row:SetAlpha(1)
                    row.primary:SetTextColor(1, 1, 1, 1)
                    if picker._showSecondary then
                        row.secondary:SetTextColor(0.72, 0.72, 0.72, 1)
                    end
                end
                row:Show()
                self._rows[index] = row
                y = y + rowHeight
            end
        end

        self:LayoutChrome(y)
        self.scrollChild:SetHeight(math.max(self.scroll:GetHeight(), y))

        refreshAllRowVisuals()
    end

    function picker:EnsureVisible(index)
        local count = #self._filteredOptions
        if count == 0 or index < 1 or index > count then
            return
        end
        local rowHeight = self._rowHeight
        local top = (index - 1) * rowHeight
        local bottom = top + rowHeight
        local current = self.scroll:GetVerticalScroll()
        local viewport = self.scroll:GetHeight()
        if top < current then
            self.scroll:SetVerticalScroll(top)
        elseif bottom > current + viewport then
            self.scroll:SetVerticalScroll(bottom - viewport)
        end
    end

    function picker:MoveHighlight(delta)
        local count = #self._filteredOptions
        if count == 0 then
            return
        end
        local index = self._highlightedIndex
        if index < 1 then
            index = (delta > 0) and 0 or (count + 1)
        end

        local newIndex = index
        while true do
            newIndex = newIndex + delta
            if newIndex < 1 or newIndex > count then
                break
            end
            local option = self._filteredOptions[newIndex]
            if option and not option.disabled then
                self._highlightedIndex = newIndex
                self:EnsureVisible(newIndex)
                break
            end
        end
        refreshAllRowVisuals()
    end

    function picker:SelectHighlighted()
        local count = #self._filteredOptions
        if count == 0 then
            return
        end
        local index = self._highlightedIndex
        if index < 1 or index > count then
            index = 1
        end
        local option = self._filteredOptions[index]
        if option and option.disabled then
            option = nil
            for i = 1, count do
                local candidate = self._filteredOptions[i]
                if candidate and not candidate.disabled then
                    option = candidate
                    index = i
                    break
                end
            end
        end
        if option and self._onSelect then
            self._highlightedIndex = index
            self._onSelect(option.value, option, false)
            self:Close()
        end
    end

    function picker:ApplyFilter(opts)
        opts = type(opts) == "table" and opts or {}
        local query = lowerString(trimString(self.search:GetText() or ""))
        self._query = query
        local preserveScroll = opts.preserveScroll and true or false
        local previousScroll = preserveScroll and (tonumber(self.scroll:GetVerticalScroll()) or 0) or nil
        local previousHighlightedValueKey = nil
        if preserveScroll and self._highlightedIndex and self._highlightedIndex > 0 then
            local previousOption = self._filteredOptions and self._filteredOptions[self._highlightedIndex] or nil
            previousHighlightedValueKey = previousOption and previousOption._valueKey or nil
        end

        local filtered = {}
        for _, option in ipairs(self._allOptions) do
            local score = scoreOption(option, query)
            if query == "" or score ~= nil then
                option._score = score or 0
                table.insert(filtered, option)
            end
        end

        if query == "" then
            if preserveOrder then
                table.sort(filtered, function(a, b)
                    return a._order < b._order
                end)
            else
                table.sort(filtered, function(a, b)
                    local av = tonumber(a.value)
                    local bv = tonumber(b.value)
                    if av and bv and av ~= bv then
                        return av < bv
                    end
                    if av and not bv then return true end
                    if bv and not av then return false end
                    if a._labelLower ~= b._labelLower then
                        return a._labelLower < b._labelLower
                    end
                    return a._order < b._order
                end)
            end
        else
            table.sort(filtered, function(a, b)
                if a._score ~= b._score then
                    return a._score < b._score
                end
                if a._labelLower ~= b._labelLower then
                    return a._labelLower < b._labelLower
                end
                return a._order < b._order
            end)
        end

        self._filteredOptions = filtered
        self._highlightedIndex = 0
        if #filtered > 0 then
            if previousHighlightedValueKey ~= nil then
                for index, option in ipairs(filtered) do
                    if option._valueKey == previousHighlightedValueKey and not option.disabled then
                        self._highlightedIndex = index
                        break
                    end
                end
            end
            if self._selectedValueKey ~= nil then
                if self._highlightedIndex == 0 then
                    for index, option in ipairs(filtered) do
                        if option._valueKey == self._selectedValueKey and not option.disabled then
                            self._highlightedIndex = index
                            break
                        end
                    end
                end
            end
            if self._highlightedIndex == 0 then
                for index, option in ipairs(filtered) do
                    if not option.disabled then
                        self._highlightedIndex = index
                        break
                    end
                end
            end
        end

        self:RenderRows()
        if preserveScroll then
            local maxScroll = math.max(0, (self.scrollChild:GetHeight() or 0) - (self.scroll:GetHeight() or 0))
            local clamped = math.max(0, math.min(previousScroll or 0, maxScroll))
            self.scroll:SetVerticalScroll(clamped)
        else
            self:EnsureVisible(self._highlightedIndex)
        end
    end

    function picker:QueueFilter()
        self._filterToken = self._filterToken + 1
        local token = self._filterToken
        if C_Timer and C_Timer.After then
            C_Timer.After(0.08, function()
                if not picker or not picker:IsShown() then
                    return
                end
                if picker._filterToken ~= token then
                    return
                end
                picker:ApplyFilter()
            end)
        else
            self:ApplyFilter()
        end
    end

    function picker:SetOptions(newOptions)
        local source = type(newOptions) == "table" and newOptions or {}
        local normalizedOptions = {}
        for index, option in ipairs(source) do
            local aliases = {}
            if type(option.aliases) == "table" then
                for _, alias in ipairs(option.aliases) do
                    if trimString(alias) ~= "" then
                        table.insert(aliases, trimString(alias))
                    end
                end
            end
            local normalized = {
                value = option.value,
                label = trimString(option.label or ""),
                secondary = trimString(option.secondary or ""),
                buttonLabel = trimString(option.buttonLabel or option.label or ""),
                previewFontPath = trimString(option.previewFontPath or ""),
                previewTexturePath = trimString(option.previewTexturePath or option.path or ""),
                aliases = aliases,
                disabled = option.disabled and true or false,
                _order = index,
                _valueKey = tostring(option.value or ""),
            }
            normalized._labelLower = lowerString(normalized.label)
            normalized._secondaryLower = lowerString(normalized.secondary)
            normalized._aliasesLower = {}
            for _, alias in ipairs(normalized.aliases) do
                table.insert(normalized._aliasesLower, lowerString(alias))
            end
            table.insert(normalizedOptions, normalized)
        end

        local unchanged = (#self._allOptions == #normalizedOptions)
        if unchanged then
            for index = 1, #normalizedOptions do
                local previous = self._allOptions[index]
                local current = normalizedOptions[index]
                if not previous or not current
                    or previous._valueKey ~= current._valueKey
                    or previous.label ~= current.label
                    or previous.secondary ~= current.secondary
                    or previous.buttonLabel ~= current.buttonLabel
                    or previous.previewFontPath ~= current.previewFontPath
                    or previous.previewTexturePath ~= current.previewTexturePath
                    or previous.disabled ~= current.disabled
                    or #previous.aliases ~= #current.aliases then
                    unchanged = false
                    break
                end
                for aliasIndex = 1, #current.aliases do
                    if previous.aliases[aliasIndex] ~= current.aliases[aliasIndex] then
                        unchanged = false
                        break
                    end
                end
                if not unchanged then
                    break
                end
            end
        end

        if unchanged then
            return
        end

        clearTable(self._allOptions)
        for index, option in ipairs(normalizedOptions) do
            self._allOptions[index] = option
        end
        if self:IsShown() then
            self:ApplyFilter({ preserveScroll = true })
        end
    end

    function picker:Open(anchorFrame)
        if InCombatLockdown and InCombatLockdown() then
            self._pendingOpenAnchor = anchorFrame
            if not self._combatWaitFrame then
                self._combatWaitFrame = CreateFrame("Frame")
                self._combatWaitFrame:SetScript("OnEvent", function()
                    self._combatWaitFrame:UnregisterEvent("PLAYER_REGEN_ENABLED")
                    local pending = self._pendingOpenAnchor
                    self._pendingOpenAnchor = nil
                    self:Open(pending)
                end)
            end
            self._combatWaitFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
            return
        end

        -- Premium popover rule: never allow overlapping pickers.
        if ACTIVE_PORTAL_PICKER and ACTIVE_PORTAL_PICKER ~= self and ACTIVE_PORTAL_PICKER.Close then
            ACTIVE_PORTAL_PICKER:Close()
        end

        self._anchorFrame = anchorFrame
        ensureAnchorHideHook(anchorFrame)
        self:ClearAllPoints()
        if anchorFrame and anchorFrame.GetCenter and anchorFrame:GetCenter() then
            self:SetPoint("TOPLEFT", anchorFrame, "BOTTOMLEFT", 0, -4)
        else
            self:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
        end

        self:Show()
        ACTIVE_PORTAL_PICKER = self
        self:LayoutChrome()
        self.search:SetText("")
        if not self._noSearch then
            self.search:SetFocus()
            self.search:SetCursorPosition(0)
        else
            self.search:ClearFocus()
        end
        stopFooterPulse()
        self:ApplyFilter()
    end

    function picker:Close()
        if self._combatWaitFrame then
            self._combatWaitFrame:UnregisterEvent("PLAYER_REGEN_ENABLED")
        end
        self._pendingOpenAnchor = nil
        self._anchorFrame = nil
        if ACTIVE_PORTAL_PICKER == self then
            ACTIVE_PORTAL_PICKER = nil
        end
        stopFooterPulse()
        self._hasPreviewFlash = false
        for _, row in pairs(self._rows) do
            if row then
                row._previewFlashUntil = nil
            end
        end
        if self._transientTickerActive then
            self._transientTickerActive = false
            self:SetScript("OnUpdate", nil)
        end
        self.search:ClearFocus()
        self:Hide()
    end

    picker:SetScript("OnMouseWheel", function() end)
    search:SetScript("OnMouseWheel", function() end)
    clearButton:SetScript("OnMouseWheel", function() end)
    closeButton:SetScript("OnMouseWheel", function() end)

    search:SetScript("OnTextChanged", function(_, userInput)
        if userInput then
            picker:QueueFilter()
        end
    end)
    search:SetScript("OnEnterPressed", function()
        picker:SelectHighlighted()
    end)
    search:SetScript("OnEscapePressed", function()
        picker:Close()
    end)
    search:SetScript("OnKeyDown", function(_, key)
        if key == "UP" then
            picker:MoveHighlight(-1)
        elseif key == "DOWN" then
            picker:MoveHighlight(1)
        elseif key == "ENTER" or key == "NUMPADENTER" then
            picker:SelectHighlighted()
        elseif key == "ESCAPE" then
            picker:Close()
        end
    end)
    if search.SetPropagateKeyboardInput then
        search:SetPropagateKeyboardInput(false)
    end

    scroll:SetScript("OnMouseWheel", function(selfScroll, delta)
        local current = selfScroll:GetVerticalScroll()
        local nextValue = current - (delta * 24)
        if nextValue < 0 then nextValue = 0 end
        local maxScroll = math.max(0, (picker.scrollChild:GetHeight() or 0) - (selfScroll:GetHeight() or 0))
        if nextValue > maxScroll then nextValue = maxScroll end
        selfScroll:SetVerticalScroll(nextValue)
    end)

    clearButton:SetScript("OnClick", function()
        if picker._onSelect then
            picker._onSelect(nil, nil, true)
        end
        picker:Close()
    end)
    closeButton:SetScript("OnClick", function()
        picker:Close()
    end)

    picker:SetOptions(options or {})
    return picker
end

local function createTextPlacement(parent, onApply)
    local label = createStyledFontString(parent, "ARTWORK", "GameFontHighlight", 12)
    local dropdown = CreateFrame("Frame", nil, parent, "UIDropDownMenuTemplate")
    ensureSettingsWindowDropdownChrome(dropdown)

    local function normalizePlacement()
        if PortalAuthorityDB.dockOrientation == "HORIZONTAL" then
            PortalAuthorityDB.dockUseShortNames = true
            if PortalAuthorityDB.dockHorizontalTextPosition ~= "ABOVE" and PortalAuthorityDB.dockHorizontalTextPosition ~= "BELOW" then
                PortalAuthorityDB.dockHorizontalTextPosition = "ABOVE"
            end
        else
            if PortalAuthorityDB.dockVerticalTextSide ~= "LEFT" and PortalAuthorityDB.dockVerticalTextSide ~= "RIGHT" then
                PortalAuthorityDB.dockVerticalTextSide = "LEFT"
            end
        end
    end

    local function getValues()
        if PortalAuthorityDB.dockOrientation == "HORIZONTAL" then
            return { { label = "Above", value = "ABOVE" }, { label = "Below", value = "BELOW" } }
        end
        return { { label = "Left", value = "LEFT" }, { label = "Right", value = "RIGHT" } }
    end

    UIDropDownMenu_Initialize(dropdown, function(_, level)
        normalizePlacement()
        local active = (PortalAuthorityDB.dockOrientation == "HORIZONTAL") and PortalAuthorityDB.dockHorizontalTextPosition or PortalAuthorityDB.dockVerticalTextSide
        for _, option in ipairs(getValues()) do
            local info = UIDropDownMenu_CreateInfo()
            info.text = option.label
            info.checked = active == option.value
            info.func = function()
                if PortalAuthorityDB.dockOrientation == "HORIZONTAL" then
                    PortalAuthorityDB.dockHorizontalTextPosition = option.value
                else
                    PortalAuthorityDB.dockVerticalTextSide = option.value
                end
                UIDropDownMenu_SetSelectedValue(dropdown, option.value)
                UIDropDownMenu_SetText(dropdown, option.label)
                if onApply then onApply() end
            end
            UIDropDownMenu_AddButton(info, level)
        end
    end)

    local spec = { kind = "dropdown", label = label, control = dropdown }
    spec.Refresh = function()
        normalizePlacement()
        if PortalAuthorityDB.dockOrientation == "HORIZONTAL" then
            label:SetText("Text placement")
            local value = PortalAuthorityDB.dockHorizontalTextPosition
            UIDropDownMenu_SetSelectedValue(dropdown, value)
            UIDropDownMenu_SetText(dropdown, value == "BELOW" and "Below" or "Above")
        else
            label:SetText("Text side")
            local value = PortalAuthorityDB.dockVerticalTextSide
            UIDropDownMenu_SetSelectedValue(dropdown, value)
            UIDropDownMenu_SetText(dropdown, value == "RIGHT" and "Right" or "Left")
        end
    end

    spec.Refresh()
    return spec
end

function PortalAuthority:CreateSettingsWindowSegmentedControl(parent, choices, onApply, opts)
    opts = type(opts) == "table" and opts or {}
    local spec = createPremiumSegmentedChoice(
        parent,
        tostring(opts.labelText or ""),
        nil,
        choices or {},
        onApply,
        opts
    )
    if spec and spec.label and trim(tostring(opts.labelText or "")) == "" then
        spec.label:SetText("")
        spec.label:Hide()
    end
    return spec
end

local function lowerSettingsSearchText(value)
    local text = trim(tostring(value or ""))
    if text == "" then
        return ""
    end
    return string.lower(text)
end

settingsSearchRuntime.lowerSettingsSearchText = lowerSettingsSearchText

settingsSearchRuntime.SEARCH_NOISE_EXACT = {
    ["death knight only"] = true,
    ["demon hunter only"] = true,
    ["druid only"] = true,
    ["evoker only"] = true,
    ["hunter only"] = true,
    ["mage only"] = true,
    ["monk only"] = true,
    ["paladin only"] = true,
    ["priest only"] = true,
    ["rogue only"] = true,
    ["shaman only"] = true,
    ["warlock only"] = true,
    ["warrior only"] = true,
    ["mythic+ only"] = true,
}

settingsSearchRuntime.GENERIC_CHOICE_CAPTION_EXACT = {
    ["enabled"] = true,
    ["disabled"] = true,
    ["enable"] = true,
    ["disable"] = true,
    ["on"] = true,
    ["off"] = true,
    ["show"] = true,
    ["hide"] = true,
    ["yes"] = true,
    ["no"] = true,
}

function settingsSearchRuntime.normalizeWhitespace(text)
    text = tostring(text or "")
    text = text:gsub("[%c\r\n\t]+", " ")
    text = text:gsub("%s+", " ")
    return trim(text)
end

function settingsSearchRuntime.stripFormatting(text)
    text = tostring(text or "")
    text = text:gsub("|c%x%x%x%x%x%x%x%x", "")
    text = text:gsub("|r", "")
    text = text:gsub("|T.-|t", " ")
    text = text:gsub("|A.-|a", " ")
    return settingsSearchRuntime.normalizeWhitespace(text)
end

function settingsSearchRuntime.stripQualifiers(text)
    text = settingsSearchRuntime.stripFormatting(text)
    if text == "" then
        return ""
    end

    text = text:gsub("%s*%[[^%]]-[Oo][Nn][Ll][Yy]%]%s*", " ")
    text = text:gsub("%s*%([^%)]-[Oo][Nn][Ll][Yy]%)%s*", " ")
    return settingsSearchRuntime.normalizeWhitespace(text)
end

function settingsSearchRuntime.isNoiseText(text)
    local normalized = lowerSettingsSearchText(settingsSearchRuntime.stripFormatting(text))
    if normalized == "" then
        return true
    end

    normalized = normalized:gsub("^%[", ""):gsub("%]$", "")
    normalized = normalized:gsub("^%(", ""):gsub("%)$", "")
    normalized = settingsSearchRuntime.normalizeWhitespace(normalized)
    if normalized == "" then
        return true
    end

    return settingsSearchRuntime.SEARCH_NOISE_EXACT[normalized] == true
end

function settingsSearchRuntime.sanitizeVisibleText(text)
    text = settingsSearchRuntime.stripQualifiers(text)
    if text == "" or settingsSearchRuntime.isNoiseText(text) then
        return ""
    end
    return settingsSearchRuntime.normalizeWhitespace(text)
end

function settingsSearchRuntime.normalizeChoiceLabelMode(mode)
    mode = lowerSettingsSearchText(mode)
    if mode == "index_all" then
        return "index_all"
    end
    return "suppress_generic"
end

function settingsSearchRuntime.markSearchTextOrigin(ref, origin, opts)
    if not frameIsAlive(ref) then
        return ref
    end

    origin = lowerSettingsSearchText(origin)
    if origin == "" then
        ref._paSearchTextOrigin = nil
        ref._paSearchChoiceLabelMode = nil
        return ref
    end

    ref._paSearchTextOrigin = origin
    if origin == "choice_caption" then
        opts = type(opts) == "table" and opts or {}
        ref._paSearchChoiceLabelMode = settingsSearchRuntime.normalizeChoiceLabelMode(
            opts.searchChoiceLabelMode or opts.choiceLabelMode
        )
    else
        ref._paSearchChoiceLabelMode = nil
    end
    return ref
end

function settingsSearchRuntime.shouldSuppressSearchTextRef(ref, text)
    if not frameIsAlive(ref) then
        return false
    end
    if lowerSettingsSearchText(ref._paSearchTextOrigin) ~= "choice_caption" then
        return false
    end
    if settingsSearchRuntime.normalizeChoiceLabelMode(ref._paSearchChoiceLabelMode) ~= "suppress_generic" then
        return false
    end

    local normalized = lowerSettingsSearchText(settingsSearchRuntime.stripFormatting(text))
    normalized = settingsSearchRuntime.normalizeWhitespace(normalized)
    return settingsSearchRuntime.GENERIC_CHOICE_CAPTION_EXACT[normalized] == true
end

local function setSettingsControlEnabled(target, enabled)
    if not target then
        return
    end

    local allow = enabled and true or false
    local control = target.control or target

    if target.SetEnabled then
        target:SetEnabled(allow)
    elseif control and control.SetEnabled then
        control:SetEnabled(allow)
    elseif allow and control and control.Enable then
        control:Enable()
    elseif (not allow) and control and control.Disable then
        control:Disable()
    elseif control and control.EnableMouse then
        control:EnableMouse(allow)
    end

    if target.label and target.label.SetAlpha then
        target.label:SetAlpha(allow and 1 or 0.55)
    end
    if control and control.SetAlpha then
        control:SetAlpha(allow and 1 or 0.55)
    end
    if control and control.Text and control.Text.SetAlpha then
        control.Text:SetAlpha(allow and 1 or 0.7)
    end
end

local function setSettingsTextEnabled(fontString, enabled)
    if fontString and fontString.SetAlpha then
        fontString:SetAlpha((enabled and true or false) and 1 or 0.55)
    end
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
OptionsInternal.frameIsAlive = frameIsAlive
OptionsInternal.isDescendedFromFrame = isDescendedFromFrame
OptionsInternal.markSettingsWindowVisualOptIn = markSettingsWindowVisualOptIn
OptionsInternal.isUsingCustomSettingsWindowVisualStyle = isUsingCustomSettingsWindowVisualStyle
OptionsInternal.registerSettingsWindowVisualRefresh = registerSettingsWindowVisualRefresh
OptionsInternal.setTextureSolidColor = setTextureSolidColor
OptionsInternal.hideDefaultScrollBarPart = hideDefaultScrollBarPart
OptionsInternal.getTemplateScrollBar = getTemplateScrollBar
OptionsInternal.hideDefaultScrollBarChrome = hideDefaultScrollBarChrome
OptionsInternal.refreshCustomSettingsScrollBarThumb = refreshCustomSettingsScrollBarThumb
OptionsInternal.settingsScrollRuntime = settingsScrollRuntime
OptionsInternal.restoreColorPickerPrevious = restoreColorPickerPrevious
OptionsInternal.openColorPicker = openColorPicker
OptionsInternal.applySettingsWindowSelectButtonChrome = applySettingsWindowSelectButtonChrome
OptionsInternal.createStyledFontString = createStyledFontString
OptionsInternal.getClassHex = getClassHex
OptionsInternal.createTopRightHeader = createTopRightHeader
OptionsInternal.PA_SafeBool = PA_SafeBool
OptionsInternal.createCheckbox = createCheckbox
OptionsInternal.createActionButton = createActionButton
OptionsInternal.styleSecondaryActionButtonShared = styleSecondaryActionButtonShared
OptionsInternal.createNumeric = createNumeric
OptionsInternal.createDropdown = createDropdown
OptionsInternal.createDynamicDropdown = createDynamicDropdown
OptionsInternal.createPremiumSegmentedChoice = createPremiumSegmentedChoice
OptionsInternal.CreatePicker = CreatePicker
OptionsInternal.CreateStepperControl = CreateStepperControl
OptionsInternal.createCanonicalDockFontSizeStepperControl = CreateStepperControl
OptionsInternal.createTextPlacement = createTextPlacement
OptionsInternal.setSettingsControlEnabled = setSettingsControlEnabled
OptionsInternal.setSettingsTextEnabled = setSettingsTextEnabled
function OptionsInternal.GetActivePortalPicker()
    return ACTIVE_PORTAL_PICKER
end

function CloseActivePortalPicker()
    if ACTIVE_PORTAL_PICKER and ACTIVE_PORTAL_PICKER.Close then
        ACTIVE_PORTAL_PICKER:Close()
    end
end

OptionsInternal.IsPointerInsideActivePickerSafeZone = isPointerInsideActivePickerSafeZone
OptionsInternal.CloseActivePortalPicker = CloseActivePortalPicker
