// /public/js/ui.js
// Этот файл отвечает за обновление DOM на основе состояния игры,
// полученного от client.js (который, в свою очередь, получает его от сервера).

(function() {
    // --- DOM Элементы ---
    const uiElements = {
        player: { // Панель для персонажа, которым управляет ЭТОТ клиент
            panel: document.getElementById('player-panel'),
            name: document.getElementById('player-name'),
            avatar: document.getElementById('player-panel')?.querySelector('.player-avatar'),
            hpFill: document.getElementById('player-hp-fill'), hpText: document.getElementById('player-hp-text'),
            resourceFill: document.getElementById('player-resource-fill'), resourceText: document.getElementById('player-resource-text'),
            status: document.getElementById('player-status'),
            effectsContainer: document.getElementById('player-effects'),
            buffsList: document.getElementById('player-effects')?.querySelector('.player-buffs'),
            // ИСПРАВЛЕНО: Селектор для списка дебаффов игрока
            debuffsList: document.getElementById('player-effects')?.querySelector('.player-debuffs')
        },
        opponent: { // Панель для персонажа-противника ЭТОГО клиента
            panel: document.getElementById('opponent-panel'),
            name: document.getElementById('opponent-name'),
            avatar: document.getElementById('opponent-panel')?.querySelector('.opponent-avatar'),
            hpFill: document.getElementById('opponent-hp-fill'), hpText: document.getElementById('opponent-hp-text'),
            resourceFill: document.getElementById('opponent-resource-fill'), resourceText: document.getElementById('opponent-resource-text'),
            status: document.getElementById('opponent-status'),
            effectsContainer: document.getElementById('opponent-effects'),
            // ИСПРАВЛЕНО: Селектор для списка баффов оппонента
            buffsList: document.getElementById('opponent-effects')?.querySelector('.opponent-buffs'),
            // ИСПРАВЛЕНО: Селектор для списка дебаффов оппонента
            debuffsList: document.getElementById('opponent-effects')?.querySelector('.opponent-debuffs')
        },
        controls: {
            turnIndicator: document.getElementById('turn-indicator'),
            buttonAttack: document.getElementById('button-attack'),
            buttonBlock: document.getElementById('button-block'), // Защита пока не активна
            abilitiesGrid: document.getElementById('abilities-grid'),
        },
        log: {
            list: document.getElementById('log-list'),
        },
        gameOver: {
            screen: document.getElementById('game-over-screen'),
            message: document.getElementById('result-message'),
            returnToMenuButton: document.getElementById('return-to-menu-button'),
            modalContent: document.getElementById('game-over-screen')?.querySelector('.modal-content')
        },
        gameHeaderTitle: document.querySelector('.game-header h1'),
        playerResourceTypeIcon: document.getElementById('player-resource-bar')?.closest('.stat-bar-container')?.querySelector('.bar-icon i'),
        opponentResourceTypeIcon: document.getElementById('opponent-resource-bar')?.closest('.stat-bar-container')?.querySelector('.bar-icon i'),
        playerResourceBarContainer: document.getElementById('player-resource-bar')?.closest('.stat-bar-container'),
        opponentResourceBarContainer: document.getElementById('opponent-resource-bar')?.closest('.stat-bar-container'),
    };

    function addToLog(message, type = 'info') {
        const logListElement = uiElements.log.list;
        if (!logListElement) return;
        const li = document.createElement('li');
        li.textContent = message;
        const config = window.GAME_CONFIG || {};
        // Формируем класс для лога на основе типа (используем константы из конфига или фоллбэк)
        const logTypeClass = config[`LOG_TYPE_${type.toUpperCase()}`] ? `log-${config[`LOG_TYPE_${type.toUpperCase()}`]}` : `log-${type}`;
        li.className = logTypeClass;
        logListElement.appendChild(li);
        // Прокрутка лога вниз
        requestAnimationFrame(() => { logListElement.scrollTop = logListElement.scrollHeight; });
    }

    function updateFighterPanelUI(panelRole, fighterState, fighterBaseStats, isControlledByThisClient) {
        const elements = uiElements[panelRole]; // 'player' или 'opponent'
        const config = window.GAME_CONFIG || {};

        // Базовая проверка наличия необходимых элементов и данных
        if (!elements || !elements.hpFill || !elements.hpText || !elements.resourceFill || !elements.resourceText || !elements.status || !fighterState || !fighterBaseStats) {
            // Если панель должна быть видима, но нет данных, можно ее скрыть или показать плейсхолдер
            if (elements && elements.panel && elements.panel.style.display !== 'none') {
                // console.warn(`updateFighterPanelUI: Нет данных для видимой панели ${panelRole}.`);
                // elements.panel.style.opacity = '0.5'; // Пример: сделать полупрозрачной, если нет данных
            }
            // ВАЖНО: Очистить содержимое панели, если данных нет.
            if (elements) {
                if(elements.name) elements.name.innerHTML = (panelRole === 'player') ? '<i class="fas fa-question icon-player"></i> Ожидание данных...' : '<i class="fas fa-question icon-opponent"></i> Ожидание игрока...';
                if(elements.hpText) elements.hpText.textContent = 'N/A';
                if(elements.resourceText) elements.resourceText.textContent = 'N/A';
                if(elements.status) elements.status.textContent = 'Неизвестно';
                if(elements.buffsList) elements.buffsList.innerHTML = 'Нет';
                if(elements.debuffsList) elements.debuffsList.innerHTML = 'Нет';
                if(elements.avatar) elements.avatar.src = 'images/default_avatar.png';
                if(panelRole === 'player' && uiElements.playerResourceTypeIcon) uiElements.playerResourceTypeIcon.className = 'fas fa-question';
                if(panelRole === 'opponent' && uiElements.opponentResourceTypeIcon) uiElements.opponentResourceTypeIcon.className = 'fas fa-question';
                if(panelRole === 'player' && uiElements.playerResourceBarContainer) uiElements.playerResourceBarContainer.classList.remove('mana', 'stamina', 'dark-energy');
                if(panelRole === 'opponent' && uiElements.opponentResourceBarContainer) uiElements.opponentResourceBarContainer.classList.remove('mana', 'stamina', 'dark-energy');
                if(elements.panel) elements.panel.style.opacity = '0.5'; // Затемняем
            }
            return;
        }
        if (elements.panel) elements.panel.style.opacity = '1'; // Делаем видимой, если данные есть


        // Обновление имени и иконки персонажа
        if (elements.name) {
            let iconClass = 'fa-question'; // Иконка по умолчанию
            const characterKey = fighterBaseStats.characterKey;

            // Определяем класс иконки в зависимости от персонажа
            if (characterKey === 'elena') { iconClass = 'fa-hat-wizard icon-player'; }
            else if (characterKey === 'almagest') { iconClass = 'fa-staff-aesculapius icon-almagest'; }
            else if (characterKey === 'balard') { iconClass = 'fa-khanda icon-opponent'; }
            else { /* console.warn(`updateFighterPanelUI: Неизвестный characterKey "${characterKey}" для иконки имени.`); */ }

            let nameHtml = `<i class="fas ${iconClass}"></i> ${fighterBaseStats.name || 'Неизвестно'}`;
            if (isControlledByThisClient) nameHtml += " (Вы)";
            elements.name.innerHTML = nameHtml;
        }

        // Обновление аватара
        if (elements.avatar && fighterBaseStats.avatarPath) {
            elements.avatar.src = fighterBaseStats.avatarPath;
            // Обновляем рамку аватара в зависимости от персонажа
            elements.avatar.classList.remove('avatar-elena', 'avatar-almagest', 'avatar-balard');
            elements.avatar.classList.add(`avatar-${fighterBaseStats.characterKey}`);
        } else if (elements.avatar) {
            elements.avatar.src = 'images/default_avatar.png';
            elements.avatar.classList.remove('avatar-elena', 'avatar-almagest', 'avatar-balard');
        }


        // Обновление полос здоровья и ресурса
        const maxHp = Math.max(1, fighterBaseStats.maxHp);
        const maxRes = Math.max(1, fighterBaseStats.maxResource);
        const currentHp = Math.max(0, fighterState.currentHp);
        const currentRes = Math.max(0, fighterState.currentResource);

        elements.hpFill.style.width = `${(currentHp / maxHp) * 100}%`;
        elements.hpText.textContent = `${Math.round(currentHp)} / ${fighterBaseStats.maxHp}`; // Здоровье округляем
        elements.resourceFill.style.width = `${(currentRes / maxRes) * 100}%`;
        elements.resourceText.textContent = `${currentRes} / ${fighterBaseStats.maxResource}`; // Ресурс не округляем


        // Обновление типа ресурса и иконки (mana/stamina/dark-energy)
        const resourceBarContainerToUpdate = (panelRole === 'player') ? uiElements.playerResourceBarContainer : uiElements.opponentResourceBarContainer;
        const resourceIconElementToUpdate = (panelRole === 'player') ? uiElements.playerResourceTypeIcon : uiElements.opponentResourceTypeIcon;

        if (resourceBarContainerToUpdate && resourceIconElementToUpdate) {
            resourceBarContainerToUpdate.classList.remove('mana', 'stamina', 'dark-energy');
            let resourceClass = 'mana'; let iconClass = 'fa-flask';
            if (fighterBaseStats.resourceName === 'Ярость') { resourceClass = 'stamina'; iconClass = 'fa-fire-alt'; }
            else if (fighterBaseStats.resourceName === 'Темная Энергия') { resourceClass = 'dark-energy'; iconClass = 'fa-skull'; } // или fa-wand-magic-sparkles, fa-star-half-alt и т.д.
            else { console.warn(`updateFighterPanelUI: Unknown resource name "${fighterBaseStats.resourceName}" for icon/color.`); iconClass = 'fa-question-circle'; }
            resourceBarContainerToUpdate.classList.add(resourceClass);
            resourceIconElementToUpdate.className = `fas ${iconClass}`;
        }

        // Обновление статуса (Готов/Защищается)
        const statusText = fighterState.isBlocking ? (config.STATUS_BLOCKING || 'Защищается') : (config.STATUS_READY || 'Готов(а)');
        elements.status.textContent = statusText;
        elements.status.classList.toggle(config.CSS_CLASS_BLOCKING || 'blocking', fighterState.isBlocking);


        // Обновление подсветки и рамки панели (в зависимости от персонажа)
        if (elements.panel) {
            let borderColorVar = 'var(--panel-border)';
            elements.panel.classList.remove('panel-elena', 'panel-almagest', 'panel-balard');

            if (fighterBaseStats.characterKey === 'elena') { elements.panel.classList.add('panel-elena'); borderColorVar = 'var(--accent-player)'; }
            else if (fighterBaseStats.characterKey === 'almagest') { elements.panel.classList.add('panel-almagest'); borderColorVar = 'var(--accent-almagest)'; }
            else if (fighterBaseStats.characterKey === 'balard') { elements.panel.classList.add('panel-balard'); borderColorVar = 'var(--accent-opponent)'; }
            else { console.warn(`updateFighterPanelUI: Unknown character key "${fighterBaseStats.characterKey}" for panel border color.`); }


            let glowColorVar = 'rgba(0, 0, 0, 0.4)'; // Базовая тень
            if (fighterBaseStats.characterKey === 'elena') glowColorVar = 'var(--panel-glow-player)';
            // В твоем CSS --panel-glow-opponent используется для обоих Баларда и Альмагест
            else if (fighterBaseStats.characterKey === 'almagest' || fighterBaseStats.characterKey === 'balard') glowColorVar = 'var(--panel-glow-opponent)';

            elements.panel.style.borderColor = borderColorVar;
            elements.panel.style.boxShadow = `0 0 15px ${glowColorVar}, inset 0 0 10px rgba(0, 0, 0, 0.3)`;
        }
    }

    /**
     * Генерирует HTML для списка эффектов.
     * @param {Array<object>} effectsArray - Массив объектов эффектов, УЖЕ отфильтрованных и отсортированных.
     * @returns {string} HTML-строка для отображения списка эффектов.
     */
    function generateEffectsHTML(effectsArray) {
        const config = window.GAME_CONFIG || {};
        if (!effectsArray || effectsArray.length === 0) return 'Нет';

        // ВАЖНО: Сортировка теперь выполняется ВНЕ этой функции (в updateEffectsUI)

        return effectsArray.map(eff => {
            let effectClasses = config.CSS_CLASS_EFFECT || 'effect'; // Базовый класс для всех эффектов
            const title = `${eff.name}${eff.description ? ` - ${eff.description}` : ''} (Осталось: ${eff.turnsLeft} х.)`;
            const displayText = `${eff.name} (${eff.turnsLeft} х.)`;

            // Добавляем специфичные классы для стилизации по типу эффекта
            // Логика определения класса должна соответствовать логике разделения на баффы/дебаффы
            if (eff.isFullSilence || eff.id.startsWith('playerSilencedOn_') || eff.type === config.ACTION_TYPE_DISABLE) {
                // Эффекты полного безмолвия, заглушения абилок или типа DISABLE
                effectClasses += ' effect-stun'; // Класс для стана/безмолвия (красный/желтый)
            } else if (eff.grantsBlock) { // Эффекты, дающие блок
                effectClasses += ' effect-block'; // Класс для эффектов блока (синий)
            } else if (eff.type === config.ACTION_TYPE_DEBUFF) { // Явные дебаффы (например, сжигание ресурса)
                effectClasses += ' effect-debuff'; // Класс для ослаблений (красноватый)
            } else if (eff.type === config.ACTION_TYPE_BUFF) { // Явные баффы (например, усиление атаки)
                effectClasses += ' effect-buff'; // Класс для усилений (зеленый)
            } else if (eff.type === config.ACTION_TYPE_HEAL) { // Эффекты лечения (HoT)
                effectClasses += ' effect-buff'; // HoT стилизуем как бафф (зеленый)
            }
                // Если есть другие типы (DoT, Drain и т.п.), которые не входят в эти категории,
                // их нужно добавить или стилизовать как info.
                // DoT можно стилизовать как effect-debuff or effect-damage, Drain as effect-debuff.
                // Например: else if (eff.type === config.ACTION_TYPE_DAMAGE) { effectClasses += ' effect-debuff'; } // DoT как дебафф
            // else if (eff.type === config.ACTION_TYPE_DRAIN) { effectClasses += ' effect-debuff'; } // Drain как дебафф
            else {
                //console.warn(`generateEffectsHTML: Эффект ID "${eff.id}" с типом "${eff.type}" не имеет специфичного класса стилизации.`);
                effectClasses += ' effect-info'; // Класс по умолчанию или информационный (серый/синий)
            }

            return `<span class="${effectClasses}" title="${title}">${displayText}</span>`;
        }).join(' ');
    }

    function updateEffectsUI(currentGameState) {
        if (!currentGameState || !window.GAME_CONFIG) { return; }
        const mySlotId = window.myPlayerId;
        const config = window.GAME_CONFIG;
        if (!mySlotId) { return; }

        const opponentSlotId = mySlotId === config.PLAYER_ID ? config.OPPONENT_ID : config.PLAYER_ID;

        const myState = currentGameState[mySlotId];
        const opponentState = currentGameState[opponentSlotId];

        // --- Логика сортировки эффектов (для использования как для баффов, так и для дебаффов) ---
        // Сортируем эффекты по типу: сначала позитивные, потом негативные, потом контроля
        const typeOrder = {
            [config.ACTION_TYPE_BUFF]: 1,
            grantsBlock: 2,
            [config.ACTION_TYPE_HEAL]: 3, // HoT эффекты
            [config.ACTION_TYPE_DEBUFF]: 4, // DoT, ресурсные дебаффы
            [config.ACTION_TYPE_DISABLE]: 5 // Silence, Stun
            // Добавьте другие типы, если нужно сортировать
        };
        const sortEffects = (a, b) => {
            // Определяем порядок для эффекта A
            let orderA = typeOrder[a.type] || 99;
            if (a.grantsBlock) orderA = typeOrder.grantsBlock;
            // isFullSilence и playerSilencedOn_X - это эффекты типа DISABLE, но их можно поставить выше в приоритете дебаффов
            if (a.isFullSilence || a.id.startsWith('playerSilencedOn_')) orderA = typeOrder[config.ACTION_TYPE_DISABLE];
            // Добавьте сюда другие специфичные проверки, если нужно изменить стандартный порядок по типу

            // Определяем порядок для эффекта B
            let orderB = typeOrder[b.type] || 99;
            if (b.grantsBlock) orderB = typeOrder.grantsBlock;
            if (b.isFullSilence || b.id.startsWith('playerSilencedOn_')) orderB = typeOrder[config.ACTION_TYPE_DISABLE];

            return (orderA || 99) - (orderB || 99); // Сортируем по порядку
        };
        // --- Конец логики сортировки ---


        // --- Обработка эффектов Игрока (My Player) ---
        if (uiElements.player && uiElements.player.buffsList && uiElements.player.debuffsList && myState && myState.activeEffects) {
            const myBuffs = [];
            const myDebuffs = [];

            // ИСПРАВЛЕНО: Проходим по массиву activeEffects один раз и пушим в нужный список
            myState.activeEffects.forEach(e => {
                // Определяем, является ли эффект баффом
                const isBuff = e.type === config.ACTION_TYPE_BUFF || e.grantsBlock || e.type === config.ACTION_TYPE_HEAL; // HoT как бафф

                // Определяем, является ли эффект дебаффом
                // Учитываем типы DEBUFF, DISABLE, а также специфические флаги/ID для полного безмолвия и заглушения конкретных абилок
                const isDebuff = e.type === config.ACTION_TYPE_DEBUFF || e.type === config.ACTION_TYPE_DISABLE || e.isFullSilence || e.id.startsWith('playerSilencedOn_');

                // Добавляем эффект в соответствующий список (каждый эффект должен попасть только в один)
                if (isBuff) {
                    myBuffs.push(e);
                } else if (isDebuff) {
                    myDebuffs.push(e);
                } else {
                    // Если эффект не попал ни в одну категорию (например, новый тип?)
                    //console.warn(`updateEffectsUI: Эффект ID "${e.id}" с типом "${e.type}" не отнесен ни к баффам, ни к дебаффам для Игрока.`);
                    myDebuffs.push(e); // Добавим в дебаффы по умолчанию
                }
            });

            // Сортируем списки баффов и дебаффов перед генерацией HTML
            myBuffs.sort(sortEffects);
            myDebuffs.sort(sortEffects);

            uiElements.player.buffsList.innerHTML = generateEffectsHTML(myBuffs);
            uiElements.player.debuffsList.innerHTML = generateEffectsHTML(myDebuffs);

        } else if (uiElements.player && uiElements.player.buffsList && uiElements.player.debuffsList) {
            // Если нет активных эффектов или состояния, очищаем списки
            uiElements.player.buffsList.innerHTML = 'Нет';
            uiElements.player.debuffsList.innerHTML = 'Нет';
        }


        // --- Обработка эффектов Оппонента (Opponent Player) ---
        // Логика аналогична игроку, но условия дебаффов могут немного отличаться
        // (например, префикс ID заглушения абилок)
        if (uiElements.opponent && uiElements.opponent.buffsList && uiElements.opponent.debuffsList && opponentState && opponentState.activeEffects) {
            const opponentBuffs = [];
            const opponentDebuffs = [];

            // ИСПРАВЛЕНО: Проходим по массиву activeEffects оппонента один раз и пушим в нужный список
            opponentState.activeEffects.forEach(e => {
                const isBuff = e.type === config.ACTION_TYPE_BUFF || e.grantsBlock || e.type === config.ACTION_TYPE_HEAL; // HoT как бафф

                // Определяем, является ли эффект дебаффом для ОППОНЕНТА
                // Учитываем типы DEBUFF, DISABLE, isFullSilence.
                // id.startsWith('playerSilencedOn_') специфично для игрока,
                // id.startsWith('effect_') используется для дебаффов, наложенных на цель (например, Seal of Weakness)
                const isDebuff = e.type === config.ACTION_TYPE_DEBUFF || e.type === config.ACTION_TYPE_DISABLE || e.isFullSilence || e.id.startsWith('effect_');
                // Если у оппонента есть свои специфичные эффекты заглушения с другим префиксом, его тоже нужно добавить сюда.

                if (isBuff) {
                    opponentBuffs.push(e);
                } else if (isDebuff) {
                    opponentDebuffs.push(e);
                } else {
                    //console.warn(`updateEffectsUI: Эффект ID "${e.id}" с типом "${e.type}" не отнесен ни к баффам, ни к дебаффам для Оппонента.`);
                    opponentDebuffs.push(e); // Добавим в дебаффы по умолчанию
                }
            });

            // Сортируем списки баффов и дебаффов оппонента
            opponentBuffs.sort(sortEffects);
            opponentDebuffs.sort(sortEffects);

            uiElements.opponent.buffsList.innerHTML = generateEffectsHTML(opponentBuffs);
            uiElements.opponent.debuffsList.innerHTML = generateEffectsHTML(opponentDebuffs);

        } else if (uiElements.opponent && uiElements.opponent.buffsList && uiElements.opponent.debuffsList) {
            // Если нет активных эффектов или состояния оппонента, очищаем списки
            uiElements.opponent.buffsList.innerHTML = 'Нет';
            uiElements.opponent.debuffsList.innerHTML = 'Нет';
        }
    }

    function updateUI() {
        const currentGameState = window.gameState; // Глобальное состояние игры
        const gameDataGlobal = window.gameData;   // Глобальные данные ( статы, абилки ) для этого клиента
        const configGlobal = window.GAME_CONFIG;   // Глобальный конфиг
        const myActualPlayerId = window.myPlayerId; // Технический ID слота этого клиента

        if (!currentGameState || !gameDataGlobal || !configGlobal || !myActualPlayerId) {
            // console.warn("updateUI: Отсутствуют глобальные gameState, gameData, GAME_CONFIG или myActualPlayerId.");
            // Сбрасываем UI панелей, если данные отсутствуют
            updateFighterPanelUI('player', null, null, true);
            updateFighterPanelUI('opponent', null, null, false);
            // Скрываем/очищаем остальные элементы UI игры
            if(uiElements.gameHeaderTitle) uiElements.gameHeaderTitle.innerHTML = `<span>Ожидание данных...</span>`;
            if(uiElements.controls.turnIndicator) uiElements.controls.turnIndicator.textContent = "Ожидание данных...";
            if(uiElements.controls.buttonAttack) uiElements.controls.buttonAttack.disabled = true;
            if(uiElements.controls.buttonBlock) uiElements.controls.buttonBlock.disabled = true;
            if(uiElements.controls.abilitiesGrid) uiElements.controls.abilitiesGrid.innerHTML = '<p class="placeholder-text">Загрузка способностей...</p>';

            return;
        }
        if (!uiElements.player.panel || !uiElements.opponent.panel || !uiElements.controls.turnIndicator || !uiElements.controls.abilitiesGrid || !uiElements.log.list) {
            console.warn("updateUI: Некоторые базовые uiElements не найдены.");
            return;
        }

        // Определяем, чей сейчас ход по ID слота
        const actorSlotWhoseTurnItIs = currentGameState.isPlayerTurn ? configGlobal.PLAYER_ID : configGlobal.OPPONENT_ID;
        // Определяем ID слота оппонента для этого клиента
        const opponentActualSlotId = myActualPlayerId === configGlobal.PLAYER_ID ? configGlobal.OPPONENT_ID : configGlobal.PLAYER_ID;

        // Обновление панели "моего" персонажа
        const myStateInGameState = currentGameState[myActualPlayerId];
        const myBaseStatsForUI = gameDataGlobal.playerBaseStats; // playerBaseStats в gameData - это всегда статы персонажа этого клиента
        if (myStateInGameState && myBaseStatsForUI) {
            updateFighterPanelUI('player', myStateInGameState, myBaseStatsForUI, true);
        } else {
            updateFighterPanelUI('player', null, null, true); // Нет данных, показываем состояние ожидания
        }

        // Обновление панели "моего оппонента"
        const opponentStateInGameState = currentGameState[opponentActualSlotId];
        const opponentBaseStatsForUI = gameDataGlobal.opponentBaseStats; // opponentBaseStats в gameData - это всегда статы оппонента этого клиента

        // Если игра окончена и игрок победил, возможно, панель оппонента уже анимирована на исчезновение.
        // Не сбрасываем ее opacity/transform здесь, если она в состоянии dissolving.
        const isOpponentPanelDissolving = uiElements.opponent.panel?.classList.contains('dissolving');

        if (opponentStateInGameState && opponentBaseStatsForUI) {
            // Если игра не окончена, а панель оппонента "тает" или не полностью видна, восстанавливаем это
            // Но не если она активно в анимации растворения (dissolving)
            if (uiElements.opponent.panel && (uiElements.opponent.panel.style.opacity !== '1' || (uiElements.opponent.panel.classList.contains('dissolving') && currentGameState.isGameOver === false) )) {
                // console.log("[UI UPDATE DEBUG] Opponent panel not fully visible/dissolving but game not over. Restoring opacity/transform.");
                const panel = uiElements.opponent.panel;
                if (panel.classList.contains('dissolving')) {
                    panel.classList.remove('dissolving');
                    panel.style.transition = 'none'; // Отключаем переход временно
                    panel.offsetHeight; // Trigger reflow
                    panel.style.opacity = '1';
                    panel.style.transform = 'scale(1) translateY(0)';
                    panel.style.transition = ''; // Восстанавливаем переход
                } else {
                    panel.style.opacity = '1';
                    panel.style.transform = 'scale(1) translateY(0)'; // В случае если просто opacity < 1
                }
            } else if (uiElements.opponent.panel && !isOpponentPanelDissolving) {
                uiElements.opponent.panel.style.opacity = '1'; // Убеждаемся, что видна, если есть данные и не растворяется
            }
            updateFighterPanelUI('opponent', opponentStateInGameState, opponentBaseStatsForUI, false);
        } else {
            // Нет данных оппонента ( например, PvP игра ожидает игрока). Затемняем панель и очищаем.
            // Но не сбрасываем opacity/transform, если она активно в анимации растворения
            if (!isOpponentPanelDissolving) {
                updateFighterPanelUI('opponent', null, null, false); // Нет данных, показываем состояние ожидания/пустоты
            } else {
                // Если панель растворяется, не обновляем ее содержимое и оставляем текущие стили opacity/transform
                console.log("[UI UPDATE DEBUG] Opponent panel is dissolving, skipping content update.");
            }

        }


        // Обновление эффектов
        updateEffectsUI(currentGameState);

        // Обновление заголовка игры ( Имя1 vs Имя2)
        if (uiElements.gameHeaderTitle && gameDataGlobal.playerBaseStats && gameDataGlobal.opponentBaseStats) {
            const myName = gameDataGlobal.playerBaseStats.name; // Имя моего персонажа
            const opponentName = gameDataGlobal.opponentBaseStats.name; // Имя моего оппонента
            const myKey = gameDataGlobal.playerBaseStats.characterKey;
            const opponentKey = gameDataGlobal.opponentBaseStats.characterKey;

            let myClass = 'title-player';
            if (myKey === 'elena') myClass = 'title-enchantress';
            else if (myKey === 'almagest') myClass = 'title-sorceress';
            else if (myKey === 'balard') myClass = 'title-knight'; // Вдруг AI Балард в PvP

            let opponentClass = 'title-opponent';
            if (opponentKey === 'elena') opponentClass = 'title-enchantress';
            else if (opponentKey === 'almagest') opponentClass = 'title-sorceress';
            else if (opponentKey === 'balard') opponentClass = 'title-knight';

            uiElements.gameHeaderTitle.innerHTML = `<span class="${myClass}">${myName}</span> <span class="separator"><i class="fas fa-fist-raised"></i></span> <span class="${opponentClass}">${opponentName}</span>`;
        } else if (uiElements.gameHeaderTitle) {
            // Обновление заголовка в режиме ожидания
            const myName = gameDataGlobal.playerBaseStats?.name || 'Игрок 1';
            const myKey = gameDataGlobal.playerBaseStats?.characterKey;
            let myClass = 'title-player';
            if (myKey === 'elena') myClass = 'title-enchantress';
            else if (myKey === 'almagest') myClass = 'title-sorceress';

            uiElements.gameHeaderTitle.innerHTML = `<span class="${myClass}">${myName}</span> <span class="separator"><i class="fas fa-fist-raised"></i></span> <span class="title-opponent">Ожидание игрока...</span>`;
        }


        // Управление активностью кнопок и индикатор хода
        const canThisClientAct = actorSlotWhoseTurnItIs === myActualPlayerId;
        const isGameActive = !currentGameState.isGameOver;
        const myCharacterState = currentGameState[myActualPlayerId];

        // Обновление индикатора хода
        if (uiElements.controls.turnIndicator) {
            if (isGameActive) {
                const currentTurnActor = currentGameState.isPlayerTurn ? currentGameState.player : currentGameState.opponent;
                uiElements.controls.turnIndicator.textContent = `Ход ${currentGameState.turnNumber}: ${currentTurnActor?.name || 'Неизвестно'}`;
                // Управляем цветом индикатора хода
                if (currentTurnActor?.id === myActualPlayerId) {
                    uiElements.controls.turnIndicator.style.color = 'var(--turn-color)'; // Свой ход - желтый
                } else {
                    uiElements.controls.turnIndicator.style.color = 'var(--text-muted)'; // Ход противника - приглушенный
                }
            } else {
                uiElements.controls.turnIndicator.textContent = "Игра окончена"; // Или можно скрыть его
                uiElements.controls.turnIndicator.style.color = 'var(--text-muted)';
            }
        }


        // Кнопка атаки
        if (uiElements.controls.buttonAttack) {
            // Кнопка атаки активна, если это ход этого клиента и игра активна (полное безмолвие не блокирует базовую атаку)
            // ИСПРАВЛЕНО: Убрана проверка !isFullySilenced из условия disabled для базовой атаки
            uiElements.controls.buttonAttack.disabled = !(canThisClientAct && isGameActive);

            // Управление классом для подсветки бафнутой атаки
            const myCharKey = gameDataGlobal.playerBaseStats?.characterKey;
            let attackBuffId = null;
            if (myCharKey === 'elena') attackBuffId = configGlobal.ABILITY_ID_NATURE_STRENGTH;
            else if (myCharKey === 'almagest') attackBuffId = configGlobal.ABILITY_ID_ALMAGEST_BUFF_ATTACK;

            if (attackBuffId && myCharacterState && myCharacterState.activeEffects) {
                // Проверяем, есть ли активный "отложенный" бафф (isDelayed=true) на атакующем,
                // который готов сработать на следующую атаку.
                const isAttackBuffReady = myCharacterState.activeEffects.some(
                    eff => (eff.id === attackBuffId || eff.id === GAME_CONFIG.ABILITY_ID_NATURE_STRENGTH || eff.id === GAME_CONFIG.ABILITY_ID_ALMAGEST_BUFF_ATTACK)
                        && eff.isDelayed // Явно проверяем, что это отложенный бафф
                        && eff.turnsLeft > 0 // Эффект должен еще действовать
                        && !eff.justCast // Не должен быть наложен в этом ходу, чтобы сработать НА ЭТОМ ходу
                );

                // Подсветка активна, если бафф готов И это ход этого клиента И игра активна
                // Подсветка не зависит от безмолвия, т.к. атака возможна и под безмолвием.
                uiElements.controls.buttonAttack.classList.toggle(configGlobal.CSS_CLASS_ATTACK_BUFFED || 'attack-buffed', isAttackBuffReady && canThisClientAct && isGameActive);
            } else {
                uiElements.controls.buttonAttack.classList.remove(configGlobal.CSS_CLASS_ATTACK_BUFFED || 'attack-buffed');
            }
        }
        if (uiElements.controls.buttonBlock) uiElements.controls.buttonBlock.disabled = true; // Пока не используется

        // Кнопки способностей
        const actingPlayerState = myCharacterState; // Состояние моего персонажа
        const actingPlayerAbilities = gameDataGlobal.playerAbilities; // Способности моего персонажа (с точки зрения клиента)
        const actingPlayerResourceName = gameDataGlobal.playerBaseStats?.resourceName; // Имя ресурса моего персонажа
        const opponentStateForDebuffCheck = currentGameState[opponentActualSlotId]; // Состояние оппонента этого клиента

        uiElements.controls.abilitiesGrid?.querySelectorAll(`.${configGlobal.CSS_CLASS_ABILITY_BUTTON || 'ability-button'}`).forEach(button => {
            // Получаем актуальное состояние способности из actingPlayerState (которое пришло с сервера)
            const abilityId = button.dataset.abilityId;
            const abilityDataFromGameData = actingPlayerAbilities?.find(ab => ab.id === abilityId);

            // Если игра неактивна, нет данных о бойце, способностях или ресурсе, дизейблим кнопку.
            if (!(button instanceof HTMLButtonElement) || !isGameActive || !canThisClientAct || !actingPlayerState || !actingPlayerAbilities || !actingPlayerResourceName || !abilityDataFromGameData) {
                if(button instanceof HTMLButtonElement) button.disabled = true;
                button.classList.remove(configGlobal.CSS_CLASS_NOT_ENOUGH_RESOURCE||'not-enough-resource', configGlobal.CSS_CLASS_BUFF_IS_ACTIVE||'buff-is-active', configGlobal.CSS_CLASS_ABILITY_SILENCED||'is-silenced', configGlobal.CSS_CLASS_ABILITY_ON_COOLDOWN||'is-on-cooldown');
                const cooldownDisplay = button.querySelector('.ability-cooldown-display');
                if (cooldownDisplay) cooldownDisplay.style.display = 'none';
                return; // Пропускаем дальнейшую логику обновления кнопки, если она должна быть disabled по базовым условиям
            }

            // Проверяем условия доступности способности из актуального состояния игры (actingPlayerState)
            const hasEnoughResource = actingPlayerState.currentResource >= abilityDataFromGameData.cost;
            const isOnCooldown = (actingPlayerState.abilityCooldowns?.[abilityId] || 0) > 0; // Проверяем КД по ID способности из актуального состояния
            // Под полным безмолвием
            const isGenerallySilenced = actingPlayerState.activeEffects?.some(eff => eff.isFullSilence && eff.turnsLeft > 0);
            // Под специфическим заглушением этой способности (ищем в disabledAbilities актуального состояния)
            const isAbilitySpecificallySilenced = actingPlayerState.disabledAbilities?.some(dis => dis.abilityId === abilityId && dis.turnsLeft > 0);
            const isSilenced = isGenerallySilenced || isAbilitySpecificallySilenced; // Считается заглушенным, если под полным или специфическим безмолвием
            // Определяем длительность безмолвия для отображения (берем из специфического, если есть, иначе из полного)
            const silenceTurnsLeft = isAbilitySpecificallySilenced
                ? (actingPlayerState.disabledAbilities?.find(dis => dis.abilityId === abilityId)?.turnsLeft || 0)
                : (isGenerallySilenced ? (actingPlayerState.activeEffects.find(eff => eff.isFullSilence)?.turnsLeft || 0) : 0);


            // Нельзя кастовать бафф, если он уже активен (для баффов, которые не стакаются)
            const isBuffAlreadyActive = abilityDataFromGameData.type === configGlobal.ACTION_TYPE_BUFF && actingPlayerState.activeEffects?.some(eff => eff.id === abilityId);

            // Нельзя кастовать дебафф на цель, если он уже на ней (для определенных дебаффов)
            const isTargetedDebuffAbility = abilityId === configGlobal.ABILITY_ID_SEAL_OF_WEAKNESS || abilityId === configGlobal.ABILITY_ID_ALMAGEST_DEBUFF;
            const effectIdForDebuff = 'effect_' + abilityId; // Ищем эффект с префиксом effect_ на цели (оппоненте)
            const isDebuffAlreadyOnTarget = isTargetedDebuffAbility && opponentStateForDebuffCheck && opponentStateForDebuffCheck.activeEffects?.some(e => e.id === effectIdForDebuff);


            // Кнопка способности активна, если:
            // - Это ход этого клиента (проверено выше: canThisClientAct)
            // - Игра активна (проверено выше: isGameActive)
            // - Достаточно ресурса
            // - Бафф не активен (если это бафф)
            // - Не на кулдауне
            // - Не под безмолвием (полным или специфическим) <--- ЭТО УСЛОВИЕ ОСТАЕТСЯ ДЛЯ СПОСОБНОСТЕЙ
            // - Дебафф не активен на цели (если это такой дебафф)
            button.disabled = !hasEnoughResource ||
                isBuffAlreadyActive ||
                isSilenced || // Способности БЛОКИРУЮТСЯ полным безмолвием
                isOnCooldown ||
                isDebuffAlreadyOnTarget;


            // Управление классами для стилизации кнопки (применяются независимо от окончательного disabled состояния)
            button.classList.remove(configGlobal.CSS_CLASS_NOT_ENOUGH_RESOURCE||'not-enough-resource', configGlobal.CSS_CLASS_BUFF_IS_ACTIVE||'buff-is-active', configGlobal.CSS_CLASS_ABILITY_SILENCED||'is-silenced', configGlobal.CSS_CLASS_ABILITY_ON_COOLDOWN||'is-on-cooldown');
            const cooldownDisplay = button.querySelector('.ability-cooldown-display');

            if (isOnCooldown) {
                button.classList.add(configGlobal.CSS_CLASS_ABILITY_ON_COOLDOWN||'is-on-cooldown');
                if (cooldownDisplay) { cooldownDisplay.textContent = `КД: ${actingPlayerState.abilityCooldowns[abilityId]}`; cooldownDisplay.style.display = 'block'; }
            } else if (isSilenced) {
                button.classList.add(configGlobal.CSS_CLASS_ABILITY_SILENCED||'is-silenced');
                if (cooldownDisplay) {
                    const icon = isGenerallySilenced ? '🔕' : '🔇'; // Иконка для полного/частичного безмолвия
                    cooldownDisplay.textContent = `${icon} ${silenceTurnsLeft}`;
                    cooldownDisplay.style.display = 'block';
                }
            } else {
                if (cooldownDisplay) cooldownDisplay.style.display = 'none'; // Скрываем, если нет ни КД, ни безмолвия

                // Добавляем классы для визуальной обратной связи, ЕСЛИ кнопка НЕ задизейблена по КД или Безмолвию
                // (т.е. эти классы показывают *другие* причины, по которым кнопка может быть disabled)
                if (!isOnCooldown && !isSilenced) {
                    button.classList.toggle(configGlobal.CSS_CLASS_NOT_ENOUGH_RESOURCE||'not-enough-resource', !hasEnoughResource);
                    button.classList.toggle(configGlobal.CSS_CLASS_BUFF_IS_ACTIVE||'buff-is-active', isBuffAlreadyActive);
                    // Если дебафф уже на цели, но кнопка не задизейблена по другим причинам, можно добавить отдельный класс для стилизации
                    // button.classList.toggle('debuff-on-target', isDebuffAlreadyOnTarget);
                }
            }

            // Обновление title (всплывающей подсказки) - показываем полную информацию
            // Используем abilityDataFromGameData для базовой информации
            let titleText = `${abilityDataFromGameData.name} (${abilityDataFromGameData.cost} ${actingPlayerResourceName})`;
            let descriptionTextFull = abilityDataFromGameData.description; // Используем описание, пришедшее с сервера

            // Если есть функция описания, используем ее
            if (typeof abilityDataFromGameData.descriptionFunction === 'function') {
                // Передаем конфиг и статы оппонента (цели) для генерации описания
                const opponentBaseStatsForDesc = gameDataGlobal.opponentBaseStats; // Статы оппонента этого клиента
                descriptionTextFull = abilityDataFromGameData.descriptionFunction(configGlobal, opponentBaseStatsForDesc);
            }


            if (descriptionTextFull) titleText += ` - ${descriptionTextFull}`;

            // Добавляем информацию об исходном КД из данных способности
            let abilityBaseCooldown = abilityDataFromGameData.cooldown;
            if (typeof abilityBaseCooldown === 'number' && abilityBaseCooldown > 0) {
                titleText += ` (Исходный КД: ${abilityBaseCooldown} х.)`;
            }

            // Добавляем информацию о текущем состоянии (КД, безмолвие, активный бафф/debuff) в тултип, если применимо
            if (isOnCooldown) {
                titleText += ` | На перезарядке! Осталось: ${actingPlayerState.abilityCooldowns[abilityId]} х.`;
            }
            if (isSilenced) {
                titleText += ` | Под безмолвием! Осталось: ${silenceTurnsLeft} х.`;
            }
            if (isBuffAlreadyActive) {
                const activeEffect = actingPlayerState.activeEffects?.find(eff => eff.id === abilityId); // Ищем активный эффект по ID способности
                // Если бафф имеет свойство 'justCast' и наложен в этом ходу, он не "готов" сработать на ЭТОМ ходу.
                // Это может быть важно для тултипа, если нужно отличать "только что наложен" от "готов к следующему действию".
                // Для "Силы Природы" (isDelayed=true) состояние "активен" означает "готов сработать на следующую атаку".
                const isDelayedBuffReady = isBuffAlreadyActive && activeEffect && activeEffect.isDelayed && !activeEffect.justCast && activeEffect.turnsLeft > 0;

                if (isDelayedBuffReady) {
                    titleText += ` | Эффект активен и сработает при следующей базовой атаке (${activeEffect.turnsLeft} х.)`;
                } else if (isBuffAlreadyActive) {
                    titleText += ` | Эффект уже активен${activeEffect ? ` (${activeEffect.turnsLeft} х.)` : ''}. Нельзя применить повторно.`;
                }
            }
            if (isDebuffAlreadyOnTarget && opponentStateForDebuffCheck) {
                const activeDebuff = opponentStateForDebuffCheck.activeEffects?.find(e => e.id === 'effect_' + abilityId);
                titleText += ` | Эффект уже наложен на ${gameDataGlobal.opponentBaseStats?.name || 'противника'}${activeDebuff ? ` (${activeDebuff.turnsLeft} х.)` : ''}.`;
            }
            if (!hasEnoughResource) {
                titleText += ` | Недостаточно ${actingPlayerResourceName} (${actingPlayerState.currentResource}/${abilityDataFromGameData.cost})`;
            }


            button.setAttribute('title', titleText);
        });
    }

    /**
     * Показывает модальное окно конца игры.
     * @param {boolean} playerWon - Флаг, выиграл ли игрок, управляющий этим клиентом.
     * @param {string} [reason=""] - Причина завершения игры.
     * @param {string|null} opponentCharacterKeyFromClient - Ключ персонажа оппонента с т.з. клиента.
     * @param {object} [data=null] - Полный объект данных из события gameOver (включает disconnectedCharacterName и т.д.)
     */
    function showGameOver(playerWon, reason = "", opponentCharacterKeyFromClient = null, data = null) {
        const config = window.GAME_CONFIG || {};
        const clientSpecificGameData = window.gameData; // Используем gameData, сохраненное в client.js
        const currentActualGameState = window.gameState; // Самое актуальное состояние игры
        const gameOverScreenElement = uiElements.gameOver.screen;

        console.log(`[UI.JS DEBUG] showGameOver CALLED. PlayerWon: ${playerWon}, Reason: ${reason}`);
        console.log(`[UI.JS DEBUG] captured currentActualGameState?.isGameOver at call time: ${currentActualGameState?.isGameOver}`); // Log state at call time
        console.log(`[UI.JS DEBUG] Opponent Character Key (from client via param): ${opponentCharacterKeyFromClient}`);
        console.log(`[UI.JS DEBUG] My Character Name (from window.gameData): ${clientSpecificGameData?.playerBaseStats?.name}`);
        console.log(`[UI.JS DEBUG] Opponent Character Name (from window.gameData): ${clientSpecificGameData?.opponentBaseStats?.name}`);
        console.log(`[UI.JS DEBUG] Full game over data received:`, data);


        if (!gameOverScreenElement) {
            console.warn("[UI.JS DEBUG] showGameOver: gameOverScreenElement not found.");
            return;
        }

        const resultMsgElement = uiElements.gameOver.message;
        const myNameForResult = clientSpecificGameData?.playerBaseStats?.name || "Игрок";
        const opponentNameForResult = clientSpecificGameData?.opponentBaseStats?.name || "Противник";

        if (resultMsgElement) {
            let winText = `Победа! ${myNameForResult} празднует!`;
            let loseText = `Поражение! ${opponentNameForResult} оказался(лась) сильнее!`;
            if (reason === 'opponent_disconnected') {
                let disconnectedName = "Противник";
                // Если в данных gameOver есть имя отключившегося персонажа, используем его
                if (data && data.disconnectedCharacterName) {
                    disconnectedName = data.disconnectedCharacterName;
                } else {
                    // Фоллбэк на имя оппонента с точки зрения клиента
                    disconnectedName = opponentNameForResult;
                }

                winText = `${disconnectedName} покинул(а) игру. Победа присуждается вам!`;
                // В PvP, если оппонент отключился, а текущий игрок проиграл (что странно, но возможно),
                // сообщение о поражении может быть стандартным или специфичным.
                // В AI режиме, если игрок отключился, нет формального победителя AI.
                // Пусть будет стандартное поражение, если playerWon === false
                if (!playerWon) {
                    // Возможно, специфичный текст для дисконнекта, когда ты проиграл?
                    // loseText = `Игра завершена из-за отключения ${disconnectedName}. Вы проиграли.`
                }
            } else if (reason === 'hp_zero') {
                // Стандартное завершение по HP - тексты определены выше
            }
            // Добавьте обработку других причин завершения, если они будут
            else {
                // Неизвестная причина завершения
                winText = `Игра окончена. Победа! (${reason})`;
                loseText = `Игра окончена. Поражение. (${reason})`;
            }


            resultMsgElement.textContent = playerWon ? winText : loseText;
            resultMsgElement.style.color = playerWon ? 'var(--heal-color)' : 'var(--damage-color)';
        }

        const opponentPanelElement = uiElements.opponent.panel;
        if (opponentPanelElement) {
            // Сначала убеждаемся, что анимация растворения снята, если она была активна от предыдущей попытки
            // и не должна применяться сейчас.
            opponentPanelElement.classList.remove('dissolving');
            opponentPanelElement.style.transition = 'none'; // Временно отключаем transition
            opponentPanelElement.offsetHeight; // Trigger reflow to apply style instantly

            // Используем characterKey проигравшего (переданный из GameInstance),
            // так как анимация растворения должна быть специфична для проигравшего персонажа,
            // который может быть Балардом или Альмагест.
            const loserCharacterKeyForDissolve = data?.loserCharacterKey;

            // Применяем анимацию растворения только если игра окончена, игрок победил,
            // и проигравший был Балардом или Альмагест (у которых есть эта анимация).
            // Исключаем случай дисконнекта, если анимация должна быть только при "убийстве" по HP.
            // В текущем CSS анимация растворения не зависит от причины, но зависит от класса 'dissolving'.
            // Добавляем класс, если игра окончена, игрок победил, и проигравший персонаж - Балард или Альмагест.
            // Если игра окончена И игрок проиграл И оппонент был Балардом/Альмагест, но игрок проиграл, анимация растворения НЕ применяется к панели оппонента.
            // Поэтому условие playerWon && ... корректно.
            if (currentActualGameState && currentActualGameState.isGameOver === true && playerWon) {
                // Проверяем, является ли проигравший (т.е. оппонент этого клиента) Балардом или Альмагест
                if (loserCharacterKeyForDissolve === 'balard' || loserCharacterKeyForDissolve === 'almagest') {
                    console.log(`[UI.JS DEBUG] ADDING .dissolving to opponent panel.`);
                    opponentPanelElement.classList.add('dissolving');
                    // Убеждаемся, что панель станет полностью прозрачной и сместится после анимации.
                    // Конечные стили (opacity: 0, transform) могут быть заданы в CSS для класса .dissolving,
                    // но их можно также установить здесь после добавления класса для гарантии.
                    opponentPanelElement.style.opacity = '0'; // Конечный стиль для transition
                    // opponentPanelElement.style.transform = 'scale(0.9) translateY(20px)'; // Конечный стиль для transition, если нужен
                } else {
                    console.log(`[UI.JS DEBUG] NOT adding .dissolving (loser key mismatch: ${loserCharacterKeyForDissolve}).`);
                    // Если анимация не применяется, убеждаемся, что панель полностью видна
                    opponentPanelElement.style.opacity = '1';
                    opponentPanelElement.style.transform = 'scale(1) translateY(0)';
                }
            } else {
                console.log(`[UI.JS DEBUG] NOT adding .dissolving. Conditions NOT met: isGameOver=${currentActualGameState?.isGameOver}, playerWon=${playerWon}.`);
                // Если игра не окончена или игрок проиграл, убеждаемся, что панель полностью видна
                opponentPanelElement.style.opacity = '1';
                opponentPanelElement.style.transform = 'scale(1) translateY(0)';
            }
            opponentPanelElement.style.transition = ''; // Восстанавливаем transition после установки начальных/конечных стилей


        }

        // Показываем модальное окно конца игры с небольшой задержкой
        // Передаем аргументы в колбэк, чтобы не полагаться на глобальный gameState в момент срабатывания setTimeout
        setTimeout((finalStateInTimeout, wonInTimeout, reasonInTimeout, keyInTimeout, dataInTimeout) => { // Use distinct names in timeout
            console.log("[UI.JS DEBUG] Timeout callback fired for showGameOver.");
            console.log("[UI.JS DEBUG] State object received in timeout:", finalStateInTimeout); // Check the whole object
            console.log("[UI.JS DEBUG] isGameOver in state (TIMEOUT):", finalStateInTimeout?.isGameOver); // Check property
            console.log("[UI.JS DEBUG] playerWon flag (TIMEOUT):", wonInTimeout); // Check playerWon flag passed


            // Проверяем условия для показа модального окна: элемент существует И состояние игры помечено как оконченное
            // ИСПРАВЛЕНО: Убрана проверка gameOverScreenElement.offsetParent !== null
            if (gameOverScreenElement && finalStateInTimeout && finalStateInTimeout.isGameOver === true) {
                console.log(`[UI.JS DEBUG] Modal SHOW condition met: gameOverScreenElement exists, finalState exists, isGameOver is true.`);
                // Убеждаемся, что modal не имеет display: none перед запуском transition opacity
                if (gameOverScreenElement.classList.contains(config.CSS_CLASS_HIDDEN || 'hidden')) {
                    gameOverScreenElement.classList.remove(config.CSS_CLASS_HIDDEN || 'hidden');
                }
                // Применяем display: flex (или другой нужный) только один раз, если нужно
                if(window.getComputedStyle(gameOverScreenElement).display === 'none') {
                    gameOverScreenElement.style.display = 'flex'; // Или какой там display в CSS для .modal
                }
                gameOverScreenElement.style.opacity = '0'; // Start from hidden opacity

                requestAnimationFrame(() => {
                    console.log("[UI.JS DEBUG] RequestAnimationFrame callback fired, animating modal.");
                    // Animate to visible
                    gameOverScreenElement.style.opacity = '1';
                    if (uiElements.gameOver.modalContent) {
                        uiElements.gameOver.modalContent.style.transition = 'transform 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.2), opacity 0.4s ease-out'; // Убеждаемся, что transition включен
                        uiElements.gameOver.modalContent.style.transform = 'scale(1) translateY(0)';
                        uiElements.gameOver.modalContent.style.opacity = '1';
                        // uiElements.gameOver.modalContent.style.transition = ''; // Можно и так, если не отключали ранее
                    }
                });
            } else {
                console.log(`[UI.JS DEBUG] Modal SHOW condition NOT met.`);
                console.log(`[UI.JS DEBUG] Details: gameOverScreenElement=${!!gameOverScreenElement}, finalState=${!!finalStateInTimeout}, finalState?.isGameOver=${finalStateInTimeout?.isGameOver}. Hiding modal.`); // More details
                // Убеждаемся, что модалка скрыта, если условия не выполняются
                if (gameOverScreenElement) {
                    // Ensure transition is off when hiding instantly
                    gameOverScreenElement.style.transition = 'none';
                    if (uiElements.gameOver.modalContent) uiElements.gameOver.modalContent.style.transition = 'none';

                    gameOverScreenElement.classList.add(config.CSS_CLASS_HIDDEN || 'hidden');
                    gameOverScreenElement.style.opacity = '0';
                    if (uiElements.gameOver.modalContent) {
                        uiElements.gameOver.modalContent.style.transform = 'scale(0.8) translateY(30px)';
                        uiElements.gameOver.modalContent.style.opacity = '0';
                    }
                    // Trigger reflow to ensure transition is off before hiding
                    gameOverScreenElement.offsetHeight;
                }
            }
        }, config.DELAY_BEFORE_VICTORY_MODAL || 1500, currentActualGameState, playerWon, reason, opponentCharacterKeyFromClient, data); // Pass captured state and other values
    }


    // Экспортируем функции UI для использования в client.js
    window.gameUI = { uiElements, addToLog, updateUI, showGameOver };
})();