```lua
if CLIENT then

    -- =========================
    -- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
    -- =========================

    -- Проверка попадания точки в круг (x,y)
    local function PointInCircle(px, py, cx, cy, radius)
        local dx, dy = px - cx, py - cy
        return (dx * dx + dy * dy) <= (radius * radius)
    end

    -- Рисует современную кнопку с эффектами наведения
    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


    -- =========================
    -- ПАНЕЛЬ ОПИСАНИЯ (DESCRIPTION PANEL)
    -- =========================

    local descriptionPanel = nil

    local 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

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


    -- =========================
    -- ДАННЫЕ КНОПОК (Основные и Подкнопки)
    -- =========================

    local 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 },
    }

    local 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 },
        },
    }


    -- =========================
    -- ПЕРЕМЕННЫЕ ДЛЯ МЕНЮ И АНИМАЦИЙ
    -- =========================

    local menuPanel = nil
    local buttonAnims = {}
    local animDuration = 0.5
    local animDelay = 0.08
    local subButtons = {}



    -- =========================
    -- СОЗДАНИЕ И УДАЛЕНИЕ МЕНЮ
    -- =========================

    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 }
        local startY = ScrH() + 100 -- старт по Y для анимации

        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 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.OnCursorExited = function(s)
                -- При уходе мыши с основной кнопки проверяем удаление подкнопок
                timer.Simple(0.01, function() RemoveSubButtons(i) end)
                HideDescription()
            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) ^ 2 -- easing
                    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()
            -- Удаляем все подкнопки, если они остались
            for idx, _ in pairs(subButtons) do
                RemoveSubButtons(idx)
            end
        end

        menuPanel.OnMousePressed = function(self, code)
            if code == MOUSE_LEFT and not self:IsHovered() then
                self:Remove()
                menuPanel = nil
            end
        end
    end



    -- =========================
    -- СОЗДАНИЕ И УДАЛЕНИЕ ПОДКОПОК
    -- =========================

    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()
                -- При уходе с подкнопки проверяем удаление подкнопок
                timer.Simple(0.01, function() RemoveSubButtons(idx) end)
            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

    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



    -- =========================
    -- ХУКИ ОТКРЫТИЯ/ЗАКРЫТИЯ МЕНЮ
    -- =========================

    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
```
Добавить градиенты и тени для кнопок вместо плоских цветов.

Напиши нужный участок кода с улучшением, и я его внедрю