Падающие листья, есть на выбор, облака, дождь

Adds persistent falling particles (leaves, petals, rain) with advanced controls (wind, glow, fog, selection, RGB) and realistic clouds to forum.blackrussia.online

// ==UserScript==
// @name         Падающие листья, есть на выбор, облака, дождь
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Adds persistent falling particles (leaves, petals, rain) with advanced controls (wind, glow, fog, selection, RGB) and realistic clouds to forum.blackrussia.online
// @author       Тех. [06] M. Ageev
// @match        https://forum.blackrussia.online/*
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- КОНФИГУРАЦИЯ И НАСТРОЙКИ ПО УМОЛЧАНИЮ ---
    const allParticleTypes = ['🌸', '🍁', '🍂', '🍃', '🌿', '✨', '💧'];
    const defaultSettings = {
        effectEnabled: true,
        panelVisible: false,
        // Основные частицы
        density: 50,
        speed: 1.5,
        size: 20,
        wind: 0.3,
        selectedTypes: ['🌸', '🍁', '🍂', '🍃'],
        // Свечение
        glowEnabled: false,
        glowIntensity: 5,
        rgbEnabled: false,
        glowColors: ['red', 'green', 'blue'],
        rgbSpeed: 1,
        // Туман
        fogDensity: 0,
        // Дождь
        rainEnabled: false,
        rainDensity: 20,
        rainSpeed: 3,
        rainDropLength: 15,
        // Облака
        cloudsEnabled: false,
        cloudDensity: 10,
        cloudSpeed: 0.2,
        cloudColor: 'rgba(255, 255, 255, 0.3)',
        // Внутренние переменные
        currentColorIndex: 0,
        colorCycleCounter: 0,
    };

    // --- ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ---
    let settings = {};
    let particles = [];
    let clouds = [];
    let canvas, ctx;
    let animationFrameId;
    let controlPanel, settingsButton;

    // --- УПРАВЛЕНИЕ НАСТРОЙКАМИ ---
    async function loadSettings() {
        const savedSettings = await GM_getValue('fallingLeavesSettings_v2_3', defaultSettings); // Используем тот же ключ для совместимости
        settings = { ...defaultSettings, ...savedSettings };
        if (!Array.isArray(settings.selectedTypes)) {
             settings.selectedTypes = defaultSettings.selectedTypes;
        }
        if (!Array.isArray(settings.glowColors)) {
            settings.glowColors = defaultSettings.glowColors;
        }
        console.log("Falling Leaves Settings Loaded:", settings);
    }

    async function saveSettings() {
        await GM_setValue('fallingLeavesSettings_v2_3', settings);
    }

    // --- СОЗДАНИЕ И НАСТРОЙКА CANVAS ---
    function setupCanvas() {
        canvas = document.createElement('canvas');
        document.body.appendChild(canvas);
        ctx = canvas.getContext('2d');

        canvas.style.position = 'fixed';
        canvas.style.top = '0';
        canvas.style.left = '0';
        canvas.style.width = '100%';
        canvas.style.height = '100%';
        canvas.style.zIndex = '9998';
        canvas.style.pointerEvents = 'none';
        canvas.style.display = settings.effectEnabled ? 'block' : 'none';

        resizeCanvas();
        window.addEventListener('resize', resizeCanvas);
    }

    function resizeCanvas() {
        if (canvas) {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        }
    }

    // --- УПРАВЛЕНИЕ ЧАСТИЦАМИ ---
    function createParticle(type) {
        let p = {
            x: Math.random() * canvas.width,
            y: -Math.random() * canvas.height * 0.5,
            opacity: 0.7 + Math.random() * 0.3,
            rotation: Math.random() * 360,
            rotationSpeed: (Math.random() - 0.5) * 2
        };

        if (settings.selectedTypes.length > 0 && type === 'default') {
            const availableTypes = settings.selectedTypes;
            p.char = availableTypes[Math.floor(Math.random() * availableTypes.length)];
            const sizeVariation = (Math.random() - 0.5) * settings.size * 0.5;
            const speedVariation = (Math.random() - 0.5) * settings.speed * 0.5;
            const baseSpeed = Math.max(0.5, Math.min(5, settings.speed + speedVariation));
            p.size = Math.max(10, Math.min(40, settings.size + sizeVariation));
            p.speedY = baseSpeed;
            p.speedX = (settings.wind / 2 + (Math.random() - 0.5) * settings.wind) * baseSpeed * 0.5;
        } else if (type === 'rain') {
            p.speedY = settings.rainSpeed;
            p.speedX = (Math.random() - 0.5) * 0.5;
            p.length = settings.rainDropLength;
            p.weight = Math.random() * 0.3 + 0.9;
            p.angle = Math.PI / 6 + (Math.random() - 0.5) * Math.PI / 12; // Slightly angled
        }
        return p;
    }

    function createParticles() {
        particles = [];
        const defaultParticleCount = Math.min(Math.max(0, settings.density), 150);
        const rainParticleCount = settings.rainEnabled ? Math.min(Math.max(0, settings.rainDensity), 150) : 0;

        for (let i = 0; i < defaultParticleCount; i++) {
            particles.push(createParticle('default'));
        }
        for (let i = 0; i < rainParticleCount; i++) {
            particles.push(createParticle('rain'));
        }
    }

     function updateParticles() {
         if (!ctx || !settings.effectEnabled) return;

         particles.forEach((p, index) => {
             if (p.length) { // Дождь
                 p.y += p.speedY * p.weight;
                 p.x += p.speedX;
                 if (p.y > canvas.height) {
                     particles[index] = createParticle('rain');
                     particles[index].y = -20;
                 }
             } else { // Обычные частицы
                 p.y += p.speedY;
                 p.x += p.speedX;
                 p.rotation += p.rotationSpeed;
                 const bufferZoneX = Math.abs(settings.wind * 50);
                 if (p.y > canvas.height + p.size || p.x < -p.size - bufferZoneX || p.x > canvas.width + p.size + bufferZoneX) {
                     particles[index] = createParticle('default');
                     particles[index].y = -p.size;
                     particles[index].x = Math.random() * canvas.width;
                 }
             }
         });
     }

    // --- УПРАВЛЕНИЕ ОБЛАКАМИ ---
    function createCloudParticle() {
        const numCircles = 3 + Math.floor(Math.random() * 3);
        const circleProps = [];
        for (let i = 0; i < numCircles; i++) {
            circleProps.push({
                sizeVariation: (Math.random() - 0.5) * 150 * 0.3,
                yOffset: (Math.random() - 0.5) * 150 * 0.2
            });
        }
        return {
            x: -Math.random() * canvas.width * 0.5,
            y: Math.random() * canvas.height * 0.4, // Clouds in the upper part
            size: 50 + Math.random() * 150,
            opacity: 0.2 + Math.random() * 0.2,
            drift: settings.cloudSpeed + Math.random() * 0.1,
            numCircles: numCircles,
            circleProps: circleProps
        };
    }

    function createClouds() {
        clouds = [];
        const cloudCount = Math.min(Math.max(0, settings.cloudDensity), 50);
        for (let i = 0; i < cloudCount; i++) {
            clouds.push(createCloudParticle());
        }
    }

    function updateClouds() {
        if (!settings.cloudsEnabled) return;
        clouds.forEach(cloud => {
            cloud.x += cloud.drift;
            if (cloud.x > canvas.width + cloud.size * 0.5) {
                cloud.x = -cloud.size * 0.5;
            }
        });
    }

    function drawParticles() {
        if (!ctx || !settings.effectEnabled) return;
        ctx.clearRect(0, 0, canvas.width, canvas.height); // Очищаем предыдущий кадр

        // --- РИСУЕМ ОБЛАКА (если включены) ---
        if (settings.cloudsEnabled) {
            clouds.forEach(cloud => {
                ctx.globalAlpha = cloud.opacity;
                ctx.fillStyle = settings.cloudColor;
                for (let i = 0; i < cloud.numCircles; i++) {
                    const props = cloud.circleProps[i];
                    ctx.beginPath();
                    ctx.ellipse(cloud.x + cloud.size * 0.5 * (i / (cloud.numCircles - 1)) - cloud.size * 0.25, cloud.y + props.yOffset, Math.max(10, cloud.size * 0.3 + props.sizeVariation), Math.max(5, cloud.size * 0.2 + props.sizeVariation * 0.5), 0, 0, 2 * Math.PI);
                    ctx.fill();
                }
            });
            ctx.globalAlpha = 1.0;
        }

        // --- РИСУЕМ ТУМАН (если включен) ---
        if (settings.fogDensity > 0) {
            const fogStartHeight = canvas.height * (1 - settings.fogDensity * 1.2);
            const gradient = ctx.createLinearGradient(0, fogStartHeight, 0, canvas.height);
            const fogColor = '220, 220, 230';
            gradient.addColorStop(0, `rgba(${fogColor}, 0)`);
            gradient.addColorStop(1, `rgba(${fogColor}, ${settings.fogDensity * 0.85})`);
            ctx.fillStyle = gradient;
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }

        // --- РИСУЕМ ЧАСТИЦЫ ---
        if (settings.glowEnabled || settings.rgbEnabled) {
            ctx.shadowBlur = settings.rgbEnabled ? settings.glowIntensity : (settings.glowEnabled ? settings.glowIntensity : 0);
            ctx.shadowColor = settings.rgbEnabled ? settings.glowColors[settings.currentColorIndex] : (settings.glowEnabled ? 'rgba(255, 255, 220, 0.6)' : 'transparent');
        } else {
            ctx.shadowBlur = 0;
            ctx.shadowColor = 'transparent';
        }

        particles.forEach(p => {
            let particleAlpha = p.opacity;
            if (settings.fogDensity > 0.1) {
                 const fadeStart = canvas.height * 0.6;
                 if (p.y > fadeStart) {
                    const fadeFactor = Math.max(0, 1 - (p.y - fadeStart) / (canvas.height - fadeStart));
                    particleAlpha *= Math.max(0.1, fadeFactor + (1-fadeFactor)*(1 - Math.sqrt(settings.fogDensity)));
                 }
            }

            ctx.save();
            ctx.globalAlpha = particleAlpha;

            if (p.length) { // Дождь
                ctx.lineWidth = 2;
                ctx.strokeStyle = 'rgba(0, 0, 200, 0.7)';
                ctx.beginPath();
                ctx.moveTo(p.x, p.y);
                const endX = p.x + Math.sin(p.angle) * p.length;
                const endY = p.y + Math.cos(p.angle) * p.length;
                ctx.lineTo(endX, endY);
                ctx.stroke();
            } else if (p.char) { // Обычные частицы
                ctx.font = `${p.size}px Arial`;
                ctx.translate(p.x + p.size / 2, p.y + p.size / 2);
                ctx.rotate(p.rotation * Math.PI / 180);
                ctx.fillText(p.char, -p.size / 2, p.size / 2);
            }

            ctx.restore();
        });

        // Сброс тени и прозрачности после отрисовки
        ctx.shadowBlur = 0;
        ctx.globalAlpha = 1.0;
    }

    // --- АНИМАЦИОННЫЙ ЦИКЛ ---
    function animate() {
        updateParticles();
        updateClouds();
        drawParticles();
        animationFrameId = requestAnimationFrame(animate);
    }

    function stopAnimation() {
        if (animationFrameId) {
            cancelAnimationFrame(animationFrameId);
            animationFrameId = null;
            if (ctx) {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
            }
        }
    }

    function startAnimation() {
        if (!animationFrameId && settings.effectEnabled) {
             createParticles();
             createClouds();
             animate();
        }
    }

     // --- УПРАВЛЕНИЕ ЭФФЕКТОМ (ВКЛ/ВЫКЛ) ---
     function setEffectEnabled(enabled) {
         settings.effectEnabled = enabled;
         if (canvas) {
             canvas.style.display = enabled ? 'block' : 'none';
         }
         if (enabled) {
             startAnimation();
         } else {
             stopAnimation();
         }
         updateControlsState();
         saveSettings();
    }

    // --- ПАНЕЛЬ УПРАВЛЕНИЯ И ИКОНКА НАСТРОЕК ---
    function createSettingsButton() {
        settingsButton = document.createElement('button');
        settingsButton.id = 'fallingLeavesSettingsButton';
        settingsButton.innerHTML = '⚙️';
        settingsButton.title = 'Настройки эффектов';
        settingsButton.style.position = 'fixed';
        settingsButton.style.bottom = '10px';
        settingsButton.style.left = '10px';
        settingsButton.style.zIndex = '10001';
        settingsButton.style.background = 'rgba(30, 30, 30, 0.8)';
        settingsButton.style.color = 'white';
        settingsButton.style.border = 'none';
        settingsButton.style.borderRadius = '5px';
        settingsButton.style.width = '40px';
        settingsButton.style.height = '40px';
        settingsButton.style.fontSize = '20px';
        settingsButton.style.lineHeight = '40px';
        settingsButton.style.textAlign = 'center';
        settingsButton.style.cursor = 'pointer';
        settingsButton.style.boxShadow = '0 2px 5px rgba(0,0,0,0.5)';
        settingsButton.style.transition = 'transform 0.2s ease';

        settingsButton.addEventListener('mouseover', () => { settingsButton.style.transform = 'scale(1.1)'; });
        settingsButton.addEventListener('mouseout', () => { settingsButton.style.transform = 'scale(1)'; });

        settingsButton.addEventListener('click', toggleControlPanel);

        document.body.appendChild(settingsButton);
    }

    function createControlPanel() {
        controlPanel = document.createElement('div');
        controlPanel.id = 'fallingLeavesControlPanel';
        controlPanel.style.position = 'fixed';
        controlPanel.style.bottom = '60px';
        controlPanel.style.left = '10px';
        controlPanel.style.backgroundColor = 'rgba(40, 40, 40, 0.9)';
        controlPanel.style.color = 'white';
        controlPanel.style.padding = '15px';
        controlPanel.style.borderRadius = '8px';
        controlPanel.style.zIndex = '10000';
        controlPanel.style.fontFamily = 'Segoe UI, Tahoma, sans-serif';
        controlPanel.style.fontSize = '14px';
        controlPanel.style.minWidth = '300px'; /* Немного уменьшена ширина */
        controlPanel.style.boxShadow = '0 4px 10px rgba(0,0,0,0.6)';
        controlPanel.style.display = 'block';
        controlPanel.style.transition = 'opacity 0.2s ease-out, visibility 0.2s ease-out';
        controlPanel.style.opacity = settings.panelVisible ? '1' : '0';
        controlPanel.style.visibility = settings.panelVisible ? 'visible' : 'hidden';

        // --- Стили ---
        const styleSheet = document.createElement("style");
        styleSheet.type = "text/css";
        styleSheet.innerText = `
            /* ... (предыдущие стили @media и общие) ... */
             @media (max-width: 600px) {
                #fallingLeavesControlPanel {
                    font-size: 11px; /* Уменьшен шрифт */
                    padding: 10px;
                    min-width: 250px; /* Уменьшена минимальная ширина */
                    bottom: 55px;
                }
                 #fallingLeavesControlPanel .control-row label,
                 #fallingLeavesControlPanel .particle-type-selector label {
                     display: block;
                     margin-bottom: 3px;
                 }
                 #fallingLeavesControlPanel input[type="range"] { width: 100%; }
                 #fallingLeavesControlPanel .particle-type-selector { grid-template-columns: repeat(3, 1fr); gap: 5px;}
            }
            #fallingLeavesControlPanel input[type="range"] {
                width: 160px; height: 8px; vertical-align: middle; margin: 0 8px; cursor: pointer;
                background: #555; border-radius: 5px; -webkit-appearance: none;
            }
            #fallingLeavesControlPanel input[type="range"]::-webkit-slider-thumb {
                -webkit-appearance: none; border: none; height: 14px; width: 14px; border-radius: 50%; background: #eee; cursor: pointer;
            }
            #fallingLeavesControlPanel input[type="range"]::-moz-range-thumb {
                border: none; height: 14px; width: 14px; border-radius: 50%; background: #eee; cursor: pointer;
            }
            #fallingLeavesControlPanel .control-row, #fallingLeavesControlPanel .checkbox-row {
                 margin-bottom: 10px; display: flex; align-items: center; flex-wrap: wrap;
            }
             #fallingLeavesControlPanel .control-row label { min-width: 90px; display: inline-block; margin-right: 10px; color: #ddd; }
             #fallingLeavesControlPanel .value-display { min-width: 40px; display: inline-block; text-align: right; font-weight: bold; color: #eee; }
             #fallingLeavesControlPanel hr { border: none; border-top: 1px solid rgba(255,255,255,0.1); margin: 12px 0; }
             #fallingLeavesControlPanel input[type="checkbox"] { margin-right: 8px; cursor: pointer; vertical-align: middle;}
             #fallingLeavesControlPanel .particle-type-selector {
                  margin-top: 8px;
                  display: grid;
                  grid-template-columns: repeat(auto-fit, minmax(40px, 1fr)); /* Более компактное размещение */
                  gap: 6px;
                  font-size: 18px;
             }
             #fallingLeavesControlPanel .particle-type-selector label {
                  display: flex;
                  align-items: center;
                  cursor: pointer;
                  color: #ccc;
             }
             #fallingLeavesControlPanel .color-input-row {
                display: flex;
                align-items: center;
                margin-bottom: 8px;
             }
             #fallingLeavesControlPanel .color-input-row label {
                min-width: 120px;
                margin-right: 10px;
                color: #ddd;
             }
             #fallingLeavesControlPanel .color-input {
                flex-grow: 1;
                padding: 6px;
                border: 1px solid #777;
                border-radius: 4px;
                background-color: #333;
                color: #eee;
                font-size: 13px;
             }
             #fallingLeavesControlPanel fieldset {
                border: 1px solid rgba(255,255,255,0.2);
                border-radius: 4px;
                padding: 8px 12px 12px 12px;
                margin-top: 10px;
             }
             #fallingLeavesControlPanel legend {
                padding: 0 8px;
                color: #bbb;
             }
             #fallingLeavesControlPanel .effect-group {
                margin-bottom: 15px;
                border-bottom: 1px solid rgba(255,255,255,0.1);
                padding-bottom: 15px;
             }
             #fallingLeavesControlPanel .effect-group:last-child {
                border-bottom: none;
             }
             #fallingLeavesControlPanel .effect-title {
                font-weight: bold;
                color: #eee;
                margin-bottom: 8px;
             }
        `;
        document.head.appendChild(styleSheet);

        // --- Элементы управления ---

         // 1. Вкл/Выкл Эффекта
         const enableRow = document.createElement('div');
         enableRow.className = 'checkbox-row';
         const enableLabel = document.createElement('label');
         enableLabel.htmlFor = 'effectEnabledCheckbox';
         enableLabel.textContent = 'Включить все эффекты:';
         enableLabel.style.fontWeight = 'bold';
         const enableCheckbox = document.createElement('input');
         enableCheckbox.type = 'checkbox';
         enableCheckbox.id = 'effectEnabledCheckbox';
         enableCheckbox.checked = settings.effectEnabled;
         enableCheckbox.addEventListener('change', (e) => setEffectEnabled(e.target.checked));
         enableRow.appendChild(enableCheckbox);
         enableRow.appendChild(enableLabel);
         controlPanel.appendChild(enableRow);
         controlPanel.appendChild(document.createElement('hr'));

        // --- Группа "Основные частицы" ---
        const defaultParticlesGroup = document.createElement('div');
        defaultParticlesGroup.className = 'effect-group';
        const defaultParticlesTitle = document.createElement('div');
        defaultParticlesTitle.className = 'effect-title';
        defaultParticlesTitle.textContent = 'Основные частицы';
        defaultParticlesGroup.appendChild(defaultParticlesTitle);

        defaultParticlesGroup.appendChild(createSliderRow('Плотность:', 'densitySlider', 0, 150, settings.density, 1, (value) => {
            settings.density = value;
            createParticles();
            if (settings.density === 0 && !settings.rainEnabled && !settings.cloudsEnabled && animationFrameId) stopAnimation();
            else if ((settings.density > 0 || settings.rainEnabled || settings.cloudsEnabled) && !animationFrameId && settings.effectEnabled) startAnimation();
             saveSettings();
        }));
        defaultParticlesGroup.appendChild(createSliderRow('Скорость:', 'speedSlider', 0.1, 5, settings.speed, 0.1, (value) => {
            settings.speed = value; saveSettings();
        }));
        defaultParticlesGroup.appendChild(createSliderRow('Размер:', 'sizeSlider', 10, 40, settings.size, 1, (value) => {
            settings.size = value; saveSettings();
        }));
        defaultParticlesGroup.appendChild(createSliderRow('Ветер:', 'windSlider', -2, 2, settings.wind, 0.1, (value) => {
            settings.wind = value; saveSettings();
        }));

        const typesFieldset = document.createElement('fieldset');
        const typesLegend = document.createElement('legend');
        typesLegend.textContent = 'Выбор типов';
        typesFieldset.appendChild(typesLegend);
        const typesContainer = document.createElement('div');
        typesContainer.className = 'particle-type-selector';
        allParticleTypes.forEach(type => {
            const typeLabel = document.createElement('label');
            const typeCheckbox = document.createElement('input');
            typeCheckbox.type = 'checkbox';
            typeCheckbox.value = type;
            typeCheckbox.checked = settings.selectedTypes.includes(type);
            typeCheckbox.addEventListener('change', (e) => {
                 const char = e.target.value;
                 if (e.target.checked) {
                     if (!settings.selectedTypes.includes(char)) { settings.selectedTypes.push(char); }
                 } else {
                     settings.selectedTypes = settings.selectedTypes.filter(t => t !== char);
                 }
                 saveSettings();
            });
            typeLabel.appendChild(typeCheckbox);
            typeLabel.appendChild(document.createTextNode(type));
            typesContainer.appendChild(typeLabel);
        });
        typesFieldset.appendChild(typesContainer);
        defaultParticlesGroup.appendChild(typesFieldset);
        controlPanel.appendChild(defaultParticlesGroup);

        // --- Группа "Атмосферные эффекты" ---
        const atmosphericGroup = document.createElement('div');
        atmosphericGroup.className = 'effect-group';
        const atmosphericTitle = document.createElement('div');
        atmosphericTitle.className = 'effect-title';
        atmosphericTitle.textContent = 'Атмосферные эффекты';
        atmosphericGroup.appendChild(atmosphericTitle);

        atmosphericGroup.appendChild(createSliderRow('Туман:', 'fogSlider', 0, 100, settings.fogDensity * 100, 1, (value) => {
             settings.fogDensity = value / 100;
             saveSettings();
         }));

        const rainRow = document.createElement('div');
        rainRow.className = 'checkbox-row';
        const rainLabel = document.createElement('label');
        rainLabel.htmlFor = 'rainEnabledCheckbox';
        rainLabel.textContent = 'Дождь:';
        const rainCheckbox = document.createElement('input');
        rainCheckbox.type = 'checkbox';
        rainCheckbox.id = 'rainEnabledCheckbox';
        rainCheckbox.checked = settings.rainEnabled;
        rainCheckbox.addEventListener('change', (e) => {
            settings.rainEnabled = e.target.checked;
            createParticles();
            if ((settings.density > 0 || settings.rainEnabled || settings.cloudsEnabled) && !animationFrameId && settings.effectEnabled) startAnimation();
            else if (!settings.density && !settings.rainEnabled && !settings.cloudsEnabled && animationFrameId) stopAnimation();
            saveSettings();
        });
        rainRow.appendChild(rainCheckbox);
        rainRow.appendChild(rainLabel);
        atmosphericGroup.appendChild(rainRow);
        atmosphericGroup.appendChild(createSliderRow('  Плотность дождя:', 'rainDensitySlider', 0, 150, settings.rainDensity, 1, (value) => {
            settings.rainDensity = value; createParticles(); saveSettings();
        }));
        atmosphericGroup.appendChild(createSliderRow('  Скорость дождя:', 'rainSpeedSlider', 1, 10, settings.rainSpeed, 0.5, (value) => {
            settings.rainSpeed = value; saveSettings();
        }));
        atmosphericGroup.appendChild(createSliderRow('  Длина капель:', 'rainDropLengthSlider', 5, 30, settings.rainDropLength, 1, (value) => {
            settings.rainDropLength = value; saveSettings();
        }));

        const cloudsRow = document.createElement('div');
        cloudsRow.className = 'checkbox-row';
        const cloudsLabel = document.createElement('label');
        cloudsLabel.htmlFor = 'cloudsEnabledCheckbox';
        cloudsLabel.textContent = 'Облака:';
        const cloudsCheckbox = document.createElement('input');
        cloudsCheckbox.type = 'checkbox';
        cloudsCheckbox.id = 'cloudsEnabledCheckbox';
        cloudsCheckbox.checked = settings.cloudsEnabled;
        cloudsCheckbox.addEventListener('change', (e) => {
            settings.cloudsEnabled = e.target.checked;
            createClouds();
            if ((settings.density > 0 || settings.rainEnabled || settings.cloudsEnabled) && !animationFrameId && settings.effectEnabled) startAnimation();
            else if (!settings.density && !settings.rainEnabled && !settings.cloudsEnabled && animationFrameId) stopAnimation();
            saveSettings();
        });
        cloudsRow.appendChild(cloudsCheckbox);
        cloudsRow.appendChild(cloudsLabel);
        atmosphericGroup.appendChild(cloudsRow);
        atmosphericGroup.appendChild(createSliderRow('  Плотность облаков:', 'cloudDensitySlider', 0, 50, settings.cloudDensity, 1, (value) => {
            settings.cloudDensity = value; createClouds(); saveSettings();
        }));
        atmosphericGroup.appendChild(createSliderRow('  Скорость облаков:', 'cloudSpeedSlider', 0.1, 2, settings.cloudSpeed, 0.1, (value) => {
            settings.cloudSpeed = value; clouds.forEach(c => c.drift = settings.cloudSpeed + Math.random() * 0.1); saveSettings();
        }));
        atmosphericGroup.appendChild(createColorInputRow('  Цвет облаков:', 'cloudColorInput', settings.cloudColor, (value) => {
            settings.cloudColor = value; saveSettings();
        }));

        controlPanel.appendChild(atmosphericGroup);

        // --- Группа "Свечение" ---
        const glowGroup = document.createElement('div');
        glowGroup.className = 'effect-group';
        const glowTitle = document.createElement('div');
        glowTitle.className = 'effect-title';
        glowTitle.textContent = 'Свечение';
        glowGroup.appendChild(glowTitle);

         const rgbEnableRow = document.createElement('div');
         rgbEnableRow.className = 'checkbox-row';
         const rgbEnableLabel = document.createElement('label');
         rgbEnableLabel.htmlFor = 'rgbEnabledCheckbox';
         rgbEnableLabel.textContent = 'RGB свечение:';
         const rgbEnableCheckbox = document.createElement('input');
         rgbEnableCheckbox.type = 'checkbox';
         rgbEnableCheckbox.id = 'rgbEnabledCheckbox';
         rgbEnableCheckbox.checked = settings.rgbEnabled;
         rgbEnableCheckbox.addEventListener('change', (e) => {
             settings.rgbEnabled = e.target.checked;
             updateGlowControlsState();
             saveSettings();
         });
         rgbEnableRow.appendChild(rgbEnableCheckbox);
         rgbEnableRow.appendChild(rgbEnableLabel);
         glowGroup.appendChild(rgbEnableRow);

         const glowEnableRow = document.createElement('div');
         glowEnableRow.className = 'checkbox-row';
         const glowEnableLabel = document.createElement('label');
         glowEnableLabel.htmlFor = 'glowCheckbox';
         glowEnableLabel.textContent = 'Обычное свечение:';
         const glowEnableCheckbox = document.createElement('input');
         glowEnableCheckbox.type = 'checkbox';
         glowEnableCheckbox.id = 'glowCheckbox';
         glowEnableCheckbox.checked = settings.glowEnabled;
         glowEnableCheckbox.addEventListener('change', (e) => {
             settings.glowEnabled = e.target.checked;
             updateGlowControlsState();
             saveSettings();
         });
         glowEnableRow.appendChild(glowEnableCheckbox);
         glowEnableRow.appendChild(glowEnableLabel);
         glowGroup.appendChild(glowEnableRow);

         glowGroup.appendChild(createSliderRow('Интенсивность:', 'glowIntensitySlider', 0, 20, settings.glowIntensity, 1, (value) => {
             settings.glowIntensity = value; saveSettings();
         }));
         glowGroup.appendChild(createColorInputRow('Цвета RGB (через запятую):', 'rgbColorsInput', settings.glowColors.join(','), (value) => {
             settings.glowColors = value.split(',').map(s => s.trim());
             saveSettings();
         }));
         glowGroup.appendChild(createSliderRow('Скорость RGB:', 'rgbSpeedSlider', 0.1, 5, settings.rgbSpeed, 0.1, (value) => {
             settings.rgbSpeed = value; saveSettings();
         }));

        controlPanel.appendChild(glowGroup);

        // Автор
        const authorDiv = document.createElement('div');
        authorDiv.style.marginTop = '15px';
        authorDiv.style.fontSize = '11px';
        authorDiv.style.color = 'rgba(255, 255, 255, 0.7)';
        authorDiv.textContent = 'Автор: Тех. [06] M. Ageev';
        controlPanel.appendChild(authorDiv);

        document.body.appendChild(controlPanel);
        updateControlsState();
        updateGlowControlsState();
    }

    function updateGlowControlsState() {
        const glowCheckbox = document.getElementById('glowCheckbox');
        const rgbCheckbox = document.getElementById('rgbEnabledCheckbox');
        const glowIntensitySlider = document.getElementById('glowIntensitySlider');
        const rgbColorsInput = document.getElementById('rgbColorsInput');
        const rgbSpeedSlider = document.getElementById('rgbSpeedSlider');

        if (glowCheckbox && rgbCheckbox && glowIntensitySlider && rgbColorsInput && rgbSpeedSlider) {
            const isRgbEnabled = settings.rgbEnabled;
            const isGlowEnabled = settings.glowEnabled;

            glowCheckbox.disabled = isRgbEnabled;
            glowIntensitySlider.disabled = isRgbEnabled && !isGlowEnabled;
            rgbCheckbox.disabled = isGlowEnabled;
            rgbColorsInput.disabled = !isRgbEnabled;
            rgbSpeedSlider.disabled = !isRgbEnabled;

            glowCheckbox.style.opacity = isRgbEnabled ? '0.5' : '1';
            glowIntensitySlider.style.opacity = (isRgbEnabled && !isGlowEnabled) ? '0.5' : '1';
            rgbCheckbox.style.opacity = isGlowEnabled ? '0.5' : '1';
            rgbColorsInput.style.opacity = !isRgbEnabled ? '0.5' : '1';
            rgbSpeedSlider.style.opacity = !isRgbEnabled ? '0.5' : '1';

            glowCheckbox.style.cursor = isRgbEnabled ? 'not-allowed' : 'pointer';
            glowIntensitySlider.style.cursor = (isRgbEnabled && !isGlowEnabled) ? 'not-allowed' : 'pointer';
            rgbCheckbox.style.cursor = isGlowEnabled ? 'not-allowed' : 'pointer';
            rgbColorsInput.style.cursor = isRgbEnabled ? 'pointer' : 'not-allowed';
            rgbSpeedSlider.style.cursor = isRgbEnabled ? 'pointer' : 'not-allowed';
        }
    }

    // Вспомогательные функции
    function createSliderRow(labelText, id, min, max, value, step, onChange) {
        const row = document.createElement('div');
        row.className = 'control-row';
        const label = document.createElement('label');
        label.htmlFor = id;
        label.textContent = labelText;
        const slider = document.createElement('input');
        slider.type = 'range';
        slider.id = id;
        slider.min = min.toString();
        slider.max = max.toString();
        slider.step = step.toString();
        slider.value = value.toString();
        const valueDisplay = document.createElement('span');
        valueDisplay.className = 'value-display';
        const displayValue = Number.isInteger(step) || step >= 1 ? Math.round(value).toString() : parseFloat(value).toFixed(1);
        valueDisplay.textContent = displayValue;
        slider.addEventListener('input', (e) => {
            const newValue = parseFloat(e.target.value);
            const displayValue = Number.isInteger(step) || step >= 1 ? Math.round(newValue).toString() : newValue.toFixed(1);
            valueDisplay.textContent = displayValue;
            onChange(newValue);
        });
        row.appendChild(label);
        row.appendChild(slider);
        row.appendChild(valueDisplay);
        return row;
    }

    function createColorInputRow(labelText, id, value, onChange) {
        const row = document.createElement('div');
        row.className = 'color-input-row';
        const label = document.createElement('label');
        label.htmlFor = id;
        label.textContent = labelText;
        const input = document.createElement('input');
        input.type = 'text';
        input.id = id;
        input.className = 'color-input';
        input.value = value;
        input.addEventListener('change', (e) => onChange(e.target.value));
        row.appendChild(label);
        row.appendChild(input);
        return row;
    }

    function toggleControlPanel() {
        settings.panelVisible = !settings.panelVisible;
        if (controlPanel) {
             requestAnimationFrame(() => {
                 if (settings.panelVisible) {
                     controlPanel.style.opacity = '1';
                     controlPanel.style.visibility = 'visible';
                 } else {
                     controlPanel.style.opacity = '0';
                     controlPanel.style.visibility = 'hidden';
                 }
             });
        }
        saveSettings();
    }

    function updateControlsState() {
         if (!controlPanel) return;
         const controlsToDisable = controlPanel.querySelectorAll('input:not(#effectEnabledCheckbox), fieldset, button');
         controlsToDisable.forEach(control => {
             control.disabled = !settings.effectEnabled;
             control.style.opacity = settings.effectEnabled ? '1' : '0.5';
             control.style.cursor = settings.effectEnabled ? '' : 'not-allowed';
             if (control.tagName === 'FIELDSET') {
                 const innerControls = control.querySelectorAll('input, label');
                 innerControls.forEach(inner => {
                      inner.style.opacity = settings.effectEnabled ? '1' : '0.6';
                      inner.style.cursor = settings.effectEnabled ? 'pointer' : 'not-allowed';
                 });
             }
         });
    }

    // --- ИНИЦИАЛИЗАЦИЯ ---
    async function init() {
        console.log("Falling Leaves Script Initializing (v2.5)...");
        await loadSettings();
        setupCanvas();
        createControlPanel();
        createSettingsButton();
        if (settings.effectEnabled) {
             startAnimation();
        }
        console.log("Falling Leaves Script Ready!");
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();