<script>
  import { API_URL, WS_URL } from '$lib/api.js';
  import { apiFetch } from '$lib/api.js';
  import { goto } from '$app/navigation';
  import { onMount, onDestroy } from 'svelte';
  import Card from '$lib/Card.svelte';
  import DeckTypeBadge from '$lib/DeckTypeBadge.svelte';

  const token = () => localStorage.getItem('token');

  let queueWs = null;
  let gameWs = null;
  let phase = $state('idle');
  let error = $state('');

  let decks = $state([]);
  let selectedDeckId = $state('');
  let selectedDeck = $derived(decks.find(d => d.id === selectedDeckId));

  let gameId = $state('');
  let gameState = $state(null);
  let myId = $state('');

  let viewingBoard = $state(false);
  let showDifficultyModal = $state(false);
  let selectedDifficulty = $state(5);

  const difficultyLabel = $derived(
    selectedDifficulty <= 2  ? 'Throws the game' :
    selectedDifficulty === 3 ? 'Fully random' :
    selectedDifficulty <= 5  ? 'Beginner' :
    selectedDifficulty <= 7  ? 'Intermediate' :
    selectedDifficulty <= 9  ? 'Advanced' :
                               'Expert'
  );

  let selectedHandIndex = $state(null);
  let combatAnimating = $state(false);
  let lunging = $state(new Set());
  let lungingDown = $state(new Set());
  let shaking = $state(new Set());

  let me = $derived(gameState?.you);
  let opp = $derived(gameState?.opponent);
  let isMyTurn = $derived(gameState?.active_player_id === myId);
  let gameOver = $derived(!!gameState?.result);
  let sacrificeMode = $state(false);

  let displayedDefense = $state({});
  let destroying = $state(new Set());
  let destroyed = $state(new Set());
  let displayedLife = $state({});

  const TURN_TIME_LIMIT = 120; // seconds
  const TIMER_WARNING = 30;    // show timer when this many seconds remain

  let turnStartedAt = $state(null);
  let secondsRemaining = $state(TURN_TIME_LIMIT);
  let timerInterval = null

  $effect(() => {
    if (!gameState?.turn_started_at) return;
    turnStartedAt = new Date(gameState.turn_started_at);
    clearInterval(timerInterval);
    timerInterval = setInterval(async () => {
      const elapsed = (Date.now() - turnStartedAt) / 1000;
      secondsRemaining = Math.max(0, TURN_TIME_LIMIT - elapsed);

      if (secondsRemaining <= 0 && !isMyTurn && gameState && !gameState.result) {
        clearInterval(timerInterval);
        await claimTimeoutWin();
      }
    }, 500);
  });

  onDestroy(() => {
    clearInterval(timerInterval);
  });

  async function claimTimeoutWin() {
    const res = await apiFetch(`${API_URL}/game/${gameId}/claim-timeout-win`, {
      method: 'POST'
    });
    if (!res.ok) {
      // Server rejected the claim. Game may have ended another way
      const err = await res.json();
      console.warn('Timeout claim rejected:', err.detail);
    }
  }

  $effect(() => {
    if (!gameState || combatAnimating) return;
    displayedLife = {
      [gameState.you.user_id]: Math.max(0, gameState.you.life),
      [gameState.opponent.user_id]: Math.max(0, gameState.opponent.life),
    };
  });

  // Keep displayedDefense in sync with board state when not animating
  $effect(() => {
    if (!gameState || combatAnimating) return;
    const all = [
      ...(gameState.you.board.filter(Boolean) || []),
      ...(gameState.opponent.board.filter(Boolean) || []),
    ];
    const next = {};
    for (const card of all) next[card.instance_id] = card.defense;
    displayedDefense = next;
  });

  // Reset sacrifice mode when turn changes
  $effect(() => {
    if (!isMyTurn) sacrificeMode = false;
  });

  onMount(async () => {
    if (!token()) { goto('/auth'); return; }
    const res = await apiFetch(`${API_URL}/decks`);
    decks = await res.json();
    if (decks.length > 0) selectedDeckId = decks[0].id;
  });

  onDestroy(() => {
    queueWs?.close();
    gameWs?.close();
  });

  function joinQueue() {
    if (!selectedDeckId || selectedDeck?.total_cost === 0 || selectedDeck?.total_cost > 50) return;
    error = '';
    phase = 'queuing';
    queueWs = new WebSocket(`${WS_URL}/ws/queue?deck_id=${selectedDeckId}`);
    queueWs.onopen = () => queueWs.send(token());
    queueWs.onmessage = (e) => {
      const msg = JSON.parse(e.data);
      if (msg.type === 'game_start') {
        gameId = msg.game_id;
        queueWs.close();
        connectToGame();
      } else if (msg.type === 'error') {
        error = msg.message;
        phase = 'idle';
      }
    };
    queueWs.onerror = () => { error = 'Connection failed'; phase = 'idle'; };
  }

  function connectToGame() {
    gameWs = new WebSocket(`${WS_URL}/ws/game/${gameId}`);
    gameWs.onopen = () => gameWs.send(token());
    gameWs.onmessage = async (e) => {
      const msg = JSON.parse(e.data);
      if (msg.type === 'state') {
        const newState = msg.state;
        if (
          gameState &&
          newState.last_combat_events?.length > 0 &&
          JSON.stringify(newState.last_combat_events) !== JSON.stringify(gameState.last_combat_events)
        ) {
          await animateCombat(newState);
        }
        destroyed = new Set();
        gameState = newState;
        if (!myId) myId = newState.you.user_id;
        phase = newState.result ? 'ended' : 'playing';
      } else if (msg.type === 'sacrifice_animation') {
        const id = msg.instance_id;
        destroying = new Set([...destroying, id]);
        await delay(600);
        destroying = new Set([...destroying].filter(i => i !== id));
        destroyed = new Set([...destroyed, id]);
      } else if (msg.type === 'error') {
        error = msg.message;
        setTimeout(() => error = '', 3000);
      }
    };
    gameWs.onerror = () => { error = 'Connection lost'; };
  }

  async function animateCombat(newState) {
    combatAnimating = true;

    // The attacker is whoever was active when end_turn was called.
    // After end_turn resolves, active_player_id switches. So we look
    // at who is NOT the current active player to find the attacker,
    // unless the game just ended (result is set), in which case
    // active_player_id hasn't switched yet.
    const attackerId = newState.result
      ? newState.active_player_id
      : newState.player_order.find(id => id !== newState.active_player_id);

    const attackerIsMe = attackerId === myId;

    for (const event of newState.last_combat_events) {
      await delay(100);

      const attackerBoard = attackerIsMe ? gameState.you.board : gameState.opponent.board;
      const defenderBoard = attackerIsMe ? gameState.opponent.board : gameState.you.board;

      const attacker = attackerBoard[event.attacker_slot];
      const defender = event.defender_slot !== null ? defenderBoard[event.defender_slot] : null;

      if (attacker) {
        if (attackerIsMe) {
          lunging = new Set([...lunging, attacker.instance_id]);
        } else {
          lungingDown = new Set([...lungingDown, attacker.instance_id]);
        }
        if (defender) shaking = new Set([...shaking, defender.instance_id]);
        await delay(220);
        if (defender) {
          const newDefense = Math.max(0, (displayedDefense[defender.instance_id] ?? defender.defense) - attacker.attack);
          displayedDefense = { ...displayedDefense, [defender.instance_id]: newDefense };
        } else {
          // Direct life damage
          const defenderId = attackerIsMe ? gameState.opponent.user_id : gameState.you.user_id;
          const newLife = Math.max(0, (displayedLife[defenderId] ?? 0) - attacker.attack);
          displayedLife = { ...displayedLife, [defenderId]: newLife };
        }
        await delay(200);
        lunging = new Set([...lunging].filter(id => id !== attacker.instance_id));
        lungingDown = new Set([...lungingDown].filter(id => id !== attacker.instance_id));
        if (defender) shaking = new Set([...shaking].filter(id => id !== defender.instance_id));
        if (defender && (displayedDefense[defender.instance_id] ?? defender.defense) <= 0) {
          destroying = new Set([...destroying, defender.instance_id]);
          await delay(600);
          destroying = new Set([...destroying].filter(id => id !== defender.instance_id));
          destroyed = new Set([...destroyed, defender.instance_id]);
        }
        await delay(80);
      }
    }

    combatAnimating = false;
  }

  function delay(ms) { return new Promise(r => setTimeout(r, ms)); }

  function send(msg) { gameWs?.send(JSON.stringify(msg)); }

  function selectHandCard(index) {
    if (!isMyTurn || combatAnimating) return;
    selectedHandIndex = selectedHandIndex === index ? null : index;
  }

  function clickSlot(slot) {
    if (!isMyTurn || combatAnimating || selectedHandIndex === null) return;
    send({ type: 'play_card', hand_index: selectedHandIndex, slot });
    selectedHandIndex = null;
  }

  async function sacrifice(slot) {
    if (!isMyTurn || combatAnimating) return;
    const card = me.board[slot];
    if (!card) return;
    destroying = new Set([...destroying, card.instance_id]);
    await delay(600);
    destroying = new Set([...destroying].filter(id => id !== card.instance_id));
    destroyed = new Set([...destroyed, card.instance_id]);
    send({ type: 'sacrifice', slot });
    sacrificeMode = false;
  }

  function endTurn() {
    if (!isMyTurn || combatAnimating) return;
    selectedHandIndex = null;
    send({ type: 'end_turn' });
  }

  function handleHandCardMouseMove(e, node) {
    const rect = node.getBoundingClientRect();
    const cy = rect.top + rect.height / 2;
    const dy = (e.clientY - cy) / (rect.height / 2);
    const ty = -dy * 80;
    node.style.setProperty('--peek-y', `${ty}px`);
  }

  function handleHandCardMouseLeave(node) {
    node.style.setProperty('--peek-y', '0px');
  }

  async function joinSolo() {
    if (!selectedDeckId || selectedDeck?.total_cost === 0 || selectedDeck?.total_cost > 50) return;
    showDifficultyModal = false;
    error = '';
    phase = 'queuing';
    const res = await apiFetch(`${API_URL}/game/solo?deck_id=${selectedDeckId}&difficulty=${selectedDifficulty}`, {
      method: 'POST'
    });
    if (!res.ok) {
      const err = await res.json();
      error = err.detail;
      phase = 'idle';
      return;
    }
    const data = await res.json();
    gameId = data.game_id;
    connectToGame();
  }
</script>

<main>

  {#if phase === 'idle'}
    <div class="lobby">
      <h1 class="lobby-title">Find a Match</h1>
      <a href="/how-to-play" class="how-to-play-link">How to Play</a>
      {#if decks.length === 0}
        <p class="lobby-hint">You need a deck to play. <a href="/decks">Build one first.</a></p>
      {:else}
        <div class="deck-select">
          <label class="deck-label" for="deck">Choose your deck</label>
          <select id="deck" bind:value={selectedDeckId}>
            {#each decks as deck}
              <option value={deck.id}>{deck.name} ({deck.card_count} cards, cost {deck.total_cost})</option>
            {/each}
          </select>
        </div>
        <p class="error">{selectedDeck && (selectedDeck.total_cost === 0 || selectedDeck.total_cost > 50) ? `Deck cost must be between 1 and 50 (current: ${selectedDeck.total_cost})` : ''}</p>
        <div class="lobby-buttons">
          <button class="play-btn" onclick={joinQueue} disabled={!selectedDeckId || selectedDeck?.total_cost === 0 || selectedDeck?.total_cost > 50}>
            Find Opponent
          </button>
          <button class="play-btn solo-btn" onclick={() => showDifficultyModal = true} disabled={!selectedDeckId || selectedDeck?.total_cost === 0 || selectedDeck?.total_cost > 50}>
            vs. Computer
          </button>
        </div>
      {/if}
    </div>

  {:else if phase === 'queuing'}
    <div class="lobby">
      <div class="spinner"></div>
      <p class="lobby-hint">Waiting for an opponent...</p>
      <button class="cancel-btn" onclick={() => { queueWs?.close(); phase = 'idle'; }}>Cancel</button>
    </div>

  {:else if (phase === 'playing' || (phase === 'ended' && viewingBoard)) && gameState}
    <div class="game">

      <div class="sidebar left-sidebar">
        <div class="sidebar-section top-section">
          <div class="sidebar-name opp-name">{opp.username}</div>
          <DeckTypeBadge deckType={opp.deck_type} />
          <div class="sidebar-life">♥ {displayedLife[opp.user_id] ?? opp.life}</div>
          <div class="sidebar-deck">Deck: {opp.deck_size}</div>
          <div class="sidebar-hand">Hand: {opp.hand_size}</div>
        </div>
        <div class="sidebar-section bottom-section">
          <div class="sidebar-name you-name">You</div>
          <div class="sidebar-life">♥ {displayedLife[me.user_id] ?? me.life}</div>
          <div class="sidebar-energy">
            <div class="cost-bubble-display">✦</div>
            <span class="energy-count">{me.energy}/{me.energy_cap}</span>
            {#if isMyTurn && !combatAnimating}
              <button
                class="sacrifice-mode-btn"
                class:active={sacrificeMode}
                onclick={() => sacrificeMode = !sacrificeMode}
                title="Sacrifice a card"
              >🗡</button>
            {/if}
          </div>
          <div class="sidebar-deck">Deck: {me.deck_size}</div>
        </div>
      </div>

      <div class="field">
        <div class="board opponent-board">
          {#each opp.board as card, slot}
            <div class="slot">
              {#if card && !destroyed.has(card.instance_id)}
                <div class="board-card-wrap"
                  class:lunging={lunging.has(card.instance_id)}
                  class:lunge-down={lungingDown.has(card.instance_id)}
                  class:shaking={shaking.has(card.instance_id)}
                  class:destroying={destroying.has(card.instance_id)}
                >
                  <div class="board-card">
                    <Card {card} noHover={true} defenseOverride={displayedDefense[card.instance_id] ?? card.defense} />
                  </div>
                </div>
              {:else}
                <div class="empty-slot"></div>
              {/if}
            </div>
          {/each}
        </div>

        <div class="divider">
          <span class="turn-indicator" class:my-turn={isMyTurn}>
            {phase === 'ended' ? 'Game Ended' : isMyTurn ? 'Your turn' : `${opp.username}'s turn`}
          </span>
          {#if secondsRemaining <= TIMER_WARNING}
            <span class="turn-timer" class:urgent={secondsRemaining <= 10}>
              {Math.ceil(secondsRemaining)}s
            </span>
          {/if}
        </div>

        <div class="board my-board">
          {#each me.board as card, slot}
            <div
              class="slot"
              class:targetable={selectedHandIndex !== null && card === null}
              onclick={() => {
                if (sacrificeMode && card) { sacrifice(slot); }
                else if (card === null) { clickSlot(slot); }
              }}
            >
              {#if card && !destroyed.has(card.instance_id)}
                <div class="board-card-wrap"
                  class:lunging={lunging.has(card.instance_id)}
                  class:lunge-down={lungingDown.has(card.instance_id)}
                  class:shaking={shaking.has(card.instance_id)}
                  class:destroying={destroying.has(card.instance_id)}
                >
                  <div class="board-card">
                    <Card {card} noHover={true} defenseOverride={displayedDefense[card.instance_id] ?? card.defense} />
                  </div>
                  {#if sacrificeMode}
                    <div class="sacrifice-overlay">🗡</div>
                  {/if}
                </div>
              {:else}
                <div class="empty-slot" class:highlight={selectedHandIndex !== null}>
                  {#if selectedHandIndex !== null}
                    <span class="slot-hint">Play here</span>
                  {/if}
                </div>
              {/if}
            </div>
          {/each}
        </div>
      </div>

      <div class="sidebar right-sidebar">
        {#if phase === 'ended'}
          <button class="end-turn-btn" onclick={() => viewingBoard = false}>Go Back</button>
        {:else if isMyTurn && !combatAnimating}
          <button class="end-turn-btn" onclick={endTurn}>End Turn</button>
        {/if}
      </div>

    </div>

    <div class="hand" class:inactive={!isMyTurn}>
      {#each me.hand as card, i}
        <button
          class="hand-card"
          class:selected={selectedHandIndex === i}
          class:unaffordable={card.cost > me.energy || !isMyTurn}
          onclick={() => selectHandCard(i)}
          disabled={!isMyTurn || card.cost > me.energy}
          onmousemove={(e) => handleHandCardMouseMove(e, e.currentTarget)}
          onmouseleave={(e) => handleHandCardMouseLeave(e.currentTarget)}
        >
          <Card {card} noHover={true} />
        </button>
      {/each}
    </div>

    {#if error}
      <div class="toast">{error}</div>
    {/if}

  {:else if phase === 'ended' && gameState?.result}
    <div class="lobby">
      <h1 class="lobby-title" class:win={gameState.result.winner_id === myId} class:lose={gameState.result.winner_id !== myId}>
        {gameState.result.winner_id === myId ? 'Victory' : 'Defeat'}
      </h1>
      <p class="lobby-hint">{gameState.result.reason}</p>
      <div class="lobby-buttons">
        <button class="play-btn" onclick={() => viewingBoard = true}>View Board</button>
        <button class="play-btn" onclick={() => { gameState = null; phase = 'idle'; }}>Go Back</button>
      </div>
    </div>
  {/if}

  {#if showDifficultyModal}
    <div class="modal-backdrop" onclick={() => showDifficultyModal = false}>
      <div class="modal" onclick={(e) => e.stopPropagation()}>
        <h2 class="modal-title">Choose Difficulty</h2>
        <div class="difficulty-display">
          <span class="difficulty-number">{selectedDifficulty}</span>
          <span class="difficulty-label">{difficultyLabel}</span>
        </div>
        <input
          class="difficulty-slider"
          type="range"
          min="1"
          max="10"
          bind:value={selectedDifficulty}
        />
        <div class="difficulty-ticks">
          <span>1</span><span>10</span>
        </div>
        <div class="modal-buttons">
          <button class="cancel-btn" onclick={() => showDifficultyModal = false}>Cancel</button>
          <button class="play-btn" onclick={joinSolo}>Start Game</button>
        </div>
      </div>
    </div>
  {/if}

</main>

<style>
  @import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700;900&family=Crimson+Text:ital,wght@0,400;0,600;1,400&display=swap');

  main {
    height: 100vh;
    overflow: hidden;
    background: #0d0a04;
    display: flex;
    flex-direction: column;
  }

  /* ── Lobby ── */
  .lobby {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.5rem;
    padding: 2rem;
  }

  .lobby-title {
    font-family: 'Cinzel', serif;
    font-size: 32px;
    font-weight: 900;
    color: #f0d080;
    margin: 0;
    letter-spacing: 0.1em;
  }

  .lobby-title.win  { color: #6aaa6a; }
  .lobby-title.lose { color: #c85050; }

  .how-to-play-link {
    font-family: 'Crimson Text', serif;
    font-size: 14px;
    font-style: italic;
    color: rgba(240, 180, 80, 0.4);
    text-decoration: underline;
    transition: color 0.15s;
    margin-top: -1rem;
    padding-bottom: 20px;
  }
  .how-to-play-link:hover { color: rgba(240, 180, 80, 0.7); }

  .lobby-hint {
    font-family: 'Crimson Text', serif;
    font-size: 16px;
    font-style: italic;
    color: rgba(240, 180, 80, 0.6);
    margin: 0;
    text-align: center;
  }

  .lobby-hint a { color: #f0d080; }

  .final-life {
    font-family: 'Cinzel', serif;
    font-size: 13px;
    color: rgba(240, 180, 80, 0.5);
  }

  .deck-label {
    font-family: 'Cinzel', serif;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: rgba(240, 180, 80, 0.5);
    display: block;
    margin-bottom: 0.5rem;
  }

  .deck-select { display: flex; flex-direction: column; }

  select {
    font-family: 'Crimson Text', serif;
    font-size: 15px;
    color: #f0d080;
    background: #1a1008;
    border: 1.5px solid #6b4c1e;
    border-radius: 6px;
    padding: 8px 12px;
    outline: none;
    cursor: pointer;
    min-width: 240px;
  }

  .play-btn, .cancel-btn {
    font-family: 'Cinzel', serif;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 10px 32px;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.15s;
  }

  .play-btn {
    background: #c8861a;
    color: #fff8e0;
    border: none;
  }

  .play-btn:hover:not(:disabled) { background: #e09820; }

  .play-btn:disabled {
    background: #6b4c1e;
    color: rgba(255, 248, 224, 0.4);
    cursor: not-allowed;
  }

  .lobby-buttons {
    display: flex;
    gap: 1rem;
  }

  .solo-btn {
    background: #2a3d20;
    border: 1px solid #5a8a40;
    color: #a8d880;
  }

  .solo-btn:hover:not(:disabled) {
    background: #3a5a2a;
  }

  .solo-btn:disabled {
    background: #1a2510;
    color: rgba(168, 216, 128, 0.3);
    cursor: not-allowed;
    border-color: #3a5a2a;
  }

  .cancel-btn {
    background: none;
    color: rgba(240, 180, 80, 0.6);
    border: 1px solid rgba(107, 76, 30, 0.4);
  }

  .cancel-btn:hover { border-color: #c8861a; color: #f0d080; }

  .error {
    font-family: 'Crimson Text', serif;
    font-size: 14px;
    color: #c85050;
    margin: 0;
    height: 1.4em;
    margin-top: -1rem;
    margin-bottom: -1rem;
  }

  .spinner {
    width: 40px;
    height: 40px;
    border: 3px solid rgba(200, 134, 26, 0.2);
    border-top-color: #c8861a;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
  }

  @keyframes spin { to { transform: rotate(360deg); } }

  /* ── Game layout ── */
  .game {
    flex: 1;
    display: flex;
    flex-direction: row;
    min-height: 0;
    gap: 0.5rem;
    padding: 0.5rem 0.5rem 0 0.5rem;
  }

  /* ── Sidebars ── */
  .left-sidebar {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    width: 200px;
    flex-shrink: 0;
    padding: 0.5rem;
  }

  .right-sidebar {
    width: 100px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding: 0.75rem 0.5rem;
  }

  .sidebar-section {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    align-items: flex-start;
  }

  .sidebar-section :global(.type-badge) {
    align-self: flex-start;
  }

  .bottom-section {
    padding-bottom: 0.5rem;
  }

  .sidebar-name {
    font-family: 'Cinzel', serif;
    font-size: 14px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .opp-name { color: rgba(200, 80, 80, 0.8); }
  .you-name { color: #c8861a; }

  .sidebar-life {
    font-family: 'Cinzel', serif;
    font-size: 30px;
    font-weight: 700;
    color: #f0d080;
  }

  .sidebar-deck, .sidebar-hand {
    font-family: 'Crimson Text', serif;
    font-size: 16px;
    font-style: italic;
    color: rgba(240, 180, 80, 0.45);
  }

  .sidebar-energy {
    display: flex;
    align-items: center;
    gap: 4px;
  }

  .cost-bubble-display {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: #6ea0ec;
    border: 2.5px solid #000;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #08152c;
    font-size: 13px;
    font-weight: 700;
    font-family: 'Cinzel', serif;
    flex-shrink: 0;
  }

  .energy-count {
    font-family: 'Cinzel', serif;
    font-size: 18px;
    font-weight: 700;
    color: #f0d080;
  }

  .sacrifice-mode-btn {
    background: none;
    color: rgba(107, 76, 30, 1);
    border: 2px solid rgba(107, 76, 30, 1);
    border-radius: 15px;
    font-size: 16px;
    cursor: pointer;
    padding: 6px 5.5px;
    line-height: 1;
    transition: background 0.15s, border-color 0.15s;
    margin-left: 4px;
  }

  .sacrifice-mode-btn:hover {
    border-color: #c8861a;
    background: rgba(200, 134, 26, 0.1);
  }

  .sacrifice-mode-btn.active {
    background: rgba(180, 40, 40, 0.3);
    border-color: #c84040;
  }

  .end-turn-btn {
    font-family: 'Cinzel', serif;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    background: #c8861a;
    border: none;
    border-radius: 4px;
    color: #fff8e0;
    padding: 10px 8px;
    cursor: pointer;
    transition: background 0.15s;
    margin-top: auto;
  }

  .end-turn-btn:hover { background: #e09820; }

  .sacrifice-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 48px;
    background: rgba(180, 40, 40, 0.35);
    border-radius: 10px;
    cursor: pointer;
    z-index: 10;
    transition: background 0.15s;
  }

  .sacrifice-overlay:hover {
    background: rgba(220, 60, 60, 0.5);
  }

  /* ── Field ── */
  .field {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
    gap: 0.25rem;
    min-height: 0;
  }

  .board {
    flex: 1;
    display: flex;
    gap: 10px;
    align-items: center;
    justify-content: center;
    min-height: 0;
  }

  .slot {
    /* card aspect ratio 300:460 */
    flex: 0 0 calc(400px * 0.55 * 300 / 400);
    height: 100%;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  /* ── Board cards ── */
  .board-card-wrap {
    position: relative;
    width: calc(300px * 0.55);
    height: calc(400px * 0.55);
    flex-shrink: 0;
    transition: transform 0.15s, filter 0.15s;
  }

  .my-board .board-card-wrap:hover {
    transform: scale(1.5);
    z-index: 25;
  }

  .opponent-board .board-card-wrap:hover {
    transform: scale(1.5) translateY(20px);
    z-index: 25;
  }

  .board-card-wrap.destroying {
    animation: crumble 0.6s ease-in forwards;
    z-index: 20;
    pointer-events: none;
  }

  @keyframes crumble {
    0%   {
      transform: scale(1) rotate(0deg);
      opacity: 1;
      filter: brightness(1);
    }
    15%  {
      transform: scale(1.08) rotate(-3deg);
      filter: brightness(2) saturate(0);
    }
    30%  {
      transform: scale(0.95) rotate(5deg);
      filter: brightness(1.5) saturate(0);
    }
    50%  {
      transform: scale(0.85) rotate(-4deg) translateY(8px);
      opacity: 0.7;
      filter: brightness(0.8) saturate(0);
    }
    75%  {
      transform: scale(0.6) rotate(8deg) translateY(20px);
      opacity: 0.3;
      filter: brightness(0.4) saturate(0);
    }
    100% {
      transform: scale(0.2) rotate(15deg) translateY(40px);
      opacity: 0;
      filter: brightness(0) saturate(0);
    }
  }

  .board-card {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .board-card :global(.card) {
    position: absolute !important;
    top: 0 !important;
    left: 0 !important;
    width: 300px !important;
    transform: scale(0.55) !important;
    transform-origin: top left !important;
  }

  .empty-slot {
    width: calc(300px * 0.55);
    height: calc(400px * 0.55);
    border: 1.5px dashed rgba(107, 76, 30, 0.25);
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: border-color 0.15s, background 0.15s;
    z-index: 1;
  }

  .empty-slot.highlight {
    border-color: #c8861a;
    background: rgba(200, 134, 26, 0.08);
    cursor: pointer;
  }

  .slot-hint {
    font-family: 'Cinzel', serif;
    font-size: 9px;
    color: rgba(200, 134, 26, 0.7);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    pointer-events: none;
  }

  /* ── Combat animations ── */
  .board-card-wrap.lunging {
    animation: lunge-up 0.4s cubic-bezier(0.2, 0, 0.8, 1) forwards;
    z-index: 20;
  }

  .board-card-wrap.lunge-down {
    animation: lunge-down 0.4s cubic-bezier(0.2, 0, 0.8, 1) forwards;
    z-index: 20;
  }

  @keyframes lunge-up {
    0%   { transform: translateY(0); }
    45%  { transform: translateY(-70px); }
    100% { transform: translateY(0); }
  }

  @keyframes lunge-down {
    0%   { transform: translateY(0); }
    45%  { transform: translateY(70px); }
    100% { transform: translateY(0); }
  }

  .board-card-wrap.shaking {
    animation: shake 0.4s ease forwards;
  }

  @keyframes shake {
    0%   { transform: translateX(0); }
    20%  { transform: translateX(-10px); }
    40%  { transform: translateX(10px); }
    60%  { transform: translateX(-7px); }
    80%  { transform: translateX(7px); }
    100% { transform: translateX(0); }
  }

  /* ── Divider ── */
  .divider {
    height: 24px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    border-top: 1px solid rgba(107, 76, 30, 0.2);
    border-bottom: 1px solid rgba(107, 76, 30, 0.2);
  }

  .turn-timer {
    font-family: 'Cinzel', serif;
    font-size: 11px;
    font-weight: 700;
    color: rgba(240, 180, 80, 0.6);
    transition: color 0.3s;
  }

  .turn-timer.urgent {
    color: #c85050;
    animation: pulse 0.8s ease-in-out infinite;
  }

  @keyframes pulse {
    0%, 100% { opacity: 1; }
    50%       { opacity: 0.4; }
  }

  .turn-indicator {
    font-family: 'Cinzel', serif;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: rgba(240, 180, 80, 0.3);
    transition: color 0.3s;
  }

  .turn-indicator.my-turn { color: #c8861a; }

  /* ── Hand ── */
  .hand {
    flex-shrink: 0;
    display: flex;
    flex-direction: row;
    gap: 8px;
    justify-content: center;
    padding: 0.5rem;
    overflow-x: auto;
    overflow-y: hidden;
    border-top: 1px solid rgba(107, 76, 30, 0.3);
    background: rgba(0,0,0,0.3);
    height: calc(400px * 0.9 + 1rem);
  }

  .hand.inactive { opacity: 0.6; }

  .hand-card {
    all: unset;
    cursor: pointer;
    flex-shrink: 0;
    /* wrapper is the scaled size so layout is correct */
    width: calc(300px * 0.65);
    height: calc(400px * 0.65);
    position: relative;
    transition: transform 0.15s, filter 0.15s;
    --peek-x: 0px;
    --peek-y: 0px;
  }

  .hand-card :global(.card) {
    position: absolute !important;
    top: 20px !important;
    left: 0 !important;
    width: 300px !important;
    transform: scale(0.65) !important;
    transform-origin: top left !important;
    transition: transform 0.15s ease !important;
  }

  .hand-card:hover:not(:disabled) :global(.card) {
    transform: scale(1.1) translate(-50px, calc(var(--peek-y) - 80px)) !important;
    transform-origin: top left !important;
    z-index: 50 !important;
  }

  .hand-card.selected {
    /* transform: translateY(-16px); */
    filter: drop-shadow(0 0 8px rgba(200, 134, 26, 0.9));
    z-index: 25 !important;
  }

  .hand-card.selected :global(.card) {
    /* transform: scale(1.) translate(-30px, calc(var(--peek-y) - 80px)) !important; */
    /* transform-origin: top left !important; */
    z-index: 50 !important;
    filter: drop-shadow(0 0 8px rgba(200, 134, 26, 0.9));
  }

  .hand-card.unaffordable { filter: grayscale(0.7) brightness(0.55); }
  .hand-card:disabled { cursor: default; }

  /* ── Difficulty modal ── */
  .modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 200;
  }

  .modal {
    background: #110d04;
    border: 1.5px solid #6b4c1e;
    border-radius: 10px;
    padding: 2rem 2.5rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.25rem;
    min-width: 300px;
  }

  .modal-title {
    font-family: 'Cinzel', serif;
    font-size: 20px;
    font-weight: 700;
    color: #f0d080;
    margin: 0;
    letter-spacing: 0.08em;
  }

  .difficulty-display {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
  }

  .difficulty-number {
    font-family: 'Cinzel', serif;
    font-size: 48px;
    font-weight: 900;
    color: #f0d080;
    line-height: 1;
  }

  .difficulty-label {
    font-family: 'Crimson Text', serif;
    font-size: 16px;
    font-style: italic;
    color: rgba(240, 180, 80, 0.6);
  }

  .difficulty-slider {
    width: 100%;
    accent-color: #c8861a;
    cursor: pointer;
  }

  .difficulty-ticks {
    width: 100%;
    display: flex;
    justify-content: space-between;
    font-family: 'Cinzel', serif;
    font-size: 10px;
    color: rgba(240, 180, 80, 0.4);
    margin-top: -0.75rem;
  }

  .modal-buttons {
    display: flex;
    gap: 0.75rem;
    margin-top: 0.25rem;
  }

  /* ── Toast ── */
  .toast {
    position: fixed;
    bottom: 220px;
    left: 50%;
    transform: translateX(-50%);
    background: rgba(180, 40, 40, 0.9);
    color: #fff;
    font-family: 'Cinzel', serif;
    font-size: 12px;
    padding: 8px 20px;
    border-radius: 6px;
    pointer-events: none;
    z-index: 100;
  }
</style>