/**
 * Star Wars Hearthstone - Client
 * PvP over LAN (Radmin VPN), 2-4 players
 */

(function () {
  'use strict';

  let socket = null;
  let gameState = null;
  let cardDb = {};
  let yourIndex = -1;
  let attackMode = { active: false, attackerPlayer: -1, attackerBoard: -1 };
  let spellMode = { active: false, handIndex: -1, cardId: '', spellTarget: '' };
  let heroAbilityMode = { active: false };
  const seenMinions = new Set();
  let lastHandLength = 0;
  let prevGameState = null;
  let combatTimeout = null;
  const DEFAULT_PORT = 3542;
  
  // Touch support variables
  let touchState = {
    startX: 0,
    startY: 0,
    startTime: 0,
    target: null,
    isSwipe: false,
    minSwipeDistance: 50,
    maxSwipeTime: 500,
    longPressTimer: null,
    longPressDelay: 500
  };

  const $ = (id) => document.getElementById(id);
  const $all = (sel) => document.querySelectorAll(sel);
  
  // Helper to detect if device is touch-enabled
  const isTouchDevice = () => {
    return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
  };
  
  // Helper to get touch coordinates
  const getTouchCoords = (e) => {
    if (e.touches && e.touches.length > 0) {
      return { x: e.touches[0].clientX, y: e.touches[0].clientY };
    }
    return { x: e.clientX, y: e.clientY };
  };
  
  // Helper to find element at coordinates
  const elementFromPoint = (x, y) => {
    const el = document.elementFromPoint(x, y);
    if (!el) return null;
    return el.closest('.card-wrap, .drop-target, .hero-target, .spell-target, .hero-ability-target');
  };

  function spawnEffect(type, x, y) {
    const layer = $('effects-layer');
    if (!layer) return;
    const el = document.createElement('div');
    el.className = 'effect effect-' + type;
    el.style.left = (typeof x === 'number' ? x : 0) + 'px';
    el.style.top = (typeof y === 'number' ? y : 0) + 'px';
    layer.appendChild(el);
    setTimeout(function () { el.remove(); }, 800);
  }

  function showScreen(id) {
    $all('.screen').forEach((s) => s.classList.add('hidden'));
    const el = $(id);
    if (el) el.classList.remove('hidden');
  }

  function showError(msg) {
    const el = $('lobby-error');
    if (!el) return;
    el.textContent = msg;
    el.classList.remove('hidden');
  }

  function clearError() {
    const el = $('lobby-error');
    if (el) el.classList.add('hidden');
  }

  function setLobbyPanel(name) {
    $all('#lobby .panel').forEach((p) => {
      p.classList.remove('active', 'hidden');
      if (p.id !== name + '-panel') p.classList.add('hidden');
      else p.classList.add('active');
    });
  }

  function renderPlayerList(listId, players) {
    const ul = $(listId);
    if (!ul) return;
    ul.innerHTML = (players || []).map((p) => `<li>${escapeHtml(p.name)}</li>`).join('');
  }

  function escapeHtml(s) {
    const div = document.createElement('div');
    div.textContent = s;
    return div.innerHTML;
  }

  function connect(url) {
    if (socket) {
      socket.disconnect();
      socket.removeAllListeners();
    }
    socket = io(url, { transports: ['websocket', 'polling'], reconnection: false });
    socket.on('connect', () => {
      clearError();
    });
    socket.on('connect_error', (e) => {
      const errorMsg = e.message || 'Не удалось подключиться к серверу';
      showError(errorMsg + '. Проверьте, что сервер запущен и используйте расширенные настройки для указания IP вручную.');
    });
    socket.on('error', (msg) => showError(msg));
    socket.on('roomCreated', (data) => {
      const codeEl = $('room-code-display');
      if (codeEl) {
        codeEl.textContent = data.code;
        codeEl.onclick = function () {
          navigator.clipboard.writeText(data.code).then(() => {
            const old = codeEl.textContent;
            codeEl.textContent = 'Скопировано!';
            setTimeout(() => { codeEl.textContent = old; }, 1500);
          }).catch(() => {});
        };
      }
      const connectInfoEl = $('room-connect-info');
      if (connectInfoEl && data.serverIP && data.serverPort) {
        const isLocal = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
        const serverAddr = isLocal ? 'localhost:' + data.serverPort : data.serverIP + ':' + data.serverPort;
        const fullInfo = serverAddr + ' → код: ' + data.code;
        connectInfoEl.textContent = fullInfo;
        connectInfoEl.onclick = function () {
          navigator.clipboard.writeText(fullInfo).then(() => {
            const old = connectInfoEl.textContent;
            connectInfoEl.textContent = 'Скопировано!';
            setTimeout(() => { connectInfoEl.textContent = old; }, 1500);
          }).catch(() => {});
        };
      }
      setLobbyPanel('room');
      renderPlayerList('player-list', data.lobby);
      const portEl = $('display-port');
      if (portEl) portEl.textContent = data.serverPort || window.location.port || DEFAULT_PORT;
    });
    socket.on('roomJoined', (data) => {
      setLobbyPanel('connect');
      renderPlayerList('connect-player-list', data.lobby);
      const statusEl = $('connect-status');
      if (statusEl) statusEl.textContent = 'Подключено к комнате. Ожидание начала игры...';
    });
    socket.on('lobbyUpdate', (lobby) => {
      renderPlayerList('player-list', lobby);
      renderPlayerList('connect-player-list', lobby);
    });
    socket.on('backToLobby', () => {
      showScreen('lobby');
      clearError();
      setLobbyPanel('host');
      const hp = $('host-panel');
      if (hp) hp.classList.add('active');
    });
    socket.on('turnTime', function (data) {
      var el = $('turn-timer');
      if (!el) return;
      var m = Math.floor(data.left / 60);
      var s = data.left % 60;
      el.textContent = m + ':' + (s < 10 ? '0' : '') + s;
      el.classList.toggle('timer-warn', data.left > 0 && data.left <= 15);
      el.classList.toggle('timer-ended', data.ended);
      if (data.warn && typeof window.Sounds !== 'undefined') window.Sounds.timerWarning();
      if (data.ended && typeof window.Sounds !== 'undefined') window.Sounds.timerEnd();
    });
    socket.on('gameState', (state) => {
      if (combatTimeout) {
        clearTimeout(combatTimeout);
        combatTimeout = null;
      }
      const wasLobby = $('game')?.classList.contains('hidden') === true;
      if (wasLobby) {
        seenMinions.clear();
        lastHandLength = 0;
        prevGameState = null;
      }
      prevGameState = gameState && state.phase !== 'ended' ? { ...gameState, players: gameState.players.map((p) => ({ ...p, board: (p.board || []).map((m) => ({ ...m })) })) } : null;
      showScreen('game');
      gameState = state;
      cardDb = state.cardDb || {};
      yourIndex = state.yourIndex ?? 0;
      if (state.phase === 'ended') {
        showGameOver(state);
        renderGameEnded(state);
        return;
      }
      renderGame(state);
    });
    return socket;
  }

  function renderGameEnded(state) {
    const badge = $('turn-badge');
    if (badge) badge.textContent = 'Игра окончена';
    const endBtn = $('btn-end-turn');
    if (endBtn) endBtn.disabled = true;
    const resetBtn = $('btn-reset-lobby');
    if (resetBtn) {
      resetBtn.classList.toggle('hidden', state.yourIndex !== 0);
    }
    bindGameEvents(state);
  }

  function showGameOver(state) {
    const winner = state.winner;
    const modal = $('modal-overlay');
    const title = $('modal-title');
    const body = $('modal-body');
    if (!modal || !title || !body) return;
    if (winner && winner.id === socket?.id) {
      title.textContent = 'Победа!';
      title.style.color = 'var(--amber)';
      body.textContent = 'Светлая сторона Силы с тобой.';
      if (typeof window.Sounds !== 'undefined') window.Sounds.victory();
    } else if (winner) {
      title.textContent = 'Поражение';
      title.style.color = 'var(--red)';
      body.textContent = 'Победу одержал ' + (winner.name || 'противник') + '.';
      if (typeof window.Sounds !== 'undefined') window.Sounds.defeat();
    } else {
      title.textContent = 'Игра окончена';
      title.style.color = 'var(--cyan)';
      body.textContent = 'Победитель не определён.';
    }
    modal.classList.remove('hidden');
  }

  function renderBoards(state) {
    const you = state.players[yourIndex];
    if (!you) return;
    const opponentsArea = $('opponents-area');
    const yourBoard = $('your-board');
    if (opponentsArea) {
      opponentsArea.innerHTML = state.players
        .map((p, i) => {
          if (i === yourIndex) return '';
          const isCurrent = state.currentPlayerIndex === i;
          const name = p.name || 'Игрок ' + (i + 1);
          const minions = (p.board || []).map((m, j) => renderBoardMinion(m, i, j, state, true, false));
          const heroBar = renderHeroTarget(i, state);
          const heroDrop = renderHeroDropZone(i, state);
          return `
            <div class="opponent-block ${isCurrent ? 'current-turn' : ''}" data-opponent-index="${i}">
              <div class="opponent-name">${escapeHtml(name)}</div>
              <div class="opponent-stats">
                <span>❤ ${p.health}</span>
                <span>🔵 ${p.mana}/${p.maxMana}</span>
              </div>
              <div class="opponent-board">${heroDrop}${heroBar}${minions.join('')}</div>
            </div>`;
        })
        .filter(Boolean)
        .join('');
    }
    if (yourBoard) {
      yourBoard.innerHTML = (you.board || []).map((m, j) => {
        const isNew = !seenMinions.has(m.id);
        if (isNew) seenMinions.add(m.id);
        return renderBoardMinion(m, yourIndex, j, state, false, isNew);
      }).join('');
    }
  }

  function showDamageEffect(element, damage, isHero) {
    if (!element) return;
    const damageEl = document.createElement('div');
    damageEl.className = 'damage-indicator';
    damageEl.textContent = '-' + damage;
    damageEl.style.position = 'absolute';
    const rect = element.getBoundingClientRect();
    damageEl.style.left = (rect.left + rect.width / 2) + 'px';
    damageEl.style.top = (rect.top + rect.height / 2) + 'px';
    damageEl.style.pointerEvents = 'none';
    damageEl.style.zIndex = '1000';
    document.body.appendChild(damageEl);
    setTimeout(() => {
      damageEl.style.animation = 'damageFloat 1.5s ease-out forwards';
      setTimeout(() => damageEl.remove(), 1000); // Сократил с 1500 до 1000 мс
    }, 10);
  }

  function showAttackAnnouncement(state, logEntry) {
    const announcementEl = $('attack-announcement');
    if (!announcementEl || !logEntry) return;
    
    // Обработка специальных событий
    if (logEntry.type === 'skipTurn') {
      const playerName = state.players[logEntry.playerIndex]?.name || `Игрок ${logEntry.playerIndex + 1}`;
      announcementEl.textContent = `⏭ ${playerName} пропускает ход`;
      announcementEl.classList.remove('hidden');
      setTimeout(() => {
        announcementEl.classList.add('hidden');
      }, 2000);
      return;
    }
    
    if (logEntry.type === 'playerDefeated') {
      const playerName = state.players[logEntry.playerIndex]?.name || `Игрок ${logEntry.playerIndex + 1}`;
      announcementEl.textContent = `💀 ${playerName} выбыл из игры`;
      announcementEl.classList.remove('hidden');
      setTimeout(() => {
        announcementEl.classList.add('hidden');
      }, 2500);
      return;
    }
    
    const who = (i) => state.players[i]?.name || ('Игрок ' + (i + 1));
    let text = '';
    
    if (logEntry.type === 'attack' && logEntry.attackerMinionId && logEntry.targetMinionId) {
      const attackerPlayer = state.players.find(p => p.board?.some(m => m.id === logEntry.attackerMinionId));
      const targetPlayer = state.players.find(p => p.board?.some(m => m.id === logEntry.targetMinionId));
      if (attackerPlayer && targetPlayer) {
        const attackerIdx = state.players.indexOf(attackerPlayer);
        const targetIdx = state.players.indexOf(targetPlayer);
        const attackerMinion = attackerPlayer.board.find(m => m.id === logEntry.attackerMinionId);
        const targetMinion = targetPlayer.board.find(m => m.id === logEntry.targetMinionId);
        const attackerCard = cardDb[attackerMinion?.cardId];
        const targetCard = cardDb[targetMinion?.cardId];
        const attackerName = attackerCard?.name || 'Миньон';
        const targetName = targetCard?.name || 'Миньон';
        const damage = logEntry.damage || attackerMinion?.attack || 0;
        text = who(attackerIdx) + ' → ' + attackerName + ' ⚔ атакует ' + targetName + ' (' + damage + ' урона) → ' + who(targetIdx);
      }
    } else if (logEntry.type === 'attackHero' && logEntry.fromPlayer !== undefined && logEntry.toPlayer !== undefined) {
      const attackerPlayer = state.players[logEntry.fromPlayer];
      const targetPlayer = state.players[logEntry.toPlayer];
      if (attackerPlayer && targetPlayer) {
        if (logEntry.attackerMinionId) {
          const attackerMinion = attackerPlayer.board?.find(m => m.id === logEntry.attackerMinionId);
          const attackerCard = cardDb[attackerMinion?.cardId];
          const attackerName = attackerCard?.name || 'Миньон';
          const damage = logEntry.damage || attackerMinion?.attack || 0;
          text = who(logEntry.fromPlayer) + ' → ' + attackerName + ' ⚔ атакует героя ' + who(logEntry.toPlayer) + ' (' + damage + ' урона)!';
        } else {
          const damage = logEntry.damage || 0;
          text = who(logEntry.fromPlayer) + ' ⚔ атакует героя ' + who(logEntry.toPlayer) + ' (' + damage + ' урона)!';
        }
      }
    } else if (logEntry.type === 'spell' && logEntry.fromPlayer !== undefined && logEntry.toPlayer !== undefined) {
      const spellCard = cardDb[logEntry.spell];
      const spellName = spellCard?.name || 'Заклинание';
      if (logEntry.toIdx !== undefined && logEntry.toIdx >= 0) {
        const targetPlayer = state.players[logEntry.toPlayer];
        const targetMinion = targetPlayer?.board?.[logEntry.toIdx];
        const targetCard = cardDb[targetMinion?.cardId];
        const targetName = targetCard?.name || 'Миньон';
        const damage = logEntry.damage || '';
        const damageText = damage ? ' (' + damage + ' урона)' : '';
        text = who(logEntry.fromPlayer) + ' → ' + spellName + ' ✨ по ' + targetName + damageText + ' → ' + who(logEntry.toPlayer);
      } else {
        const damage = logEntry.damage || '';
        const damageText = damage ? ' (' + damage + ' урона)' : '';
        text = who(logEntry.fromPlayer) + ' → ' + spellName + ' ✨ по герою ' + who(logEntry.toPlayer) + damageText + '!';
      }
    } else if (logEntry.type === 'heroAbility' && logEntry.fromPlayer !== undefined && logEntry.toPlayer !== undefined) {
      if (logEntry.toIdx !== undefined && logEntry.toIdx >= 0) {
        const targetPlayer = state.players[logEntry.toPlayer];
        const targetMinion = targetPlayer?.board?.[logEntry.toIdx];
        const targetCard = cardDb[targetMinion?.cardId];
        const targetName = targetCard?.name || 'Миньон';
        const damage = logEntry.damage || 1;
        text = who(logEntry.fromPlayer) + ' → Геройская способность ⚡ по ' + targetName + ' (' + damage + ' урона) → ' + who(logEntry.toPlayer);
      } else {
        const damage = logEntry.damage || 1;
        text = who(logEntry.fromPlayer) + ' → Геройская способность ⚡ по герою ' + who(logEntry.toPlayer) + ' (' + damage + ' урона)!';
      }
    } else if (logEntry.type === 'battlecry' && logEntry.damage && logEntry.fromPlayer !== undefined && logEntry.toPlayer !== undefined) {
      const attackerPlayer = state.players[logEntry.fromPlayer];
      const targetPlayer = state.players[logEntry.toPlayer];
      if (attackerPlayer && targetPlayer) {
        if (logEntry.toIdx !== undefined && logEntry.toIdx >= 0) {
          const targetMinion = targetPlayer?.board?.[logEntry.toIdx];
          const targetCard = cardDb[targetMinion?.cardId];
          const targetName = targetCard?.name || 'Миньон';
          text = who(logEntry.fromPlayer) + ' → Battlecry 💥 по ' + targetName + ' (' + logEntry.damage + ' урона) → ' + who(logEntry.toPlayer);
        } else {
          text = who(logEntry.fromPlayer) + ' → Battlecry 💥 по герою ' + who(logEntry.toPlayer) + ' (' + logEntry.damage + ' урона)!';
        }
      }
    }
    
    if (text) {
      announcementEl.textContent = text;
      announcementEl.classList.remove('hidden');
    setTimeout(() => {
      announcementEl.classList.add('hidden');
    }, 2000); // Сократил с 3500 до 2000 мс
    }
  }

  function runCombatAnimation(state) {
    const lastLog = state.log && state.log.length ? state.log[state.log.length - 1] : null;
    if (!lastLog) return false;
    
    if (lastLog.type === 'attack' && lastLog.attackerMinionId && lastLog.targetMinionId) {
      const attackerWrap = document.querySelector(`[data-minion-id="${lastLog.attackerMinionId}"]`);
      const targetWrap = document.querySelector(`[data-minion-id="${lastLog.targetMinionId}"]`);
      if (!attackerWrap || !targetWrap) return false;
      attackerWrap.classList.add('combat-lunge');
      targetWrap.classList.add('combat-hit');
      if (lastLog.damage) showDamageEffect(targetWrap, lastLog.damage, false);
      if (typeof window.Sounds !== 'undefined') window.Sounds.attack();
      if (lastLog.attackerDied) {
        attackerWrap.classList.add('combat-death');
        showDamageEffect(attackerWrap, '💀', false);
      }
      if (lastLog.targetDied) {
        targetWrap.classList.add('combat-death');
        showDamageEffect(targetWrap, '💀', false);
      }
      combatTimeout = setTimeout(function () {
        combatTimeout = null;
        renderBoards(state);
        bindGameEvents(state);
      }, 600);
      return true;
    } else if (lastLog.type === 'attackHero' && lastLog.toPlayer !== undefined) {
      const targetPlayer = state.players[lastLog.toPlayer];
      if (targetPlayer) {
        const heroEl = document.querySelector(`[data-player-index="${lastLog.toPlayer}"].hero-target, .opponent-block[data-opponent-index="${lastLog.toPlayer}"] .drop-target-hero`);
        if (heroEl) {
          heroEl.classList.add('hero-damage-flash');
          if (lastLog.damage) showDamageEffect(heroEl, lastLog.damage, true);
          setTimeout(() => heroEl.classList.remove('hero-damage-flash'), 500);
        }
      }
      return true;
    } else if ((lastLog.type === 'battlecry' || lastLog.type === 'spell' || lastLog.type === 'heroAbility') && lastLog.damage && lastLog.toPlayer !== undefined) {
      const targetPlayer = state.players[lastLog.toPlayer];
      if (targetPlayer) {
        if (lastLog.toIdx !== undefined && lastLog.toIdx >= 0 && targetPlayer.board && targetPlayer.board[lastLog.toIdx]) {
          const minionEl = document.querySelector(`[data-player-index="${lastLog.toPlayer}"][data-board-index="${lastLog.toIdx}"]`);
          if (minionEl) {
            minionEl.classList.add('spell-hit');
            showDamageEffect(minionEl, lastLog.damage, false);
            setTimeout(() => minionEl.classList.remove('spell-hit'), 500);
          }
        } else {
          const heroEl = document.querySelector(`[data-player-index="${lastLog.toPlayer}"].hero-target, .opponent-block[data-opponent-index="${lastLog.toPlayer}"] .drop-target-hero`);
          if (heroEl) {
            heroEl.classList.add('hero-damage-flash');
            showDamageEffect(heroEl, lastLog.damage, true);
            setTimeout(() => heroEl.classList.remove('hero-damage-flash'), 500);
          }
        }
      }
      return true;
    }
    return false;
  }

  function renderGame(state) {
    const you = state.players[yourIndex];
    if (!you) return;

    $('your-mana').textContent = you.mana;
    $('your-max-mana').textContent = you.maxMana;
    $('your-health').textContent = you.health;
    $('your-deck').textContent = state.yourDeckCount ?? you.deck?.length ?? 0;
    const lastLog = state.log && state.log.length ? state.log[state.log.length - 1] : null;
    const logEl = $('game-log');
    if (logEl) {
      function logLine(entry) {
        if (!entry) return '';
        var who = function (i) { return state.players[i]?.name || ('Игрок ' + (i + 1)); };
        if (entry.type === 'play') {
          var c = cardDb[entry.cardId];
          return who(entry.playerIndex) + ' разыграл ' + (c?.name || entry.cardId);
        }
        if (entry.type === 'turn') return 'Ход: ' + who(entry.to);
        if (entry.type === 'skipTurn') return who(entry.playerIndex) + ' пропускает ход';
        if (entry.type === 'playerDefeated') return who(entry.playerIndex) + ' выбыл из игры';
        if (entry.type === 'attackHero') return 'Атака по герою!';
        if (entry.type === 'attack') return 'Бой!';
        if (entry.type === 'draw') return who(entry.playerIndex) + ' вытянул карту';
        if (entry.type === 'fatigue') return who(entry.playerIndex) + ' усталость (' + (entry.damage || 0) + ' урона)';
        if (entry.type === 'spell') return who(entry.fromPlayer) + ' заклинание';
        if (entry.type === 'heroAbility') return who(entry.fromPlayer) + ' — геройская способность';
        if (entry.type === 'battlecry' || entry.type === 'deathrattle') return '';
        return '';
      }
      var lines = (state.log || []).slice(-10).map(logLine).filter(Boolean);
      logEl.innerHTML = lines.map(function (s) { return '<div class="game-log-line">' + escapeHtml(s) + '</div>'; }).join('');
      logEl.scrollTop = logEl.scrollHeight;
    }
    if (lastLog?.type === 'attackHero' && typeof window.Sounds !== 'undefined') window.Sounds.attack();

    const isYourTurn = state.currentPlayerIndex === yourIndex;
    const endBtn = $('btn-end-turn');
    if (endBtn) endBtn.disabled = !isYourTurn;
    const heroBtn = $('btn-hero-ability');
    if (heroBtn) {
      var canHero = isYourTurn && (you.mana ?? 0) >= 2 && !state.yourHeroAbilityUsed && !attackMode.active && !spellMode.active && !heroAbilityMode.active;
      heroBtn.disabled = !canHero;
      heroBtn.classList.toggle('hidden', !isYourTurn);
    }
    const resetBtnEl = $('btn-reset-lobby');
    if (resetBtnEl) resetBtnEl.classList.add('hidden');
    const badge = $('turn-badge');
    if (badge) {
      badge.classList.toggle('your-turn', isYourTurn);
      badge.textContent = isYourTurn ? 'ВАШ ХОД' : 'Ход ' + (state.turn || 1);
    }

    var skipBoardUpdate = false;
    if (lastLog?.type === 'attack' && lastLog.attackerMinionId && lastLog.targetMinionId && prevGameState && runCombatAnimation(state)) {
      skipBoardUpdate = true;
      showAttackAnnouncement(state, lastLog);
    } else if (lastLog && (lastLog.type === 'attackHero' || lastLog.type === 'spell' || lastLog.type === 'heroAbility' || (lastLog.type === 'battlecry' && lastLog.damage))) {
      showAttackAnnouncement(state, lastLog);
    } else if (lastLog?.type === 'attack' && lastLog.attackerMinionId && lastLog.targetMinionId) {
      showAttackAnnouncement(state, lastLog);
    } else if (lastLog?.type === 'skipTurn' || lastLog?.type === 'playerDefeated') {
      showAttackAnnouncement(state, lastLog);
    }
    if (!skipBoardUpdate) {
      renderBoards(state);
    }

    const hand = you.hand || [];
    var justDrew = hand.length > lastHandLength && lastHandLength > 0;
    if (justDrew && typeof window.Sounds !== 'undefined') window.Sounds.drawCard();
    lastHandLength = hand.length;

    const handEl = $('your-hand');
    if (handEl) {
      handEl.innerHTML = hand
        .map((cid, i) => {
          var meta = cardDb[cid];
          var cost = meta?.cost ?? 0;
          var playable = false;
          if (meta?.type === 'minion') playable = you.mana >= cost && (you.board?.length ?? 0) < 7;
          else if (meta?.type === 'spell') playable = you.mana >= cost && !attackMode.active && !spellMode.active && !heroAbilityMode.active;
          var drawAnim = justDrew && i === hand.length - 1;
          return renderHandCard(cid, i, hand.length, playable, drawAnim);
        })
        .join('');
    }

    const deckCount = state.yourDeckCount ?? you.deck?.length ?? 0;
    const canDraw = isYourTurn && !state.yourManualDrawUsed && deckCount > 0 && (you.hand?.length ?? 0) < 10;
    renderDeckFan(deckCount, justDrew, canDraw);
    bindGameEvents(state);
  }

  function renderDeckFan(deckCount, justDrew, canDraw) {
    const fan = $('deck-fan');
    const stack = $('deck-stack');
    const badge = $('deck-count-badge');
    if (!stack) return;
    const n = Math.min(5, Math.max(0, deckCount));
    let html = '';
    for (let i = 0; i < 5; i++) {
      html += i < n ? '<div class="deck-card"></div>' : '';
    }
    stack.innerHTML = html;
    if (badge) badge.textContent = String(deckCount);
    if (fan) {
      fan.classList.toggle('deck-just-drew', !!justDrew);
      fan.classList.toggle('deck-drawable', !!canDraw);
      fan.title = canDraw ? 'Клик — вытянуть карту' : 'Колода';
      if (justDrew) setTimeout(function () { fan.classList.remove('deck-just-drew'); }, 400);
    }
  }

  function abilityTags(meta) {
    var tags = [];
    if (meta.battlecry) tags.push('<span class="ability-tag">Вой:</span>' + escapeHtml(meta.battlecry));
    if (meta.deathrattle) tags.push('<span class="ability-tag">Предсм.:</span>' + escapeHtml(meta.deathrattle));
    if (!tags.length) return '';
    return '<div class="card-abilities">' + tags.join(' ') + '</div>';
  }

  function renderHandCard(cardId, handIndex, handSize, playable, drawFromDeck) {
    var meta = cardDb[cardId];
    if (!meta) return '';
    var name = meta.name || cardId;
    var cost = meta.cost ?? 0;
    var atk = meta.attack ?? 0;
    var hp = meta.health ?? 0;
    var art = getCardArt(meta);
    var faction = meta.faction || 'neutral';
    var rotate = handSize <= 1 ? 0 : -10 + (handIndex / Math.max(1, handSize - 1)) * 20;
    var isMinion = meta.type === 'minion';
    var statsHtml = isMinion
      ? '<div class="card-cost-wrap"><span class="card-stat-label">Мана</span><span class="card-cost">' + cost + '</span></div>'
        + '<div class="card-atk-wrap"><span class="card-stat-label">Атака</span><span class="atk">' + atk + '</span></div>'
        + '<div class="card-hp-wrap"><span class="card-stat-label">Здоровье</span><span class="hp">' + hp + '</span></div>'
      : '<div class="card-cost-wrap"><span class="card-stat-label">Мана</span><span class="card-cost">' + cost + '</span></div>';
    var textHtml = meta.text ? '<div class="card-text">' + escapeHtml(meta.text) + '</div>' : '';
    var abilHtml = abilityTags(meta);
    var fictionalClass = meta.fictional ? ' fictional-card' : '';
    return '<div class="card-wrap in-hand ' + (playable ? 'playable' : '') + ' ' + (drawFromDeck ? 'draw-from-deck' : '') + fictionalClass + '" data-hand-index="' + handIndex + '" data-card-id="' + cardId + '" style="--hand-rotate: ' + rotate + 'deg" title="' + escapeHtml(name) + (meta.text ? ': ' + meta.text : '') + (meta.fictional ? ' [ВЫМЫШЛЕННЫЙ]' : '') + '">'
      + '<div class="card faction-' + faction + fictionalClass + '">'
      + '<div class="card-art">' + art + '</div>'
      + '<div class="card-info">'
      + '<div class="card-name">' + escapeHtml(name) + '</div>'
      + textHtml + abilHtml
      + '<div class="card-stats">' + statsHtml + '</div>'
      + '<div class="card-info-row"><span></span><button type="button" class="card-btn-info" data-card-id="' + cardId + '" title="Описание">i</button></div>'
      + '</div></div></div>';
  }

  var swgMap = {
    r2d2: 'r2d2', c3po: 'c3po', yoda: 'yoda', boba: 'bobbafett', vader: 'darthvader',
    stormtrooper: 'stormtrooper', deathstar: 'deathstar', atat: 'atat', xwing: 'xwing',
    tie: 'tie', falcon: 'falcon', leia: 'leia', chewie: 'wookie', ackbar: 'akbar',
    kylo: 'kylo', bb8: 'bb8', phasma: 'phasma', trooper: 'stormtrooper',
    jawas: 'galrep', sandcrawler: 'galrep', tusken: 'galrep', sarlacc: 'galrep',
    bantha: 'galrep', rancor: 'galrep', tauntaun: 'galrep', temple: 'jediorder',
    cantina: 'galrep', void: 'galrep', storm: 'jediorder', malice: 'darthvader',
    zephyr: 'galrep', walker: 'galrep', crystal: 'jediorder', shadow: 'darthvader',
    nebula: 'galrep', elite: 'galrep',
    anakin: 'saberjedi', kanan: 'saberjedi', ezra: 'saberjedi', cal_kestis: 'saberjedi',
    plo_koon: 'saberjedi', ki_adi: 'saberjedi', aayla: 'saberjedi', shaak_ti: 'saberjedi',
    savage: 'sabersith', jar_jar: 'galrep', tarkin: 'galemp', starkiller: 'sabersith',
    moff_gideon: 'galemp', rebel_trooper: 'galrep', rebel_commando: 'galrep', rebel_scout: 'galrep',
    clone_trooper: 'stormtrooper', clone_commando: 'stormtrooper', arc_trooper: 'stormtrooper',
    battle_droid: 'combatdrone', super_battle_droid: 'combatdrone', droideka: 'combatdrone',
    cody: 'stormtrooper', rex: 'stormtrooper', fives: 'stormtrooper', echo: 'stormtrooper',
    father: 'jediorder', son: 'sith', daughter: 'jediorder',
    korriban: 'galemp', tatooine: 'galrep', coruscant: 'galrep', zakkuul: 'galemp',
    kamino: 'galrep', mustafar: 'galemp', naboo: 'galrep', endor: 'galrep', hoth: 'galrep', dagobah: 'jediorder',
    star_destroyer: 'stardestroyer', speeder: 'landspeeder', wedge: 'xwing',
    nebulon: 'xwing', droideka: 'combatdrone', jango: 'bobbafett',
    luke: 'saberjedi', obiwan: 'saberjedi', quigon: 'saberjedi', mace: 'saberjedi',
    dooku: 'sabersith', maul: 'sabersith', snoke: 'sith', palpatine: 'sith',
    han: 'falcon', rey: 'newrep', finn: 'newrep', padme: 'galrep', jabba: 'carbonite',
    lando: 'cantina', ewok: 'jediorder', wicket: 'jediorder', scout: 'stormtrooper',
    guard: 'stormtrooper', probe: 'r2d2',
    ahsoka: 'saberjedi', ventress: 'sabersith', rex: 'stormtrooper', thrawn: 'galemp',
    mando: 'mandalorian', grogu: 'yoda', jyn: 'newrep', cassian: 'galrep',
    k2so: 'r2d2', chirrut: 'jediorder', baze: 'stormtrooper', poe: 'xwing',
    hux: 'stormtrooper', rose: 'galrep',
    lightsaber: 'saberjedi', force: 'saberjedi', blaster: 'tie', hyperspace: 'xwing', fear: 'sith', hope: 'galrep',
  };
  function getCardArt(meta) {
    var swg = swgMap[meta.art];
    if (swg) return '<i class="swg swg-lg swg-' + swg + ' card-art-swg" aria-hidden="true"></i>';
    return '<span class="card-art-fallback">✦</span>';
  }

  function renderBoardMinion(m, playerIndex, boardIndex, state, isOpponent, summonAnim) {
    const meta = cardDb[m.cardId];
    const name = meta?.name || m.cardId;
    const art = meta ? getCardArt(meta) : '✦';
    const faction = meta?.faction || 'neutral';
    const isYourTurn = state.currentPlayerIndex === state.yourIndex;
    const canAttackTwice = meta && meta.canAttackTwice;
    const attacksUsed = m.attacksUsed || 0;
    const canAttack = !isOpponent && m.canAttack && isYourTurn && (!canAttackTwice || attacksUsed < 2);
    const attackable = attackMode.active && attackMode.attackerPlayer === state.yourIndex && attackMode.attackerBoard === boardIndex;
    const targetable = attackMode.active && attackMode.attackerPlayer === state.yourIndex && playerIndex !== state.yourIndex;
    const cls = ['card-wrap'];
    if (canAttack && !attackMode.active) cls.push('attackable');
    if (attackable) cls.push('attackable');
    if (targetable) cls.push('targetable');
    if (isOpponent && isYourTurn) cls.push('drop-target');
    var spellTarget = false;
    if (spellMode.active && spellMode.spellTarget) {
      var st = spellMode.spellTarget;
      if (isOpponent && (st === 'any' || st === 'enemy_minion' || st === 'any_minion')) spellTarget = true;
      if (!isOpponent && (st === 'friendly_minion' || st === 'any_minion')) spellTarget = true;
    }
    var heroTarget = heroAbilityMode.active && isOpponent;
    if (heroTarget) cls.push('hero-ability-target');
    if (spellTarget) cls.push('spell-target');
    if (m.frozen) cls.push('frozen');
    if (meta && meta.fictional) cls.push('fictional-card');
    const draggable = !isOpponent && canAttack && !m.frozen ? ' draggable="true"' : '';
    const cardCls = 'card faction-' + faction + (summonAnim ? ' summon-anim' : '') + (m.frozen ? ' frozen-card' : '') + (meta && meta.fictional ? ' fictional-card' : '');
    const dropAttrs = (isOpponent && isYourTurn) || spellTarget || heroTarget ? ` data-drop-player="${playerIndex}" data-drop-board="${boardIndex}"` : '';
    const dragTitle = !isOpponent && canAttack && !m.frozen ? ' title="Перетащи на врага для атаки"' : (m.frozen ? ' title="Заморожен (не может атаковать)"' : '');
    var abilHtml = meta ? abilityTags(meta) : '';
    var textHtml = meta && meta.text ? '<div class="card-text">' + escapeHtml(meta.text) + '</div>' : '';
    var frozenIcon = m.frozen ? '<div class="frozen-icon">❄</div>' : '';
    return '<div class="' + cls.join(' ') + '" data-player-index="' + playerIndex + '" data-board-index="' + boardIndex + '" data-minion-id="' + m.id + '" data-attacker-board-index="' + (!isOpponent && canAttack && !m.frozen ? boardIndex : '') + '"' + draggable + dropAttrs + dragTitle + '>'
      + '<div class="' + cardCls + '">'
      + frozenIcon
      + '<div class="card-art">' + art + '</div>'
      + '<div class="card-info">'
      + '<div class="card-name">' + escapeHtml(name) + '</div>'
      + textHtml + abilHtml
      + '<div class="card-stats">'
      + '<div class="card-atk-wrap"><span class="card-stat-label">Атака</span><span class="atk">' + m.attack + '</span></div>'
      + '<div class="card-hp-wrap"><span class="card-stat-label">Здоровье</span><span class="hp">' + m.health + '</span></div>'
      + '</div>'
      + '<div class="card-info-row"><span></span><button type="button" class="card-btn-info" data-card-id="' + escapeHtml(m.cardId) + '" title="Описание">i</button></div>'
      + '</div></div></div>';
  }

  function renderHeroDropZone(playerIndex, state) {
    const isYourTurn = state.currentPlayerIndex === state.yourIndex;
    if (!isYourTurn || playerIndex === state.yourIndex) return '';
    if (attackMode.active && !spellMode.active && !heroAbilityMode.active) return '';
    var spellHero = spellMode.active && spellMode.spellTarget === 'any';
    var heroAb = heroAbilityMode.active;
    const p = state.players[playerIndex];
    const name = p?.name || 'Игрок ' + (playerIndex + 1);
    var heroCls = 'drop-target drop-target-hero' + (spellHero ? ' spell-target' : '') + (heroAb ? ' hero-ability-target' : '');
    var title = spellHero ? 'Цель заклинания' : (heroAb ? 'Цель геройской способности' : 'Перетащи сюда миньона для атаки по лидеру');
    return '<div class="' + heroCls + '" data-drop-player="' + playerIndex + '" data-drop-board="-1" title="' + title + '">'
      + '<span class="drop-hero-icon">❤</span>'
      + '<span class="drop-hero-name">' + escapeHtml(name) + '</span>'
      + '</div>';
  }

  function renderHeroTarget(playerIndex, state) {
    if (!attackMode.active || attackMode.attackerPlayer !== state.yourIndex || playerIndex === state.yourIndex) return '';
    const p = state.players[playerIndex];
    const name = p?.name || 'Игрок ' + (playerIndex + 1);
    return `
      <div class="card-wrap targetable hero-target drop-target" data-player-index="${playerIndex}" data-board-index="-1" data-drop-player="${playerIndex}" data-drop-board="-1">
        <div class="card faction-neutral">
          <div class="card-art">❤</div>
          <div class="card-info">
            <div class="card-name">${escapeHtml(name)} (лидер)</div>
            <div class="card-stats"><span class="hp">${p?.health ?? 30}</span></div>
          </div>
        </div>
      </div>`;
  }

  function bindGameEvents(state) {
    const you = state.players[state.yourIndex];
    const isYourTurn = state.currentPlayerIndex === state.yourIndex;

    $all('.card-wrap.in-hand').forEach((wrap) => {
      wrap.classList.toggle('disabled', !isYourTurn);
      const handIndex = parseInt(wrap.dataset.handIndex, 10);
      const meta = cardDb[wrap.dataset.cardId];
      const isMinion = meta && meta.type === 'minion';
      const canPlay = isYourTurn && !attackMode.active && !heroAbilityMode.active && meta && you.mana >= (meta.cost || 0);
      
      if (isMinion && canPlay && (you.board?.length ?? 0) < 7) {
        wrap.draggable = true;
        wrap.ondragstart = function (e) {
          if (!canPlay) return;
          e.dataTransfer.setData('text/plain', 'hand:' + handIndex);
          e.dataTransfer.effectAllowed = 'move';
          wrap.classList.add('dragging');
        };
        wrap.ondragend = function () {
          wrap.classList.remove('dragging');
          $all('.drop-target').forEach(function (t) { t.classList.remove('drop-over'); });
        };
      } else {
        wrap.draggable = false;
        wrap.ondragstart = null;
        wrap.ondragend = null;
      }
      
      wrap.onclick = function (e) {
        if (e.target.closest('.card-btn-info')) return;
        if (!isYourTurn || attackMode.active || heroAbilityMode.active) return;
        if (!meta) return;
        if (you.mana < (meta.cost || 0)) return;
        if (meta.type === 'minion') {
          if ((you.board?.length ?? 0) >= 7) return;
          if (typeof window.Sounds !== 'undefined') window.Sounds.playCard();
          var r = wrap.getBoundingClientRect();
          spawnEffect('play', r.left + r.width / 2, r.top + r.height / 2);
          wrap.querySelector('.card')?.classList.add('play-anim');
          socket.emit('playCard', { handIndex: handIndex, boardPos: you.board.length });
          setTimeout(function () { wrap.querySelector('.card')?.classList.remove('play-anim'); }, 500);
          return;
        }
        if (meta.type === 'spell') {
          if (spellMode.active || heroAbilityMode.active) return;
          var needTarget = meta.spellTarget && meta.spellTarget !== 'none';
          if (!needTarget) {
            if (typeof window.Sounds !== 'undefined') window.Sounds.playCard();
            socket.emit('playSpell', { handIndex: handIndex });
            return;
          }
          spellMode = { active: true, handIndex: handIndex, cardId: wrap.dataset.cardId, spellTarget: meta.spellTarget };
          $('spell-mode')?.classList.remove('hidden');
          renderGame(state);
          return;
        }
      };
      
      // Touch support for mobile
      if (isTouchDevice()) {
        wrap.ontouchstart = function (e) {
          if (e.target.closest('.card-btn-info')) {
            // Handle info button separately
            const btn = e.target.closest('.card-btn-info');
            if (btn) {
              const cid = btn.dataset.cardId;
              if (cid) {
                const meta = cardDb[cid];
                if (meta) {
                  const nameEl = $('card-info-name');
                  const metaEl = $('card-info-meta');
                  const textEl = $('card-info-text');
                  const abilEl = $('card-info-abilities');
                  const bioEl = $('card-info-bio');
                  if (nameEl) nameEl.textContent = meta.name || cid;
                  if (metaEl) {
                    if (meta.type === 'spell') metaEl.textContent = 'Заклинание · Мана: ' + (meta.cost ?? 0);
                    else metaEl.textContent = 'Минайон · Мана: ' + (meta.cost ?? 0) + ', Атака: ' + (meta.attack ?? 0) + ', Здоровье: ' + (meta.health ?? 0);
                  }
                  if (textEl) { textEl.textContent = meta.text || ''; textEl.classList.toggle('hidden', !meta.text); }
                  if (abilEl) {
                    var parts = [];
                    if (meta.battlecry) parts.push('<strong>Вой:</strong> ' + escapeHtml(meta.battlecry));
                    if (meta.deathrattle) parts.push('<strong>Предсмертный хрип:</strong> ' + escapeHtml(meta.deathrattle));
                    abilEl.innerHTML = parts.length ? parts.join('<br>') : '';
                    abilEl.classList.toggle('hidden', !parts.length);
                  }
                  if (bioEl) { bioEl.textContent = meta.bio || ''; bioEl.classList.toggle('hidden', !meta.bio); }
                  $('card-info-overlay')?.classList.remove('hidden');
                }
              }
            }
            return;
          }
          
          if (!isYourTurn || attackMode.active || heroAbilityMode.active) return;
          if (!meta) return;
          
          const touch = e.touches[0];
          touchState.startX = touch.clientX;
          touchState.startY = touch.clientY;
          touchState.startTime = Date.now();
          touchState.target = wrap;
          touchState.isSwipe = false;
          wrap.classList.add('touch-active');
          
          // Long press for card info
          touchState.longPressTimer = setTimeout(function() {
            if (touchState.target === wrap && meta) {
              const nameEl = $('card-info-name');
              const metaEl = $('card-info-meta');
              const textEl = $('card-info-text');
              const abilEl = $('card-info-abilities');
              const bioEl = $('card-info-bio');
              if (nameEl) nameEl.textContent = meta.name || wrap.dataset.cardId;
              if (metaEl) {
                if (meta.type === 'spell') metaEl.textContent = 'Заклинание · Мана: ' + (meta.cost ?? 0);
                else metaEl.textContent = 'Минайон · Мана: ' + (meta.cost ?? 0) + ', Атака: ' + (meta.attack ?? 0) + ', Здоровье: ' + (meta.health ?? 0);
              }
              if (textEl) { textEl.textContent = meta.text || ''; textEl.classList.toggle('hidden', !meta.text); }
              if (abilEl) {
                var parts = [];
                if (meta.battlecry) parts.push('<strong>Вой:</strong> ' + escapeHtml(meta.battlecry));
                if (meta.deathrattle) parts.push('<strong>Предсмертный хрип:</strong> ' + escapeHtml(meta.deathrattle));
                abilEl.innerHTML = parts.length ? parts.join('<br>') : '';
                abilEl.classList.toggle('hidden', !parts.length);
              }
              if (bioEl) { bioEl.textContent = meta.bio || ''; bioEl.classList.toggle('hidden', !meta.bio); }
              $('card-info-overlay')?.classList.remove('hidden');
              wrap.classList.add('long-pressed');
            }
          }, touchState.longPressDelay);
        };
        
        wrap.ontouchmove = function (e) {
          if (!touchState.target || touchState.target !== wrap) return;
          
          // Cancel long press if moved
          if (touchState.longPressTimer) {
            clearTimeout(touchState.longPressTimer);
            touchState.longPressTimer = null;
          }
          
          const touch = e.touches[0];
          const dx = touch.clientX - touchState.startX;
          const dy = touch.clientY - touchState.startY;
          const distance = Math.sqrt(dx * dx + dy * dy);
          
          if (distance > touchState.minSwipeDistance) {
            touchState.isSwipe = true;
            wrap.classList.add('swiping');
            wrap.classList.remove('long-pressed');
          }
        };
        
        wrap.ontouchend = function (e) {
          if (!touchState.target || touchState.target !== wrap) return;
          
          // Cancel long press timer
          if (touchState.longPressTimer) {
            clearTimeout(touchState.longPressTimer);
            touchState.longPressTimer = null;
          }
          
          wrap.classList.remove('touch-active', 'swiping', 'long-pressed');
          
          const touch = e.changedTouches[0];
          const dx = touch.clientX - touchState.startX;
          const dy = touch.clientY - touchState.startY;
          const distance = Math.sqrt(dx * dx + dy * dy);
          const time = Date.now() - touchState.startTime;
          
          // If it was a swipe or long press, don't trigger card play
          if (touchState.isSwipe || distance > touchState.minSwipeDistance || time > touchState.longPressDelay) {
            touchState.target = null;
            return;
          }
          
          // Tap - trigger card play
          if (time < touchState.maxSwipeTime && distance < 10 && you.mana >= (meta?.cost || 0)) {
            if (meta?.type === 'minion' && (you.board?.length ?? 0) < 7) {
              if (typeof window.Sounds !== 'undefined') window.Sounds.playCard();
              var r = wrap.getBoundingClientRect();
              spawnEffect('play', r.left + r.width / 2, r.top + r.height / 2);
              wrap.querySelector('.card')?.classList.add('play-anim');
              socket.emit('playCard', { handIndex: handIndex, boardPos: you.board.length });
              setTimeout(function () { wrap.querySelector('.card')?.classList.remove('play-anim'); }, 500);
            } else if (meta?.type === 'spell') {
              if (spellMode.active || heroAbilityMode.active) return;
              var needTarget = meta.spellTarget && meta.spellTarget !== 'none';
              if (!needTarget) {
                if (typeof window.Sounds !== 'undefined') window.Sounds.playCard();
                socket.emit('playSpell', { handIndex: handIndex });
              } else {
                spellMode = { active: true, handIndex: handIndex, cardId: wrap.dataset.cardId, spellTarget: meta.spellTarget };
                $('spell-mode')?.classList.remove('hidden');
                renderGame(state);
              }
            }
          }
          
          touchState.target = null;
        };
        
        wrap.ontouchcancel = function () {
          if (touchState.longPressTimer) {
            clearTimeout(touchState.longPressTimer);
            touchState.longPressTimer = null;
          }
          wrap.classList.remove('touch-active', 'swiping', 'long-pressed');
          touchState.target = null;
        };
      }
    });

    $all('.card-btn-info').forEach(function (btn) {
      btn.onclick = function (e) {
        e.stopPropagation();
        var cid = btn.dataset.cardId;
        if (!cid) return;
        var meta = cardDb[cid];
        if (!meta) return;
        var nameEl = $('card-info-name');
        var metaEl = $('card-info-meta');
        var textEl = $('card-info-text');
        var abilEl = $('card-info-abilities');
        var bioEl = $('card-info-bio');
        if (nameEl) nameEl.textContent = meta.name || cid;
        if (metaEl) {
          if (meta.type === 'spell') metaEl.textContent = 'Заклинание · Мана: ' + (meta.cost ?? 0);
          else metaEl.textContent = 'Минайон · Мана: ' + (meta.cost ?? 0) + ', Атака: ' + (meta.attack ?? 0) + ', Здоровье: ' + (meta.health ?? 0);
        }
        if (textEl) { textEl.textContent = meta.text || ''; textEl.classList.toggle('hidden', !meta.text); }
        if (abilEl) {
          var parts = [];
          if (meta.battlecry) parts.push('<strong>Вой:</strong> ' + escapeHtml(meta.battlecry));
          if (meta.deathrattle) parts.push('<strong>Предсмертный хрип:</strong> ' + escapeHtml(meta.deathrattle));
          abilEl.innerHTML = parts.length ? parts.join('<br>') : '';
          abilEl.classList.toggle('hidden', !parts.length);
        }
        if (bioEl) { bioEl.textContent = meta.bio || ''; bioEl.classList.toggle('hidden', !meta.bio); }
        $('card-info-overlay')?.classList.remove('hidden');
      };
    });
    var closeInfo = $('btn-card-info-close');
    if (closeInfo) closeInfo.onclick = function () { $('card-info-overlay')?.classList.add('hidden'); };

    $all('.card-wrap.attackable:not(.targetable)').forEach((wrap) => {
      wrap.onclick = () => {
        if (!isYourTurn || attackMode.active || spellMode.active || heroAbilityMode.active) return;
        const playerIndex = parseInt(wrap.dataset.playerIndex, 10);
        const boardIndex = parseInt(wrap.dataset.boardIndex, 10);
        if (playerIndex !== state.yourIndex) return;
        attackMode = { active: true, attackerPlayer: playerIndex, attackerBoard: boardIndex };
        $('attack-mode')?.classList.remove('hidden');
        renderGame(state);
      };
      
      // Touch support for attack
      if (isTouchDevice()) {
        wrap.ontouchstart = function (e) {
          if (!isYourTurn || attackMode.active || spellMode.active || heroAbilityMode.active) return;
          const playerIndex = parseInt(wrap.dataset.playerIndex, 10);
          const boardIndex = parseInt(wrap.dataset.boardIndex, 10);
          if (playerIndex !== state.yourIndex) return;
          
          const touch = e.touches[0];
          touchState.startX = touch.clientX;
          touchState.startY = touch.clientY;
          touchState.startTime = Date.now();
          touchState.target = wrap;
        };
        
        wrap.ontouchend = function (e) {
          if (!touchState.target || touchState.target !== wrap) return;
          const touch = e.changedTouches[0];
          const dx = touch.clientX - touchState.startX;
          const dy = touch.clientY - touchState.startY;
          const distance = Math.sqrt(dx * dx + dy * dy);
          const time = Date.now() - touchState.startTime;
          
          if (time < touchState.maxSwipeTime && distance < 20) {
            const playerIndex = parseInt(wrap.dataset.playerIndex, 10);
            const boardIndex = parseInt(wrap.dataset.boardIndex, 10);
            if (playerIndex === state.yourIndex) {
              attackMode = { active: true, attackerPlayer: playerIndex, attackerBoard: boardIndex };
              $('attack-mode')?.classList.remove('hidden');
              renderGame(state);
            }
          }
          
          touchState.target = null;
        };
      }
    });

    $all('.card-wrap.targetable, .card-wrap.hero-target').forEach((wrap) => {
      wrap.onclick = () => {
        if (spellMode.active || heroAbilityMode.active) return;
        if (!attackMode.active) return;
        const targetPlayer = parseInt(wrap.dataset.playerIndex ?? wrap.dataset.dropPlayer, 10);
        const targetBoard = parseInt(wrap.dataset.boardIndex ?? wrap.dataset.dropBoard, 10);
        if (typeof window.Sounds !== 'undefined') window.Sounds.attack();
        socket.emit('attack', {
          attackerPlayerIndex: state.yourIndex,
          attackerBoardIndex: attackMode.attackerBoard,
          targetPlayerIndex: targetPlayer,
          targetBoardIndex: targetBoard,
        });
        attackMode = { active: false, attackerPlayer: -1, attackerBoard: -1 };
        $('attack-mode')?.classList.add('hidden');
      };
      
      // Touch support for target selection
      if (isTouchDevice()) {
        wrap.ontouchstart = function (e) {
          if (spellMode.active || heroAbilityMode.active) return;
          if (!attackMode.active) return;
          e.preventDefault();
          const targetPlayer = parseInt(wrap.dataset.playerIndex ?? wrap.dataset.dropPlayer, 10);
          const targetBoard = parseInt(wrap.dataset.boardIndex ?? wrap.dataset.dropBoard, 10);
          if (typeof window.Sounds !== 'undefined') window.Sounds.attack();
          socket.emit('attack', {
            attackerPlayerIndex: state.yourIndex,
            attackerBoardIndex: attackMode.attackerBoard,
            targetPlayerIndex: targetPlayer,
            targetBoardIndex: targetBoard,
          });
          attackMode = { active: false, attackerPlayer: -1, attackerBoard: -1 };
          $('attack-mode')?.classList.add('hidden');
        };
      }
    });

    $all('.spell-target, .drop-target-hero.spell-target').forEach(function (el) {
      el.onclick = function (e) {
        if (!spellMode.active) return;
        e.stopPropagation();
        var tp = parseInt(el.dataset.dropPlayer ?? el.dataset.playerIndex, 10);
        var tb = parseInt(el.dataset.dropBoard ?? el.dataset.boardIndex, 10);
        if (typeof window.Sounds !== 'undefined') window.Sounds.playCard();
        socket.emit('playSpell', { handIndex: spellMode.handIndex, targetPlayerIndex: tp, targetBoardIndex: tb });
        spellMode = { active: false, handIndex: -1, cardId: '', spellTarget: '' };
        $('spell-mode')?.classList.add('hidden');
        renderGame(state);
      };
      
      // Touch support for spell targets
      if (isTouchDevice()) {
        el.ontouchstart = function (e) {
          if (!spellMode.active) return;
          e.preventDefault();
          e.stopPropagation();
          var tp = parseInt(el.dataset.dropPlayer ?? el.dataset.playerIndex, 10);
          var tb = parseInt(el.dataset.dropBoard ?? el.dataset.boardIndex, 10);
          if (typeof window.Sounds !== 'undefined') window.Sounds.playCard();
          socket.emit('playSpell', { handIndex: spellMode.handIndex, targetPlayerIndex: tp, targetBoardIndex: tb });
          spellMode = { active: false, handIndex: -1, cardId: '', spellTarget: '' };
          $('spell-mode')?.classList.add('hidden');
          renderGame(state);
        };
      }
    });

    $all('.hero-ability-target, .drop-target-hero.hero-ability-target').forEach(function (el) {
      el.onclick = function (e) {
        if (!heroAbilityMode.active) return;
        e.stopPropagation();
        var tp = parseInt(el.dataset.dropPlayer ?? el.dataset.playerIndex, 10);
        var tb = parseInt(el.dataset.dropBoard ?? el.dataset.boardIndex, 10);
        if (typeof window.Sounds !== 'undefined') window.Sounds.attack();
        socket.emit('heroAbility', { targetPlayerIndex: tp, targetBoardIndex: tb });
        heroAbilityMode = { active: false };
        $('hero-ability-mode')?.classList.add('hidden');
        renderGame(state);
      };
      
      // Touch support for hero ability targets
      if (isTouchDevice()) {
        el.ontouchstart = function (e) {
          if (!heroAbilityMode.active) return;
          e.preventDefault();
          e.stopPropagation();
          var tp = parseInt(el.dataset.dropPlayer ?? el.dataset.playerIndex, 10);
          var tb = parseInt(el.dataset.dropBoard ?? el.dataset.boardIndex, 10);
          if (typeof window.Sounds !== 'undefined') window.Sounds.attack();
          socket.emit('heroAbility', { targetPlayerIndex: tp, targetBoardIndex: tb });
          heroAbilityMode = { active: false };
          $('hero-ability-mode')?.classList.add('hidden');
          renderGame(state);
        };
      }
    });

    var heroAbilityBtn = $('btn-hero-ability');
    if (heroAbilityBtn) heroAbilityBtn.onclick = function () {
      if (heroAbilityBtn.disabled || heroAbilityMode.active) return;
      heroAbilityMode = { active: true };
      $('hero-ability-mode')?.classList.remove('hidden');
      renderGame(state);
    };
    
    var forgeBtn = $('btn-forge');
    if (forgeBtn && isYourTurn && you.mana >= 2) {
      forgeBtn.disabled = false;
      forgeBtn.onclick = function () {
        if (attackMode.active || spellMode.active || heroAbilityMode.active) return;
        $('forge-overlay')?.classList.remove('hidden');
        renderForgeDeck(state);
      };
    } else if (forgeBtn) {
      forgeBtn.disabled = true;
    }

    var cancelSpellBtn = $('btn-cancel-spell');
    if (cancelSpellBtn) cancelSpellBtn.onclick = function () {
      spellMode = { active: false, handIndex: -1, cardId: '', spellTarget: '' };
      attackMode = { active: false, attackerPlayer: -1, attackerBoard: -1 };
      heroAbilityMode = { active: false };
      $('spell-mode')?.classList.add('hidden');
      $('attack-mode')?.classList.add('hidden');
      $('hero-ability-mode')?.classList.add('hidden');
      renderGame(state);
    };

    var cancelHeroBtn = $('btn-cancel-hero');
    if (cancelHeroBtn) cancelHeroBtn.onclick = function () {
      heroAbilityMode = { active: false };
      $('hero-ability-mode')?.classList.add('hidden');
      renderGame(state);
    };

    const cancelBtn = $('btn-cancel-attack');
    if (cancelBtn) {
      cancelBtn.onclick = function () {
        attackMode = { active: false, attackerPlayer: -1, attackerBoard: -1 };
        spellMode = { active: false, handIndex: -1, cardId: '', spellTarget: '' };
        heroAbilityMode = { active: false };
        $('attack-mode')?.classList.add('hidden');
        $('spell-mode')?.classList.add('hidden');
        $('hero-ability-mode')?.classList.add('hidden');
        renderGame(state);
      };
    }

    const endBtn = $('btn-end-turn');
    if (endBtn) {
      endBtn.onclick = isYourTurn
        ? () => {
            if (typeof window.Sounds !== 'undefined') window.Sounds.endTurn();
            socket.emit('endTurn');
          }
        : null;
    }

    const resetBtn = $('btn-reset-lobby');
    if (resetBtn) {
      resetBtn.onclick = state.yourIndex === 0 ? () => socket.emit('resetToLobby') : null;
    }

    var deckEl = $('deck-fan');
    if (deckEl && deckEl.classList.contains('deck-drawable')) {
      deckEl.onclick = function () {
        var r = deckEl.getBoundingClientRect();
        spawnEffect('draw', r.left + r.width / 2, r.top + r.height / 2);
        socket.emit('drawCard');
      };
      deckEl.style.cursor = 'pointer';
      
      // Touch support for deck
      if (isTouchDevice()) {
        deckEl.ontouchstart = function (e) {
          e.preventDefault();
          var r = deckEl.getBoundingClientRect();
          spawnEffect('draw', r.left + r.width / 2, r.top + r.height / 2);
          socket.emit('drawCard');
        };
      }
    } else if (deckEl) {
      deckEl.onclick = null;
      deckEl.style.cursor = 'default';
      if (isTouchDevice()) {
        deckEl.ontouchstart = null;
      }
    }

    $all('.card-wrap.attackable[draggable="true"]').forEach(function (wrap) {
      wrap.ondragstart = function (e) {
        var idx = wrap.dataset.attackerBoardIndex;
        if (idx === undefined) return;
        e.dataTransfer.setData('text/plain', idx);
        e.dataTransfer.effectAllowed = 'move';
        wrap.classList.add('dragging');
      };
      wrap.ondragend = function () {
        wrap.classList.remove('dragging');
        $all('.drop-target').forEach(function (t) { t.classList.remove('drop-over'); });
      };
    });

    $all('.drop-target').forEach(function (target) {
      target.ondragover = function (e) {
        e.preventDefault();
        e.dataTransfer.dropEffect = 'move';
        target.classList.add('drop-over');
      };
      target.ondragleave = function () {
        target.classList.remove('drop-over');
      };
      target.ondrop = function (e) {
        e.preventDefault();
        target.classList.remove('drop-over');
        var data = e.dataTransfer.getData('text/plain');
        if (data === '') return;
        
        if (data.startsWith('hand:')) {
          var handIndex = parseInt(data.split(':')[1], 10);
          if (isNaN(handIndex)) return;
          var meta = cardDb[you.hand?.[handIndex]];
          if (!meta || meta.type !== 'minion') return;
          if (you.mana < (meta.cost || 0)) return;
          if ((you.board?.length ?? 0) >= 7) return;
          if (typeof window.Sounds !== 'undefined') window.Sounds.playCard();
          var rect = target.getBoundingClientRect();
          spawnEffect('play', rect.left + rect.width / 2, rect.top + rect.height / 2);
          socket.emit('playCard', { handIndex: handIndex, boardPos: you.board.length });
          return;
        }
        
        var attackerBoard = parseInt(data, 10);
        var targetPlayer = parseInt(target.dataset.dropPlayer, 10);
        var targetBoard = target.dataset.dropBoard !== undefined ? parseInt(target.dataset.dropBoard, 10) : -1;
        if (isNaN(attackerBoard) || isNaN(targetPlayer)) return;
        if (typeof window.Sounds !== 'undefined') window.Sounds.attack();
        var rect = target.getBoundingClientRect();
        spawnEffect('attack', rect.left + rect.width / 2, rect.top + rect.height / 2);
        socket.emit('attack', {
          attackerPlayerIndex: state.yourIndex,
          attackerBoardIndex: attackerBoard,
          targetPlayerIndex: targetPlayer,
          targetBoardIndex: isNaN(targetBoard) ? -1 : targetBoard,
        });
        attackMode = { active: false, attackerPlayer: -1, attackerBoard: -1 };
        $('attack-mode')?.classList.add('hidden');
      };
    });
    
    var yourBoardEl = $('your-board');
    if (yourBoardEl && isYourTurn && !attackMode.active && !spellMode.active && !heroAbilityMode.active) {
      yourBoardEl.classList.add('drop-target');
      yourBoardEl.ondragover = function (e) {
        e.preventDefault();
        e.dataTransfer.dropEffect = 'move';
        yourBoardEl.classList.add('drop-over');
      };
      yourBoardEl.ondragleave = function () {
        yourBoardEl.classList.remove('drop-over');
      };
      yourBoardEl.ondrop = function (e) {
        e.preventDefault();
        yourBoardEl.classList.remove('drop-over');
        var data = e.dataTransfer.getData('text/plain');
        if (!data.startsWith('hand:')) return;
        var handIndex = parseInt(data.split(':')[1], 10);
        if (isNaN(handIndex)) return;
        var meta = cardDb[you.hand?.[handIndex]];
        if (!meta || meta.type !== 'minion') return;
        if (you.mana < (meta.cost || 0)) return;
        if ((you.board?.length ?? 0) >= 7) return;
        if (typeof window.Sounds !== 'undefined') window.Sounds.playCard();
        var rect = yourBoardEl.getBoundingClientRect();
        spawnEffect('play', rect.left + rect.width / 2, rect.top + rect.height / 2);
        socket.emit('playCard', { handIndex: handIndex, boardPos: you.board.length });
      };
    } else if (yourBoardEl) {
      yourBoardEl.classList.remove('drop-target');
      yourBoardEl.ondragover = null;
      yourBoardEl.ondragleave = null;
      yourBoardEl.ondrop = null;
    }
  }

  function initLobby() {
    const hostTab = document.querySelector('.tab[data-tab="host"]');
    const joinTab = document.querySelector('.tab[data-tab="join"]');
    hostTab?.addEventListener('click', () => {
      $all('.tab').forEach((t) => t.classList.remove('active'));
      hostTab.classList.add('active');
      $('host-panel')?.classList.add('active');
      $('join-panel')?.classList.remove('active');
      clearError();
    });
    joinTab?.addEventListener('click', () => {
      $all('.tab').forEach((t) => t.classList.remove('active'));
      joinTab.classList.add('active');
      $('join-panel')?.classList.add('active');
      $('host-panel')?.classList.remove('active');
      clearError();
    });

    $('btn-host')?.addEventListener('click', () => {
      clearError();
      const name = ($('host-name')?.value || '').trim() || 'Игрок 1';
      const currentHost = window.location.hostname;
      let url = window.location.origin;
      if (currentHost !== 'localhost' && currentHost !== '127.0.0.1') {
        url = window.location.origin;
      }
      
      if (!socket || !socket.connected) {
        connect(url);
        socket.once('connect', () => {
          socket.emit('createRoom', name);
        });
        socket.once('connect_error', (e) => {
          showError('Не удалось подключиться к серверу. Проверьте, что сервер запущен.');
        });
      } else {
        // Уже подключен, сразу отправляем
        socket.emit('createRoom', name);
      }
    });

    $('btn-join')?.addEventListener('click', () => {
      clearError();
      const addr = ($('join-addr')?.value || '').trim();
      let url;
      if (addr && addr !== 'Оставьте пустым для авто') {
        const parts = addr.split(':');
        const host = parts[0] || 'localhost';
        const port = parts[1] || DEFAULT_PORT;
        url = `http://${host}:${port}`;
      } else {
        url = window.location.origin;
      }
      connect(url);
      socket.once('connect', () => {
        setLobbyPanel('connect');
        $('connect-panel')?.classList.add('active');
      });
      socket.once('connect_error', () => {
        const detailsEl = document.querySelector('#join-panel details');
        if (detailsEl && !detailsEl.open) {
          detailsEl.open = true;
          showError('Не удалось подключиться автоматически. Проверьте расширенные настройки и введите IP сервера вручную.');
        }
      });
    });

    var joinCodeHandler = function () {
      clearError();
      const code = ($('join-code')?.value || '').trim().toUpperCase();
      if (!code || code.length !== 5) {
        showError('Введите код комнаты (5 символов)');
        return;
      }
      const name = ($('join-name')?.value || '').trim() || 'Игрок 2';
      if (!socket || !socket.connected) {
        const addr = ($('join-addr')?.value || '').trim();
        let url;
        if (addr && addr !== 'Оставьте пустым для авто') {
          const parts = addr.split(':');
          const host = parts[0] || 'localhost';
          const port = parts[1] || DEFAULT_PORT;
          url = `http://${host}:${port}`;
        } else {
          url = window.location.origin;
        }
        connect(url);
        socket.once('connect', () => {
          socket.emit('joinRoom', { code, name });
        });
      } else {
        socket.emit('joinRoom', { code, name });
      }
    };
    $('btn-join-code')?.addEventListener('click', joinCodeHandler);
    $('join-code')?.addEventListener('keypress', function (e) {
      if (e.key === 'Enter') joinCodeHandler();
    });

    $('btn-start')?.addEventListener('click', () => {
      clearError();
      var faction = $('room-faction')?.value || '';
      socket?.emit('startGame', { faction: faction || undefined });
    });

    $('btn-leave')?.addEventListener('click', () => {
      socket?.emit('leaveLobby');
      setLobbyPanel('host');
      $('host-panel')?.classList.add('active');
      showScreen('lobby');
    });

    $('btn-leave-join')?.addEventListener('click', () => {
      socket?.emit('leaveLobby');
      socket?.disconnect();
      setLobbyPanel('join');
      $('join-panel')?.classList.add('active');
      showScreen('lobby');
    });

    $('btn-modal-close')?.addEventListener('click', () => {
      $('modal-overlay')?.classList.add('hidden');
    });
    $('btn-instructions')?.addEventListener('click', () => {
      $('instructions-overlay')?.classList.remove('hidden');
    });
    $('btn-instructions-lobby')?.addEventListener('click', () => {
      $('instructions-overlay')?.classList.remove('hidden');
    });
    $('btn-instructions-close')?.addEventListener('click', () => {
      $('instructions-overlay')?.classList.add('hidden');
    });
    
    $('btn-cards-gallery')?.addEventListener('click', () => {
      $('cards-gallery-overlay')?.classList.remove('hidden');
      renderCardsGallery();
    });
    $('btn-cards-gallery-game')?.addEventListener('click', () => {
      $('cards-gallery-overlay')?.classList.remove('hidden');
      renderCardsGallery();
    });
    $('btn-gallery-close')?.addEventListener('click', () => {
      $('cards-gallery-overlay')?.classList.add('hidden');
    });
    $('cards-gallery-overlay')?.addEventListener('click', (e) => {
      if (e.target.id === 'cards-gallery-overlay') {
        $('cards-gallery-overlay')?.classList.add('hidden');
      }
    });
    
    $('gallery-faction-filter')?.addEventListener('change', renderCardsGallery);
    $('gallery-type-filter')?.addEventListener('change', renderCardsGallery);
    $('gallery-search')?.addEventListener('input', renderCardsGallery);
    
    let galleryCardDb = null;
    function renderCardsGallery() {
      const grid = $('cards-gallery-grid');
      if (!grid) return;
      
      if (!galleryCardDb) {
        fetch('/api/cards')
          .then(res => res.json())
          .then(data => {
            galleryCardDb = data;
            renderCardsGallery();
          })
          .catch(() => {
            grid.innerHTML = '<p style="color: #94a3b8; text-align: center; padding: 2rem;">Не удалось загрузить карты</p>';
          });
        return;
      }
      
      const factionFilter = ($('gallery-faction-filter')?.value || '').toLowerCase();
      const typeFilter = ($('gallery-type-filter')?.value || '').toLowerCase();
      const searchText = ($('gallery-search')?.value || '').toLowerCase();
      
      const allCards = Object.keys(galleryCardDb || {}).map(id => ({ id, ...galleryCardDb[id] }));
      const filtered = allCards.filter(card => {
        if (factionFilter && card.faction !== factionFilter) return false;
        if (typeFilter && card.type !== typeFilter) return false;
        if (searchText && !card.name.toLowerCase().includes(searchText)) return false;
        return true;
      });
      
      grid.innerHTML = filtered.map(card => {
        const isFictional = card.fictional || false;
        const cost = card.cost || 0;
        const attack = card.attack !== undefined ? card.attack : '';
        const health = card.health !== undefined ? card.health : '';
        const stats = card.type === 'minion' ? `<div class="card-stats"><span class="atk">${attack}</span><span class="hp">${health}</span></div>` : '';
        const costDisplay = card.type === 'spell' ? `<div class="card-cost-wrap"><span class="card-cost">${cost}</span></div>` : '';
        const art = getCardArt ? getCardArt(card) : '✦';
        
        return `<div class="card-wrap gallery-card ${isFictional ? 'fictional' : ''}" data-card-id="${card.id}" style="width: 140px; height: 196px;">
          <div class="card faction-${card.faction || 'neutral'}">
            <div class="card-art">${art}</div>
            <div class="card-info">
              <div class="card-name" style="font-size: 0.75rem;">${escapeHtml(card.name)}</div>
              ${card.text ? `<div class="card-text" style="font-size: 0.65rem;">${escapeHtml(card.text)}</div>` : ''}
              ${stats}
              ${costDisplay}
            </div>
          </div>
        </div>`;
      }).join('');
      
      $all('.gallery-card').forEach(cardEl => {
        cardEl.onclick = function() {
          const cardId = cardEl.dataset.cardId;
          const card = galleryCardDb[cardId];
          if (!card) return;
          const nameEl = $('card-info-name');
          const metaEl = $('card-info-meta');
          const textEl = $('card-info-text');
          const abilEl = $('card-info-abilities');
          const bioEl = $('card-info-bio');
          if (nameEl) nameEl.textContent = card.name;
          if (metaEl) {
            const parts = [];
            if (card.type === 'minion') {
              parts.push(`Стоимость: ${card.cost || 0}`);
              parts.push(`Атака: ${card.attack !== undefined ? card.attack : 0}`);
              parts.push(`Здоровье: ${card.health !== undefined ? card.health : 0}`);
            } else {
              parts.push(`Стоимость: ${card.cost || 0}`);
            }
            if (card.faction) {
              const factionNames = { rebellion: 'Сопротивление', empire: 'Империя', pirates: 'Пираты', mandalorians: 'Мандалорцы', neutral: 'Нейтральная' };
              parts.push(`Фракция: ${factionNames[card.faction] || card.faction}`);
            }
            if (card.legendary) parts.push('Легендарная');
            if (card.fictional) parts.push('⚡ ВЫМЫШЛЕННЫЙ ПЕРСОНАЖ');
            metaEl.textContent = parts.join(' | ');
          }
          if (textEl) textEl.textContent = card.text || '';
          if (abilEl) {
            let abil = '';
            if (card.battlecry) abil += `Battlecry: ${card.battlecry} `;
            if (card.deathrattle) abil += `Deathrattle: ${card.deathrattle} `;
            abilEl.textContent = abil || 'Нет способностей';
          }
          if (bioEl) bioEl.textContent = card.bio || 'Нет описания';
          $('card-info-overlay')?.classList.remove('hidden');
          $('cards-gallery-overlay')?.classList.add('hidden');
        };
      });
    }
    
    $('btn-settings')?.addEventListener('click', () => {
      $('settings-overlay')?.classList.remove('hidden');
    });
    $('btn-settings-lobby')?.addEventListener('click', () => {
      $('settings-overlay')?.classList.remove('hidden');
    });
    $('btn-settings-close')?.addEventListener('click', () => {
      $('settings-overlay')?.classList.add('hidden');
    });
    
    $('settings-overlay')?.addEventListener('click', (e) => {
      if (e.target.id === 'settings-overlay') {
        $('settings-overlay')?.classList.add('hidden');
      }
    });
    
    $('btn-forge-close')?.addEventListener('click', () => {
      $('forge-overlay')?.classList.add('hidden');
    });
    
    $('forge-overlay')?.addEventListener('click', (e) => {
      if (e.target.id === 'forge-overlay') {
        $('forge-overlay')?.classList.add('hidden');
      }
    });
    
    let forgeSelected = [];
    function renderForgeDeck(state) {
      const you = state.players[state.yourIndex];
      const deckList = $('forge-deck-list');
      const selectedEl = $('forge-selected');
      const craftBtn = $('btn-forge-craft');
      if (!deckList || !selectedEl) return;
      
      deckList.innerHTML = you.deck.map((cardId, idx) => {
        const meta = cardDb[cardId];
        if (!meta || meta.type !== 'minion') return '';
        const isSelected = forgeSelected.includes(cardId);
        return `<div class="card-wrap forge-deck-card ${isSelected ? 'selected' : ''}" data-card-id="${cardId}" data-deck-index="${idx}" style="width: 100px; height: 140px; cursor: pointer;">
          <div class="card faction-${meta.faction || 'neutral'}">
            <div class="card-art">${getCardArt(meta)}</div>
            <div class="card-info">
              <div class="card-name" style="font-size: 0.7rem;">${escapeHtml(meta.name)}</div>
              <div class="card-stats">
                <span class="atk">${meta.attack || 0}</span>
                <span class="hp">${meta.health || 0}</span>
              </div>
            </div>
          </div>
        </div>`;
      }).filter(Boolean).join('');
      
      $all('.forge-deck-card').forEach(card => {
        card.onclick = function() {
          const cardId = card.dataset.cardId;
          const idx = forgeSelected.indexOf(cardId);
          if (idx >= 0) {
            forgeSelected.splice(idx, 1);
          } else if (forgeSelected.length < 3) {
            forgeSelected.push(cardId);
          }
          renderForgeDeck(state);
        };
      });
      
      selectedEl.innerHTML = forgeSelected.length ? forgeSelected.map(cardId => {
        const meta = cardDb[cardId];
        return `<div class="card-wrap" style="width: 80px; height: 112px;"><div class="card faction-${meta.faction || 'neutral'}"><div class="card-art">${getCardArt(meta)}</div><div class="card-info"><div class="card-name" style="font-size: 0.6rem;">${escapeHtml(meta.name)}</div></div></div></div>`;
      }).join('') : '<div class="forge-selected-empty">Выберите 2-3 карты</div>';
      
      if (craftBtn) {
        craftBtn.disabled = forgeSelected.length < 2 || forgeSelected.length > 3 || (you.mana || 0) < 2;
        craftBtn.onclick = function() {
          if (craftBtn.disabled) return;
          socket.emit('forgeCard', { cardIds: forgeSelected });
          forgeSelected = [];
          $('forge-overlay')?.classList.add('hidden');
        };
      }
    }
    
    $('music-toggle')?.addEventListener('click', () => {
      if (window.Music) {
        window.Music.toggle();
      }
    });
    
    $('music-volume-slider')?.addEventListener('input', (e) => {
      const volume = parseFloat(e.target.value) / 100;
      if (window.Music) {
        window.Music.setVolume(volume);
      }
    });
  }

  function init() {
    initLobby();
    showScreen('lobby');
    socket = null;
  }

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