Сделай адаптивное описание, не строго сверху, а при наведении на кнопку например верхнюю кнопку, то описание будет сверху над ней, если сбоку кнопка, то сбоку, вообщем чтобы не перекрывало другие кнопки.
```lua
if CLIENT then
    -- Вспомогательная функция для проверки попадания точки в круг
    local function PointInCircle(px, py, cx, cy, radius)
        local dx, dy = px - cx, py - cy
        return (dx * dx + dy * dy) <= (radius * radius)
    end

    local menuPanel = nil
    local buttonAnims = {}
    local animDuration = 0.5
    local animDelay = 0.08
    local subButtons = {}
    local descriptionPanel = nil -- Панель описания для кнопок и подкнопок
    local function ModernButtonPaint(s, w, h, idx, progress)
        -- Серый полупрозрачный круг
        local hovered = s:IsHovered()
        local baseAlpha = hovered and 180 or 120
        local borderAlpha = hovered and 220 or 150
        local textAlpha = hovered and 255 or 200
        surface.SetDrawColor(80, 80, 80, baseAlpha)
        draw.NoTexture()
        surface.DrawCircle(w / 2, h / 2, w / 2 - 2, 80, 80, 80, baseAlpha)
        -- Обводка
        surface.SetDrawColor(180, 180, 180, borderAlpha)
        surface.DrawCircle(w / 2, h / 2, w / 2 - 4, 180, 180, 180, borderAlpha)
        -- Эффект наведения (легкое высветление)
        if hovered then
            surface.SetDrawColor(255, 255, 255, 30)
            surface.DrawCircle(w / 2, h / 2, w / 2 - 2, 255, 255, 255, 30)
        end

        -- Текст
        draw.SimpleText(idx, "DermaLarge", w / 2, h / 2, Color(220, 220, 220, textAlpha), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
    end

    -- Объявляем функции CreateSubButtons и RemoveSubButtons заранее, чтобы они были видимы в CreateCircularMenu
    local function CreateSubButtons(parent, mainBtn, idx)
    end

    local function RemoveSubButtons(idx)
    end

    local function CreateCircularMenu()
        if IsValid(menuPanel) then
            menuPanel:Remove()
            menuPanel = nil
        end

        buttonAnims = {}
        local radius = 150
        local buttonsCount = 8
        local angleStep = 2 * math.pi / buttonsCount
        menuPanel = vgui.Create("DPanel")
        if not IsValid(menuPanel) then return end
        menuPanel:SetSize(ScrW(), ScrH())
        menuPanel:SetPos(0, 0)
        menuPanel:SetPaintBackground(false)
        -- Блюр фона
        menuPanel.Paint = function(self, w, h) Derma_DrawBackgroundBlur(self, self.m_fCreateTime or SysTime()) end
        menuPanel.m_fCreateTime = SysTime()
        -- Показываем курсор
        menuPanel:SetMouseInputEnabled(true)
        menuPanel:SetKeyboardInputEnabled(true)
        gui.EnableScreenClicker(true)
        local center = {
            x = ScrW() / 2,
            y = ScrH() / 2
        }

        for i = 1, buttonsCount do
            local angle = (i - 1) * angleStep - math.pi / 2
            local btnX = center.x + math.cos(angle) * radius
            local btnY = center.y + math.sin(angle) * radius
            local startY = ScrH() + 100
            local btn = vgui.Create("DButton", menuPanel)
            if not IsValid(btn) then continue end
            btn:SetSize(64, 64)
            btn:SetPos(btnX - 32, startY)
            btn:SetText("")
            btn:SetFont("DermaLarge")
            btn:SetZPos(i)
            btn.Paint = function(s, w, h)
                local anim = buttonAnims[i]
                local progress = anim and anim.progress or 1
                ModernButtonPaint(s, w, h, i, progress)
            end

            btn.OnCursorEntered = function(s)
                local anim = buttonAnims[i]
                if not anim or anim.progress < 1 then return end
                local desc = buttonData[i] and buttonData[i].desc or ("Описание кнопки " .. i)
                ShowDescription(desc)
                CreateSubButtons(menuPanel, s, i)
            end
            btn.DoClick = function()
                if buttonData[i] and buttonData[i].action then
                    buttonData[i].action()
                end
                if IsValid(menuPanel) then
                    menuPanel:Remove()
                    menuPanel = nil
                end
            end

            buttonAnims[i] = {
                start = CurTime() + (i - 1) * animDelay,
                progress = 0,
                btn = btn,
                fromY = startY,
                toY = btnY - 32,
                toX = btnX - 32
            }
        end

        menuPanel.Think = function(self)
            local allDone = true
            for i, anim in ipairs(buttonAnims) do
                local btn = anim.btn
                if not IsValid(btn) then continue end
                local t = CurTime() - anim.start
                if t < 0 then
                    btn:SetPos(anim.toX, anim.fromY)
                    allDone = false
                elseif t < animDuration then
                    local p = math.min(1, t / animDuration)
                    p = 1 - (1 - p) * (1 - p)
                    local y = Lerp(p, anim.fromY, anim.toY)
                    btn:SetPos(anim.toX, y)
                    anim.progress = p
                    allDone = false
                else
                    btn:SetPos(anim.toX, anim.toY)
                    anim.progress = 1
                end
            end

            if allDone then self.Think = nil end
        end

        menuPanel.OnRemove = function()
            gui.EnableScreenClicker(false)
            HideDescription()
        end

        -- Закрытие меню при нажатии вне панели
        menuPanel.OnMousePressed = function(self, code)
            if code == MOUSE_LEFT and not self:IsHovered() then
                self:Remove()
                menuPanel = nil
            end
        end
    end

    -- Реализация CreateSubButtons
    function CreateSubButtons(parent, mainBtn, idx)
        -- Проверяем, есть ли уже валидные подкнопки
        local needCreate = false
        if not subButtons[idx] or #subButtons[idx] ~= 4 then
            needCreate = true
        else
            for _, sbtn in ipairs(subButtons[idx]) do
                if not IsValid(sbtn) then
                    needCreate = true
                    break
                end
            end
        end

        if not needCreate then return end
        RemoveSubButtons(idx)
        local subBtnCount = 4
        local subBtnRadius = 48
        local subBtnSize = 36
        subButtons[idx] = {}
        local x, y = mainBtn:GetPos()
        local w, h = mainBtn:GetSize()
        local cx, cy = x + w / 2, y + h / 2
        for j = 1, subBtnCount do
            local angle = (j - 1) * (2 * math.pi / subBtnCount) - math.pi / 2
            local sx = cx + math.cos(angle) * subBtnRadius - subBtnSize / 2
            local sy = cy + math.sin(angle) * subBtnRadius - subBtnSize / 2
            local subBtn = vgui.Create("DButton", parent)
            subBtn:SetSize(subBtnSize, subBtnSize)
            subBtn:SetPos(cx - subBtnSize / 2, cy - subBtnSize / 2)
            subBtn:SetText("")
            subBtn:SetZPos(100 + j)
            subBtn._targetPos = {
                x = sx,
                y = sy
            }

            subBtn._anim = 0
            subBtn._mainBtn = mainBtn
            subBtn._idx = idx
            subBtn._j = j
            subBtn.Paint = function(s, w2, h2)
                local hovered = s:IsHovered()
                local baseAlpha = hovered and 200 or 140
                local borderAlpha = hovered and 240 or 180
                local textAlpha = hovered and 255 or 200
                surface.SetDrawColor(80, 80, 80, baseAlpha)
                draw.NoTexture()
                surface.DrawCircle(w2 / 2, h2 / 2, w2 / 2 - 2, 80, 80, 80, baseAlpha)
                surface.SetDrawColor(180, 180, 180, borderAlpha)
                surface.DrawCircle(w2 / 2, h2 / 2, w2 / 2 - 4, 180, 180, 180, borderAlpha)
                if hovered then
                    surface.SetDrawColor(255, 255, 255, 40)
                    surface.DrawCircle(w2 / 2, h2 / 2, w2 / 2 - 2, 255, 255, 255, 40)
                end

                draw.SimpleText(j, "DermaDefault", w2 / 2, h2 / 2, Color(220, 220, 220, textAlpha), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
            end

            subBtn.DoClick = function()
                if subButtonData[idx] and subButtonData[idx][j] and subButtonData[idx][j].action then
                    subButtonData[idx][j].action()
                else
                    print("Подкнопка " .. j .. " основной " .. idx)
                end
            end
            subBtn.OnCursorEntered = function(s)
                local desc = subButtonData[idx] and subButtonData[idx][j] and subButtonData[idx][j].desc or ("Описание подкнопки " .. j .. " основной " .. idx)
                ShowDescription(desc)
            end
            subBtn.OnCursorExited = function(s) HideDescription() end
            subBtn.Think = function(s)
                local hovered = IsValid(mainBtn) and (mainBtn:IsHovered() or s:IsHovered())
                local mx, my = input.GetCursorPos()
                local hoverRadius = 80
                if IsValid(mainBtn) then
                    local bx, by = mainBtn:GetPos()
                    local bw, bh = mainBtn:GetSize()
                    local bcx, bcy = bx + bw / 2, by + bh / 2
                    if PointInCircle(mx, my, bcx, bcy, hoverRadius) then hovered = true end
                end

                if not hovered then
                    s._anim = math.max(0, s._anim - FrameTime() * 10)
                else
                    s._anim = math.min(1, s._anim + FrameTime() * 10)
                end

                local tx = Lerp(s._anim, cx - subBtnSize / 2, s._targetPos.x)
                local ty = Lerp(s._anim, cy - subBtnSize / 2, s._targetPos.y)
                s:SetPos(tx, ty)
                s:SetAlpha(255 * s._anim)
                if s._anim < 0.05 and not hovered then s:Remove() end
            end

            table.insert(subButtons[idx], subBtn)
        end
    end

    -- Реализация RemoveSubButtons
    function RemoveSubButtons(idx)
        if not subButtons[idx] then return end
        local mainBtn = nil
        for i, anim in ipairs(buttonAnims) do
            if i == idx then
                mainBtn = anim.btn
                break
            end
        end

        if not IsValid(mainBtn) then return end
        local x, y = mainBtn:GetPos()
        local w, h = mainBtn:GetSize()
        local cx, cy = x + w / 2, y + h / 2
        local mx, my = input.GetCursorPos()
        local hoverRadius = 80
        if PointInCircle(mx, my, cx, cy, hoverRadius) then return end
        for _, sbtn in ipairs(subButtons[idx]) do
            if IsValid(sbtn) and sbtn:IsHovered() then return end
        end

        for _, sbtn in ipairs(subButtons[idx]) do
            if IsValid(sbtn) then sbtn:Remove() end
        end

        subButtons[idx] = nil
    end

    function ShowDescription(text)
        if not IsValid(descriptionPanel) then
            descriptionPanel = vgui.Create("DPanel")
            descriptionPanel:SetSize(ScrW(), 40)
            descriptionPanel:SetPos(0, 0)
            descriptionPanel:SetZPos(9999)
            descriptionPanel.Paint = function(self, w, h)
                surface.SetDrawColor(30, 30, 30, 220)
                surface.DrawRect(0, 0, w, h)
            end

            descriptionPanel.label = vgui.Create("DLabel", descriptionPanel)
            descriptionPanel.label:SetFont("DermaLarge")
            descriptionPanel.label:SetTextColor(Color(255, 255, 255))
            descriptionPanel.label:SetPos(20, 6)
            descriptionPanel.label:SetSize(ScrW() - 40, 32)
        end

        descriptionPanel.label:SetText(text)
        descriptionPanel:SetVisible(true)
    end

    function HideDescription()
        if IsValid(descriptionPanel) then descriptionPanel:SetVisible(false) end
    end

    -- Таблица с описаниями и действиями для основных кнопок
    buttonData = {
        [1] = { desc = "Отыгровка действий [По умолчанию исполнить партийное приветствие.]", action = function() print("Открыт инвентарь!") end },
        [2] = { desc = "Открыть карту", action = function() print("Открыта карта!") end },
        [3] = { desc = "Профиль игрока", action = function() print("Профиль!") end },
        [4] = { desc = "Настройки", action = function() print("Настройки!") end },
        [5] = { desc = "Помощь", action = function() print("Помощь!") end },
        [6] = { desc = "Магазин", action = function() print("Магазин!") end },
        [7] = { desc = "Достижения", action = function() print("Достижения!") end },
        [8] = { desc = "Выход", action = function() print("Выход!") end },
    }
    -- Таблица с описаниями и действиями для подкнопок
    subButtonData = {
        [1] = {
            [1] = { desc = "Подкнопка 1-1", action = function() print("Подкнопка 1-1") end },
            [2] = { desc = "Подкнопка 1-2", action = function() print("Подкнопка 1-2") end },
            [3] = { desc = "Подкнопка 1-3", action = function() print("Подкнопка 1-3") end },
            [4] = { desc = "Подкнопка 1-4", action = function() print("Подкнопка 1-4") end },
        },
        [2] = {
            [1] = { desc = "Подкнопка 2-1", action = function() print("Подкнопка 2-1") end },
            [2] = { desc = "Подкнопка 2-2", action = function() print("Подкнопка 2-2") end },
            [3] = { desc = "Подкнопка 2-3", action = function() print("Подкнопка 2-3") end },
            [4] = { desc = "Подкнопка 2-4", action = function() print("Подкнопка 2-4") end },
        },
        [3] = {
            [1] = { desc = "Подкнопка 3-1", action = function() print("Подкнопка 3-1") end },
            [2] = { desc = "Подкнопка 3-2", action = function() print("Подкнопка 3-2") end },
            [3] = { desc = "Подкнопка 3-3", action = function() print("Подкнопка 3-3") end },
            [4] = { desc = "Подкнопка 3-4", action = function() print("Подкнопка 3-4") end },
        },
        [4] = {
            [1] = { desc = "Подкнопка 4-1", action = function() print("Подкнопка 4-1") end },
            [2] = { desc = "Подкнопка 4-2", action = function() print("Подкнопка 4-2") end },
            [3] = { desc = "Подкнопка 4-3", action = function() print("Подкнопка 4-3") end },
            [4] = { desc = "Подкнопка 4-4", action = function() print("Подкнопка 4-4") end },
        },
        [5] = {
            [1] = { desc = "Подкнопка 5-1", action = function() print("Подкнопка 5-1") end },
            [2] = { desc = "Подкнопка 5-2", action = function() print("Подкнопка 5-2") end },
            [3] = { desc = "Подкнопка 5-3", action = function() print("Подкнопка 5-3") end },
            [4] = { desc = "Подкнопка 5-4", action = function() print("Подкнопка 5-4") end },
        },
        [6] = {
            [1] = { desc = "Подкнопка 6-1", action = function() print("Подкнопка 6-1") end },
            [2] = { desc = "Подкнопка 6-2", action = function() print("Подкнопка 6-2") end },
            [3] = { desc = "Подкнопка 6-3", action = function() print("Подкнопка 6-3") end },
            [4] = { desc = "Подкнопка 6-4", action = function() print("Подкнопка 6-4") end },
        },
        [7] = {
            [1] = { desc = "Подкнопка 7-1", action = function() print("Подкнопка 7-1") end },
            [2] = { desc = "Подкнопка 7-2", action = function() print("Подкнопка 7-2") end },
            [3] = { desc = "Подкнопка 7-3", action = function() print("Подкнопка 7-3") end },
            [4] = { desc = "Подкнопка 7-4", action = function() print("Подкнопка 7-4") end },
        },
        [8] = {
            [1] = { desc = "Подкнопка 8-1", action = function() print("Подкнопка 8-1") end },
            [2] = { desc = "Подкнопка 8-2", action = function() print("Подкнопка 8-2") end },
            [3] = { desc = "Подкнопка 8-3", action = function() print("Подкнопка 8-3") end },
            [4] = { desc = "Подкнопка 8-4", action = function() print("Подкнопка 8-4") end },
        },
    }

    hook.Add("OnContextMenuOpen", "OpenCircularMenu", function()
        if not IsValid(menuPanel) then CreateCircularMenu() end
        return true
    end)

    hook.Add("OnContextMenuClose", "CloseCircularMenu", function()
        if IsValid(menuPanel) then
            menuPanel:Remove()
            menuPanel = nil
            gui.EnableScreenClicker(false)
        end
    end)
end
```