<script setup lang="ts">
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
import { createFightScene, type FightSceneController } from '../game/FightScene'
import {
  fanfareRound, fanfareFight, announce, announceDeep, announceFast,
  announceDeepIntro, announceRandomHype, announceRoundHype,
  announceFinishHim, announceFatality, announceFlawlessVictory,
  sfxCrowdCheer, sfxCrowdGasp, sfxCrowdOoh, sfxApplause, sfxDrumRoll,
  setMusicIntensity,
} from '../game/sounds'

interface Round {
  roundNumber: number
  challengeType: string
  challengeData: string
  botAResponse: string | null
  botATimeMs: number | null
  botAScore: number | null
  botBResponse: string | null
  botBTimeMs: number | null
  botBScore: number | null
  winnerId: string | null
  narration: string | null
}

interface FightData {
  id: string
  botA: { id: string; name: string; avatarSeed: string; archetype?: string; profilePicUrl?: string | null; eloRating: number; wins: number; losses: number; tier: number } | null
  botB: { id: string; name: string; avatarSeed: string; archetype?: string; profilePicUrl?: string | null; eloRating: number; wins: number; losses: number; tier: number } | null
  arenaInfo: { id: string; name: string; description: string; modifier: string | null } | null
  arena: string
  winnerId: string | null
  botAHp: number
  botBHp: number
  totalRounds: number
  status: string
  rounds: Round[]
}

const props = defineProps<{ fight: FightData; autoplay?: boolean }>()

const canvasRef = ref<HTMLCanvasElement>()
const logEl = ref<HTMLElement>()
let scene: FightSceneController | null = null

const isReplaying = ref(false)
const displayHpA = ref(100)
const displayHpB = ref(100)
const currentRound = ref(0)
const showingFinal = ref(true)

// Floating overlay announcements (replaces in-canvas text)
const announcement = ref('')
const announcementColor = ref('#ffffff')
const announcementVisible = ref(false)
const hitText = ref('')
const hitTextVisible = ref(false)
const hitTextColor = ref('#ff2d2d')
const hitTextX = ref(50)
const hitTextY = ref(30)
const glitching = ref(false)

// Staggered log
const logItems = ref<{ type: string; round: number; text: string; color: string }[]>([])

onMounted(async () => {
  if (props.autoplay) {
    // Fresh fight — start replay immediately instead of showing static result
    await initScene()
    await nextTick()
    replay()
  } else {
    // Map server HP (0-200) to display (0-100), loser always 0
    displayHpA.value = mapHp(props.fight.botAHp, props.fight.winnerId, props.fight.botA?.id)
    displayHpB.value = mapHp(props.fight.botBHp, props.fight.winnerId, props.fight.botB?.id)
    for (const r of props.fight.rounds) addRoundToLog(r, false)
    await initScene()
  }
})

function mapHp(hp: number, winnerId: string | null, botId: string | undefined): number {
  // If there's a winner and this bot lost, show 0
  if (winnerId && botId && winnerId !== botId) return 0
  return Math.round((hp / 200) * 100)
}

onUnmounted(() => { scene?.destroy(); scene = null })

async function initScene() {
  if (!canvasRef.value || !props.fight.botA || !props.fight.botB) return
  if (scene) { scene.k.go('fight'); return }

  const container = canvasRef.value.parentElement
  if (container) {
    canvasRef.value.width = container.clientWidth
    canvasRef.value.height = container.clientHeight
  }

  scene = await createFightScene({
    canvas: canvasRef.value,
    botA: { name: props.fight.botA.name, seed: props.fight.botA.avatarSeed || props.fight.botA.name, tier: props.fight.botA.tier, archetype: props.fight.botA.archetype },
    botB: { name: props.fight.botB.name, seed: props.fight.botB.avatarSeed || props.fight.botB.name, tier: props.fight.botB.tier, archetype: props.fight.botB.archetype },
    arena: props.fight.arena,
  })
}

const challengeLabel = (type: string) => {
  const labels: Record<string, string> = {
    speed_blitz: 'SPEED BLITZ', riddle: 'RIDDLE ME THIS', code_golf: 'CODE GOLF',
    roast_battle: 'ROAST BATTLE', hallucination_check: 'HALLUCINATION CHECK',
    token_economy: 'TOKEN ECONOMY', creative_writing: 'CREATIVE WRITING',
    math_blitz: 'MATH BLITZ', trap_card: 'TRAP CARD',
    food_fight: 'FOOD FIGHT', wrestling_match: 'WRESTLING MATCH',
    music_battle: 'MUSIC BATTLE', magic_duel: 'MAGIC DUEL',
    sports_showdown: 'SPORTS SHOWDOWN', nature_clash: 'NATURE CLASH',
    space_war: 'SPACE WAR', hack_battle: 'HACK BATTLE',
    meme_war: 'MEME WAR', animal_kingdom: 'ANIMAL KINGDOM',
    demolition: 'DEMOLITION DERBY', vehicle_mayhem: 'VEHICLE MAYHEM',
    medieval_combat: 'MEDIEVAL COMBAT',
  }
  return labels[type] || type.replace(/_/g, ' ').toUpperCase()
}

const tierClass = (t: number) => `tier-${t}`
function sleep(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)) }
function scrollLog() { nextTick(() => { logEl.value?.scrollTo({ top: logEl.value.scrollHeight, behavior: 'smooth' }) }) }

// Show floating announcement over the canvas
async function showOverlay(text: string, color: string, duration: number) {
  announcement.value = text
  announcementColor.value = color
  announcementVisible.value = true
  await sleep(duration)
  announcementVisible.value = false
  await sleep(60)
}

// Show hit text that flies around
async function showHitText(text: string, color: string, x: number) {
  hitText.value = text
  hitTextColor.value = color
  hitTextX.value = x
  hitTextY.value = 35 + Math.random() * 20
  hitTextVisible.value = true
  glitching.value = true
  await sleep(100)
  glitching.value = false
  await sleep(700)
  hitTextVisible.value = false
}

function addRoundToLog(round: Round, stagger: boolean): Promise<void> {
  if (!stagger) {
    const challenge = JSON.parse(round.challengeData)
    logItems.value.push(
      { type: 'header', round: round.roundNumber, text: `ROUND ${round.roundNumber}: ${challengeLabel(round.challengeType)}`, color: 'neon-purple' },
      { type: 'prompt', round: round.roundNumber, text: challenge.prompt, color: 'text-muted' },
      { type: 'responseA', round: round.roundNumber, text: `${props.fight.botA?.name}: ${round.botAResponse?.slice(0, 120) || '[NO RESPONSE]'} (${round.botATimeMs}ms)`, color: 'neon-cyan' },
      { type: 'responseB', round: round.roundNumber, text: `${props.fight.botB?.name}: ${round.botBResponse?.slice(0, 120) || '[NO RESPONSE]'} (${round.botBTimeMs}ms)`, color: 'neon-pink' },
    )
    if (round.narration) logItems.value.push({ type: 'narration', round: round.roundNumber, text: `>> ${round.narration}`, color: 'neon-yellow' })
    const winner = round.winnerId === props.fight.botA?.id ? props.fight.botA?.name : round.winnerId === props.fight.botB?.id ? props.fight.botB?.name : 'DRAW'
    logItems.value.push({ type: 'result', round: round.roundNumber, text: `${winner} wins round! (${round.botAScore} vs ${round.botBScore})`, color: 'text-secondary' })
    return Promise.resolve()
  }

  return (async () => {
    const challenge = JSON.parse(round.challengeData)
    logItems.value.push({ type: 'header', round: round.roundNumber, text: `ROUND ${round.roundNumber}: ${challengeLabel(round.challengeType)}`, color: 'neon-purple' })
    scrollLog(); await sleep(150)
    logItems.value.push({ type: 'prompt', round: round.roundNumber, text: challenge.prompt, color: 'text-muted' })
    scrollLog(); await sleep(200)
    logItems.value.push({ type: 'responseA', round: round.roundNumber, text: `${props.fight.botA?.name}: ${round.botAResponse?.slice(0, 120) || '[NO RESPONSE]'}`, color: 'neon-cyan' })
    scrollLog(); await sleep(150)
    logItems.value.push({ type: 'time', round: round.roundNumber, text: `  ${round.botATimeMs}ms | Score: ${round.botAScore}`, color: 'text-muted' })
    scrollLog(); await sleep(150)
    logItems.value.push({ type: 'responseB', round: round.roundNumber, text: `${props.fight.botB?.name}: ${round.botBResponse?.slice(0, 120) || '[NO RESPONSE]'}`, color: 'neon-pink' })
    scrollLog(); await sleep(150)
    logItems.value.push({ type: 'time', round: round.roundNumber, text: `  ${round.botBTimeMs}ms | Score: ${round.botBScore}`, color: 'text-muted' })
    scrollLog()
  })()
}

async function replay() {
  if (isReplaying.value || !props.fight.botA || !props.fight.botB) return
  isReplaying.value = true
  showingFinal.value = false
  displayHpA.value = 100
  displayHpB.value = 100
  logItems.value = []
  currentRound.value = 0

  await initScene()
  scene?.startMusic()
  await sleep(300)

  // Deep movie trailer intro
  announceDeepIntro()
  await showOverlay(props.fight.arenaInfo?.name || 'THE RING', '#b83dff', 900)
  logItems.value.push(
    { type: 'system', round: 0, text: `ARENA: ${props.fight.arenaInfo?.name || 'THE RING'}`, color: 'neon-purple' },
    { type: 'system', round: 0, text: `${props.fight.botA.name} (${Math.round(props.fight.botA.eloRating)}) vs ${props.fight.botB.name} (${Math.round(props.fight.botB.eloRating)})`, color: 'text-secondary' },
    { type: 'divider', round: 0, text: '', color: '' },
  )
  scrollLog()
  await sleep(200)

  // Entrance animations
  if (scene) {
    await scene.playEntrance()
    await sleep(300)
  }

  for (const round of props.fight.rounds) {
    currentRound.value = round.roundNumber

    fanfareRound(round.roundNumber)
    await showOverlay(`ROUND ${round.roundNumber}`, '#00f0ff', 700)
    await sleep(80)
    await showOverlay(challengeLabel(round.challengeType), '#b83dff', 600)
    await sleep(80)
    fanfareFight()
    announceRoundHype()
    sfxCrowdCheer()
    await showOverlay('FIGHT!', '#ff2d7b', 400)
    await sleep(80)

    // Log + fight animation in parallel
    const logPromise = addRoundToLog(round, true)
    const isCritical = Math.abs((round.botAScore || 0) - (round.botBScore || 0)) > 4
    const aWon = round.winnerId === props.fight.botA!.id
    const bWon = round.winnerId === props.fight.botB!.id

    await scene!.playRound({
      round: round.roundNumber,
      challengeType: round.challengeType,
      winnerId: round.winnerId,
      botAId: props.fight.botA!.id,
      botBId: props.fight.botB!.id,
      narration: round.narration || '',
      isCritical,
      botAScore: round.botAScore || 0,
      botBScore: round.botBScore || 0,
    })

    // Hit text overlay
    const hitWords = isCritical
      ? ['CRITICAL!', 'DEVASTATING!', 'OBLITERATED!', 'ANNIHILATED!', 'WRECKED!']
      : ['POW!', 'BAM!', 'WHAM!', 'CRACK!', 'SMASH!', 'BOOM!', 'THWACK!']
    if (aWon || bWon) {
      showHitText(
        hitWords[Math.floor(Math.random() * hitWords.length)],
        isCritical ? '#ffe14d' : '#ff2d2d',
        aWon ? 65 : 35,
      )
      // Crowd reactions
      if (isCritical) {
        sfxCrowdGasp()
        setTimeout(() => sfxCrowdOoh(), 400)
      } else if (Math.random() < 0.4) {
        sfxCrowdOoh()
      }
      // Random hype voiceover on big moments
      if (isCritical || Math.random() < 0.3) {
        setTimeout(() => announceRandomHype(), 300)
      }
    }

    await logPromise

    if (round.narration) {
      logItems.value.push({ type: 'narration', round: round.roundNumber, text: `>> ${round.narration}`, color: 'neon-yellow' })
      scrollLog()
    }

    const winner = aWon ? props.fight.botA!.name : bWon ? props.fight.botB!.name : 'DRAW'
    logItems.value.push(
      { type: 'result', round: round.roundNumber, text: `${winner} ${aWon || bWon ? 'wins round!' : '- no winner'}`, color: aWon ? 'neon-cyan' : bWon ? 'neon-pink' : 'text-muted' },
      { type: 'divider', round: round.roundNumber, text: '', color: '' },
    )
    scrollLog()

    // HP update
    const baseDmg = 15
    if (aWon) {
      const dmg = Math.max(8, baseDmg + ((round.botAScore || 5) - (round.botBScore || 5)) * 3)
      displayHpB.value = Math.max(0, displayHpB.value - Math.round(dmg))
    } else if (bWon) {
      const dmg = Math.max(8, baseDmg + ((round.botBScore || 5) - (round.botAScore || 5)) * 3)
      displayHpA.value = Math.max(0, displayHpA.value - Math.round(dmg))
    }

    // Dynamic music intensity — lower HP = more intense
    const lowestHp = Math.min(displayHpA.value, displayHpB.value)
    setMusicIntensity(1 - lowestHp / 100)

    // Taunting between rounds — winner taunts, sometimes both
    if (scene && (aWon || bWon)) {
      const winnerSide = aWon ? 'a' : 'b'
      await sleep(150)
      await scene.playTaunt(winnerSide)
      // Sometimes loser taunts back (30% chance)
      if (Math.random() < 0.3) {
        await scene.playTaunt(winnerSide === 'a' ? 'b' : 'a')
      }
      await sleep(200)
    } else {
      await sleep(400)
    }
  }

  // Map server HP (0-200) to display (0-100), loser always 0
  displayHpA.value = mapHp(props.fight.botAHp, props.fight.winnerId, props.fight.botA?.id)
  displayHpB.value = mapHp(props.fight.botBHp, props.fight.winnerId, props.fight.botB?.id)

  scene?.stopMusic()

  // Always end with a dramatic death/KO sequence
  if (scene) {
    if (props.fight.winnerId) {
      const winningSide = props.fight.winnerId === props.fight.botA!.id ? 'a' : 'b'
      const winnerHp = winningSide === 'a' ? props.fight.botAHp : props.fight.botBHp
      const isPerfect = winnerHp >= 200
      const winnerName = winningSide === 'a' ? props.fight.botA!.name : props.fight.botB!.name

      // "FINISH HIM!" moment before KO
      sfxDrumRoll()
      await sleep(500)
      announceFinishHim()
      await showOverlay('FINISH HIM!', '#ff2d2d', 900)
      await sleep(150)

      if (isPerfect) {
        await scene.playPerfect(winningSide, winnerName)
        announceFlawlessVictory()
      } else {
        await scene.playKO(winningSide, winnerName)
        announceFatality()
      }

      glitching.value = true
      sfxApplause()
      sfxCrowdCheer()
      await sleep(200)
      glitching.value = false
      await showOverlay(`${winnerName} WINS!`, '#00f0ff', 1800)

      logItems.value.push(
        { type: 'divider', round: 99, text: '', color: '' },
        { type: 'result', round: 99, text: `${winnerName.toUpperCase()} WINS!${isPerfect ? ' PERFECT!' : ''}`, color: winningSide === 'a' ? 'neon-cyan' : 'neon-pink' },
      )
      scrollLog()
    } else {
      // Draws get a dramatic double-KO
      await scene.playKO('a', 'NOBODY')
      await showOverlay('DOUBLE K.O.!', '#ff2d2d', 1500)
      logItems.value.push(
        { type: 'divider', round: 99, text: '', color: '' },
        { type: 'result', round: 99, text: 'DOUBLE K.O.! DRAW!', color: 'neon-purple' },
      )
      scrollLog()
    }
  }

  isReplaying.value = false
}
</script>

<template>
  <div class="h-full flex flex-col lg:flex-row gap-1 sm:gap-2">

    <!-- LEFT: Battle Log (below on mobile) -->
    <div class="h-[30vh] sm:h-auto lg:w-[38%] flex flex-col min-h-0 border border-border rounded-lg bg-black/90 overflow-hidden order-2 lg:order-1">
      <div class="bg-surface-raised border-b border-border px-3 py-1.5 flex items-center gap-2 flex-shrink-0">
        <span class="w-2.5 h-2.5 rounded-full bg-ko" />
        <span class="w-2.5 h-2.5 rounded-full bg-neon-yellow" />
        <span class="w-2.5 h-2.5 rounded-full bg-neon-green" />
        <span class="font-pixel text-[10px] text-text-muted ml-2 tracking-wider">BATTLE LOG</span>
      </div>

      <div ref="logEl" class="flex-1 overflow-y-auto p-2 sm:p-4 font-mono text-xs sm:text-sm space-y-1 leading-relaxed">
        <div v-for="(item, idx) in logItems" :key="idx">
          <div v-if="item.type === 'divider'" class="py-2" />
          <p v-else-if="item.type === 'header'" class="text-neon-purple font-bold text-base tracking-wide pt-2">{{ item.text }}</p>
          <p v-else-if="item.type === 'prompt'" class="text-text-muted text-xs italic pl-2 pb-1">{{ item.text }}</p>
          <p v-else-if="item.type === 'responseA'" class="text-neon-cyan text-sm pl-2">{{ item.text }}</p>
          <p v-else-if="item.type === 'responseB'" class="text-neon-pink text-sm pl-2">{{ item.text }}</p>
          <p v-else-if="item.type === 'time'" class="text-text-muted text-xs pl-4">{{ item.text }}</p>
          <p v-else-if="item.type === 'narration'" class="text-neon-yellow font-bold text-sm pl-2 py-1">{{ item.text }}</p>
          <p v-else-if="item.type === 'result'" :class="['font-bold text-sm pl-2', item.color === 'neon-cyan' ? 'text-neon-cyan' : item.color === 'neon-pink' ? 'text-neon-pink' : 'text-text-secondary']">{{ item.text }}</p>
          <p v-else-if="item.type === 'system'" :class="['text-sm', item.color === 'neon-purple' ? 'text-neon-purple font-bold tracking-wider' : 'text-text-muted']">{{ item.text }}</p>
        </div>

        <div v-if="logItems.length === 0 && !isReplaying" class="text-text-muted italic pt-8 text-center text-sm">Hit REPLAY to watch the fight.</div>
        <div v-if="isReplaying && logItems.length === 0" class="text-neon-purple italic pt-8 text-center text-sm">Fight starting...</div>
      </div>
    </div>

    <!-- RIGHT: Game Canvas -->
    <div class="lg:w-[62%] flex flex-col min-h-0 border border-border rounded-lg bg-black overflow-hidden order-1 lg:order-2">

      <!-- Health bars -->
      <div class="px-2 sm:px-3 py-1.5 sm:py-2 bg-surface-raised/80 border-b border-border flex-shrink-0">
        <div class="flex items-center gap-1 sm:gap-2">
          <div class="flex-shrink-0 flex items-center gap-1 min-w-0 max-w-[25%] sm:max-w-none">
            <img
              v-if="fight.botA?.profilePicUrl"
              :src="fight.botA.profilePicUrl"
              alt=""
              class="w-5 h-5 sm:w-7 sm:h-7 rounded-full border border-neon-cyan/40 flex-shrink-0"
            />
            <p class="font-marker text-[10px] sm:text-sm tracking-wider truncate"
               :class="fight.winnerId === fight.botA?.id ? 'text-neon-cyan glow-cyan' : 'text-text-primary'">
              {{ fight.botA?.name }}
            </p>
          </div>
          <div class="flex-1 h-4 sm:h-5 bg-black/50 rounded-sm overflow-hidden border border-neon-cyan/30">
            <div class="h-full bg-gradient-to-r from-neon-cyan to-neon-purple health-bar" :style="{ width: `${displayHpA}%` }" />
          </div>
          <span class="font-mono font-bold text-[10px] sm:text-sm w-6 sm:w-8 text-right" :class="displayHpA > 50 ? 'text-neon-cyan' : displayHpA > 20 ? 'text-neon-yellow' : 'text-ko'">{{ displayHpA }}</span>
          <span class="font-funky text-neon-purple text-base sm:text-xl px-0.5 sm:px-1">VS</span>
          <span class="font-mono font-bold text-[10px] sm:text-sm w-6 sm:w-8 text-left" :class="displayHpB > 50 ? 'text-neon-pink' : displayHpB > 20 ? 'text-neon-yellow' : 'text-ko'">{{ displayHpB }}</span>
          <div class="flex-1 h-4 sm:h-5 bg-black/50 rounded-sm overflow-hidden border border-neon-pink/30">
            <div class="h-full bg-gradient-to-l from-neon-pink to-neon-purple health-bar ml-auto" :style="{ width: `${displayHpB}%` }" />
          </div>
          <div class="flex-shrink-0 flex items-center gap-1 min-w-0 max-w-[25%] sm:max-w-none">
            <p class="font-marker text-[10px] sm:text-sm tracking-wider truncate text-right"
               :class="fight.winnerId === fight.botB?.id ? 'text-neon-pink glow-pink' : 'text-text-primary'">
              {{ fight.botB?.name }}
            </p>
            <img
              v-if="fight.botB?.profilePicUrl"
              :src="fight.botB.profilePicUrl"
              alt=""
              class="w-5 h-5 sm:w-7 sm:h-7 rounded-full border border-neon-pink/40 flex-shrink-0"
            />
          </div>
        </div>
        <div class="flex items-center justify-between mt-0.5 sm:mt-1">
          <span class="font-pixel text-[8px] sm:text-[9px]" :class="tierClass(fight.botA?.tier || 0)">{{ Math.round(fight.botA?.eloRating || 0) }}</span>
          <span class="font-pixel text-[8px] sm:text-[9px] text-text-muted">{{ fight.arenaInfo?.name }} | R{{ currentRound || fight.totalRounds }}/{{ fight.totalRounds }}</span>
          <span class="font-pixel text-[8px] sm:text-[9px]" :class="tierClass(fight.botB?.tier || 0)">{{ Math.round(fight.botB?.eloRating || 0) }}</span>
        </div>
      </div>

      <!-- Canvas + floating overlays -->
      <div class="flex-1 relative min-h-0" :class="{ 'glitch-container': glitching }">
        <canvas ref="canvasRef" class="w-full h-full block" />

        <!-- Floating announcement -->
        <Transition name="announce">
          <div v-if="announcementVisible"
               class="absolute inset-0 flex items-center justify-center pointer-events-none z-20">
            <div class="announce-text-wrapper">
              <p class="font-funky text-5xl sm:text-7xl tracking-widest announce-text uppercase announce-chromatic"
                 :data-text="announcement"
                 :style="{ color: announcementColor, textShadow: `0 0 20px ${announcementColor}, 0 0 40px ${announcementColor}, 0 0 80px ${announcementColor}40, 0 0 120px ${announcementColor}20` }">
                {{ announcement }}
              </p>
            </div>
          </div>
        </Transition>

        <!-- Floating hit text -->
        <Transition name="hit-pop">
          <div v-if="hitTextVisible"
               class="absolute pointer-events-none z-30"
               :style="{ left: `${hitTextX}%`, top: `${hitTextY}%`, transform: 'translate(-50%, -50%)' }">
            <div class="hit-text-wrapper">
              <p class="font-neon text-4xl sm:text-5xl tracking-wider hit-text hit-chromatic"
                 :data-text="hitText"
                 :style="{ color: hitTextColor, textShadow: `0 0 15px ${hitTextColor}, 0 0 30px ${hitTextColor}, 0 0 60px ${hitTextColor}60` }">
                {{ hitText }}
              </p>
            </div>
          </div>
        </Transition>
      </div>

      <!-- Controls -->
      <div class="px-3 py-2 border-t border-border flex-shrink-0 flex items-center justify-between bg-surface-raised/50">
        <button
          class="px-6 py-2 border border-neon-pink/50 text-neon-pink font-marker text-sm
                 tracking-widest hover:bg-neon-pink/10 transition-all neon-border-pink
                 disabled:opacity-30 disabled:cursor-not-allowed"
          :disabled="isReplaying"
          @click="replay"
        >
          {{ isReplaying ? 'FIGHTING...' : 'REPLAY FIGHT' }}
        </button>
        <span class="font-pixel text-[10px] text-text-muted">{{ fight.status === 'finished' ? 'FINISHED' : fight.status.toUpperCase() }}</span>
      </div>
    </div>
  </div>
</template>

<style scoped>
/* Announcement transitions */
.announce-enter-active { animation: announce-in 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); }
.announce-leave-active { animation: announce-out 0.25s ease-in; }
@keyframes announce-in { from { opacity: 0; transform: scale(0.1) rotate(-15deg); filter: blur(8px); } to { opacity: 1; transform: scale(1) rotate(0); filter: blur(0); } }
@keyframes announce-out { from { opacity: 1; } to { opacity: 0; transform: scale(1.5) rotate(5deg); filter: blur(4px); } }

.announce-text-wrapper {
  position: relative;
}

.announce-text {
  animation: announce-pulse 0.4s ease-in-out infinite alternate, announce-hue 2s linear infinite;
  -webkit-text-stroke: 1px rgba(0,0,0,0.3);
}
@keyframes announce-pulse {
  from { transform: scale(1) rotate(-1deg); }
  to { transform: scale(1.08) rotate(1deg); }
}
@keyframes announce-hue {
  0% { filter: hue-rotate(0deg) brightness(1); }
  25% { filter: hue-rotate(15deg) brightness(1.1); }
  50% { filter: hue-rotate(0deg) brightness(1.2); }
  75% { filter: hue-rotate(-15deg) brightness(1.1); }
  100% { filter: hue-rotate(0deg) brightness(1); }
}

/* Chromatic aberration on announcements */
.announce-chromatic {
  position: relative;
}
.announce-chromatic::before,
.announce-chromatic::after {
  content: attr(data-text);
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  text-align: center;
  opacity: 0.5;
  pointer-events: none;
}
.announce-chromatic::before {
  color: #ff2d7b;
  animation: chromatic-r 0.3s ease-in-out infinite alternate;
  clip-path: inset(0 0 50% 0);
}
.announce-chromatic::after {
  color: #00f0ff;
  animation: chromatic-b 0.3s ease-in-out infinite alternate-reverse;
  clip-path: inset(50% 0 0 0);
}
@keyframes chromatic-r { from { transform: translate(-3px, -2px); } to { transform: translate(3px, 2px); } }
@keyframes chromatic-b { from { transform: translate(3px, 2px); } to { transform: translate(-3px, -2px); } }

/* Hit text */
.hit-pop-enter-active { animation: hit-in 0.12s cubic-bezier(0.34, 1.56, 0.64, 1); }
.hit-pop-leave-active { animation: hit-out 0.6s ease-in; }
@keyframes hit-in { from { opacity: 0; transform: translate(-50%, -50%) scale(0.1) rotate(-20deg); } to { opacity: 1; transform: translate(-50%, -50%) scale(1.2) rotate(0); } }
@keyframes hit-out { from { opacity: 1; transform: translate(-50%, -50%) scale(1); } to { opacity: 0; transform: translate(-50%, -100%) scale(0.4) rotate(15deg); } }

.hit-text-wrapper {
  position: relative;
}

.hit-text {
  animation: hit-shake 0.08s ease-in-out 5, hit-rainbow 0.5s steps(4) infinite;
  -webkit-text-stroke: 1px rgba(0,0,0,0.4);
}
@keyframes hit-shake {
  0%, 100% { transform: translate(-50%, -50%) rotate(0); }
  20% { transform: translate(-48%, -52%) rotate(-5deg) scale(1.15); }
  40% { transform: translate(-52%, -48%) rotate(5deg) scale(1.1); }
  60% { transform: translate(-50%, -53%) rotate(-3deg) scale(1.2); }
  80% { transform: translate(-49%, -47%) rotate(4deg) scale(1.05); }
}
@keyframes hit-rainbow {
  0% { filter: hue-rotate(0deg) brightness(1.2); }
  25% { filter: hue-rotate(60deg) brightness(1.4); }
  50% { filter: hue-rotate(120deg) brightness(1.2); }
  75% { filter: hue-rotate(180deg) brightness(1.3); }
  100% { filter: hue-rotate(360deg) brightness(1.2); }
}

/* Chromatic aberration on hits */
.hit-chromatic {
  position: relative;
}
.hit-chromatic::before,
.hit-chromatic::after {
  content: attr(data-text);
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0.6;
  pointer-events: none;
}
.hit-chromatic::before {
  color: #ff2d2d;
  animation: hit-chr-r 0.06s ease-in-out infinite alternate;
}
.hit-chromatic::after {
  color: #00f0ff;
  animation: hit-chr-b 0.06s ease-in-out infinite alternate-reverse;
}
@keyframes hit-chr-r { from { transform: translate(-4px, -3px) rotate(-2deg); } to { transform: translate(4px, 3px) rotate(2deg); } }
@keyframes hit-chr-b { from { transform: translate(4px, 3px) rotate(2deg); } to { transform: translate(-4px, -3px) rotate(-2deg); } }

/* Glitch effect on hits */
.glitch-container {
  animation: glitch-screen 0.15s steps(2) 2;
}
@keyframes glitch-screen {
  0% { filter: none; }
  20% { filter: hue-rotate(90deg) saturate(2); transform: translate(2px, -1px); }
  40% { filter: hue-rotate(-90deg) contrast(1.5); transform: translate(-2px, 1px); }
  60% { filter: invert(0.1) saturate(3); transform: translate(1px, 2px); }
  80% { filter: hue-rotate(45deg) brightness(1.3); transform: translate(-1px, -2px); }
  100% { filter: none; transform: none; }
}

/* VHS scanline overlay on canvas */
.glitch-container::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    0deg,
    transparent,
    transparent 2px,
    rgba(0, 0, 0, 0.06) 2px,
    rgba(0, 0, 0, 0.06) 4px
  );
  z-index: 40;
  animation: scanline-drift 0.3s linear infinite;
}
@keyframes scanline-drift {
  0% { background-position-y: 0; }
  100% { background-position-y: 4px; }
}
</style>
