Twitch Poké Ball Helper (Enhanced UI – Browse & Advanced)

Twitch Poké Ball Helper with a three-column grid for Catch/Shop plus two distinct lookup tabs: a visually rich Browse tab and a detailed Advanced tab featuring a full-width Pokémon info card with Pokédex entry and evolution chain. All styled with advanced UI techniques and a unified Roboto font. Shoutout doubleupmafia @doubleupmolly @doubleuplowlow219 @doubleupap @doubleupeazy @musiclov3r1435

当前为 2025-02-28 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Twitch Poké Ball Helper (Enhanced UI – Browse & Advanced)
// @namespace    http://tampermonkey.net/
// @version      10
// @description  Twitch Poké Ball Helper with a three-column grid for Catch/Shop plus two distinct lookup tabs: a visually rich Browse tab and a detailed Advanced tab featuring a full-width Pokémon info card with Pokédex entry and evolution chain. All styled with advanced UI techniques and a unified Roboto font. Shoutout doubleupmafia @doubleupmolly @doubleuplowlow219 @doubleupap @doubleupeazy @musiclov3r1435
// @author
// @match        https://www.twitch.tv/*
// @icon         https://static.twitchcdn.net/assets/favicon-32-e29e246c157142c94346.png
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  class PokeballHelper {
    constructor() {
      this.catchBalls = {
        dollars: { command: '$', tooltip: 'Poke Dollars', image: 'https://cdn.discordapp.com/attachments/1095453488684744786/1344388523750457354/pngwing.com_13.png?ex=67c0bae1&is=67bf6961&hm=ef378cc914ec3d785094e9a21690c377fbc3d5187ee243ae0d9d21b522ece867&' },
        check: { command: '!pokecheck', tooltip: 'Poke Check', image: 'https://cdn.discordapp.com/attachments/1095453488684744786/1344383577323995168/pngwing.com_2.png?ex=67c0b646&is=67bf64c6&hm=71bdf7b7a547df375849fa3874370067476fad556422dd16df77d6beba710a90&' },
        poke: { command: '!pokecatch', tooltip: 'Poke Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/poke_ball.png' },
        great: { command: '!pokecatch greatball', tooltip: 'Great Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/great_ball.png' },
        ultra: { command: '!pokecatch ultraball', tooltip: 'Ultra Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/ultra_ball.png' },
        master: { command: '!pokecatch masterball', tooltip: 'Master Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/master_ball.png' },
        premier: { command: '!pokecatch premierball', tooltip: 'Premier Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/premier_ball.png' },
        cherish: { command: '!pokecatch cherishball', tooltip: 'Cherish Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/cherish_ball.png' },
        greatCherish: { command: '!pokecatch greatcherishball', tooltip: 'Great Cherish Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/great_cherish_ball.png' },
        ultraCherish: { command: '!pokecatch ultracherishball', tooltip: 'Ultra Cherish Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/ultra_cherish_ball.png' },
        heavy: { command: '!pokecatch heavyball', tooltip: 'Heavy Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/heavy_ball.png' },
        feather: { command: '!pokecatch featherball', tooltip: 'Feather Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/feather_ball.png' },
        timer: { command: '!pokecatch timerball', tooltip: 'Timer Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/timer_ball.png' },
        quick: { command: '!pokecatch quickball', tooltip: 'Quick Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/quick_ball.png' },
        nest: { command: '!pokecatch nestball', tooltip: 'Nest Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/nest_ball.png' },
        fast: { command: '!pokecatch fastball', tooltip: 'Fast Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/fast_ball.png' },
        heal: { command: '!pokecatch healball', tooltip: 'Heal Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/heal_ball.png' },
        repeat: { command: '!pokecatch repeatball', tooltip: 'Repeat Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/repeat_ball.png' },
        friend: { command: '!pokecatch friendball', tooltip: 'Friend Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/friend_ball.png' },
        frozen: { command: '!pokecatch frozenball', tooltip: 'Frozen Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/frozen_ball.png' },
        night: { command: '!pokecatch nightball', tooltip: 'Night Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/night_ball.png' },
        phantom: { command: '!pokecatch phantomball', tooltip: 'Phantom Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/phantom_ball.png' },
        cipher: { command: '!pokecatch cipherball', tooltip: 'Cipher Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/cipher_ball.png' },
        magnet: { command: '!pokecatch magnetball', tooltip: 'Magnet Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/magnet_ball.png' },
        net: { command: '!pokecatch netball', tooltip: 'Net Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/net_ball.png' },
        luxury: { command: '!pokecatch luxuryball', tooltip: 'Luxury Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/luxury_ball.png' },
        stone: { command: '!pokecatch stoneball', tooltip: 'Stone Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/stone_ball.png' },
        level: { command: '!pokecatch levelball', tooltip: 'Level Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/level_ball.png' },
        clone: { command: '!pokecatch cloneball', tooltip: 'Clone Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/clone_ball.png' },
        sun: { command: '!pokecatch sunball', tooltip: 'Sun Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/sun_ball.png' },
        fantasy: { command: '!pokecatch fantasyball', tooltip: 'Fantasy Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/fantasy_ball.png' },
        mach: { command: '!pokecatch machball', tooltip: 'Mach Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/mach_ball.png' },
        dive: { command: '!pokecatch diveball', tooltip: 'Dive Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/dive_ball.png' }
      };

      this.shopBalls = {
        pokeball: { command: '!pokeshop pokeball', tooltip: 'Poke Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/poke_ball.png' },
        great: { command: '!pokeshop greatball', tooltip: 'Great Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/great_ball.png' },
        ultra: { command: '!pokeshop ultraball', tooltip: 'Ultra Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/ultra_ball.png' }
      };

      this.currentTab = 'catch';
      this.allPokemonList = null;

      this.isDragging = false;
      this.startX = 0;
      this.startY = 0;
      this.containerStartLeft = 0;
      this.containerStartTop = 0;
      this.wasDragging = false;

      this.dragStart = this.dragStart.bind(this);
      this.drag = this.drag.bind(this);
      this.dragEnd = this.dragEnd.bind(this);

      this.init();
    }

    init() {
      this.setupStyles();
      this.waitForChat().then(() => {
        this.createInterface();
        this.createTimerElement();
        this.addEventListeners();
        this.renderGrid();
        this.updateSpawnTimer();
      });
    }

    loadPosition() {
      const savedPos = localStorage.getItem('pballPosition');
      if (savedPos) {
        const { x, y } = JSON.parse(savedPos);
        this.container.style.left = `${x}px`;
        this.container.style.top = `${y}px`;
      }
    }

    dragStart(e) {
      e.preventDefault();
      this.wasDragging = false;

      const startX = e.clientX;
      const startY = e.clientY;
      const rect = this.container.getBoundingClientRect();
      const origLeft = rect.left;
      const origTop = rect.top;

      const onMouseMove = (moveEvent) => {
        const deltaX = moveEvent.clientX - startX;
        const deltaY = moveEvent.clientY - startY;

        if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
          this.wasDragging = true;
        }

        this.container.style.left = `${origLeft + deltaX}px`;
        this.container.style.top = `${origTop + deltaY}px`;
      };

      const onMouseUp = () => {
        window.removeEventListener('mousemove', onMouseMove);
        window.removeEventListener('mouseup', onMouseUp);
        const ballImg = e.target.closest('.pball-item img');
        if (ballImg) {
          ballImg.style.cursor = 'grab';
        }
      };

      const ballImg = e.target.closest('.pball-item img');
      if (ballImg) {
        ballImg.style.cursor = 'grabbing';
      }

      window.addEventListener('mousemove', onMouseMove);
      window.addEventListener('mouseup', onMouseUp);
    }

    drag(e) {
      this.container.classList.remove('dragging');

      e.preventDefault();
      const dx = e.clientX - this.startX;
      const dy = e.clientY - this.startY;
      if (!this.isDragging && (Math.abs(dx) > 5 || Math.abs(dy) > 5)) {
        this.isDragging = true;
      }
      if (this.isDragging) {
        let newX = this.containerStartLeft + dx;
        let newY = this.containerStartTop + dy;

        const chatWindow = document.querySelector('.chat-window');
        if (chatWindow) {
          const chatRect = chatWindow.getBoundingClientRect();
          const ballRect = this.container.getBoundingClientRect();
          newX = Math.max(chatRect.left, Math.min(newX, chatRect.right - ballRect.width));
          newY = Math.max(chatRect.top, Math.min(newY, chatRect.bottom - ballRect.height));
        }

        requestAnimationFrame(() => {
          this.container.style.left = `${newX}px`;
          this.container.style.top = `${newY}px`;
        });
      }
    }

    dragEnd(e) {
      document.removeEventListener('mousemove', this.drag);
      document.removeEventListener('mouseup', this.dragEnd);
      if (this.isDragging) {
        this.wasDragging = true;
        const left = this.container.offsetLeft;
        const top = this.container.offsetTop;
        localStorage.setItem('pballPosition', JSON.stringify({ x: left, y: top }));
      }
      this.container.style.transition = '';
    }

setupStyles() {
  const style = document.createElement('style');
  style.textContent = `
    /* Theme Variables */
/*--------------------------------------------------
  Import Fonts
--------------------------------------------------*/
@import url('https://fonts.googleapis.com/css2?family=Segment7Standard&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');

/*--------------------------------------------------
  Global Variables & Base Styles
--------------------------------------------------*/
:root {
  /* Pball UI Variables */
  --background-dark: #18181b;
  --background-darker: #2e2e35;
  --card-background: #1f1f26;
  --pball-border-color: #3e3e45;
  --pball-highlight-color: #76c7c0;
  --pball-highlight-gradient: linear-gradient(90deg, var(--pball-highlight-color), #4db6ac);
  --text-light: #ffffff;
  --text-muted: #ccc;
  --font-family: 'Roboto', sans-serif;
  --glass-effect: rgba(255, 255, 255, 0.1);
  --scrollbar-track: var(--background-darker);
  --scrollbar-thumb: var(--pball-border-color);
  --scrollbar-thumb-hover: #555;

  /* Timer UI Variables */
  --timer-color-led: #00ff37;
  --timer-color-low: #ff4040;
  --timer-font-led: 'Segment7Standard', monospace;
  --timer-font-label: 'Press Start 2P', cursive;
  --timer-label-text-color: #FFE135;
  --timer-transition-speed: 0.3s;
  --timer-font-size-label: 0.75rem; /* 12px */
  --timer-font-size-countdown: 2rem;  /* 32px */
}

/* Global & Scrollbar Styles */
.pball-container,
.pball-container * {
  font-family: var(--font-family);
  box-sizing: border-box;
}

::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
::-webkit-scrollbar-track {
  background: var(--scrollbar-track);
  border-radius: 8px;
}
::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
  border-radius: 8px;
  border: 1px solid var(--background-dark);
}
::-webkit-scrollbar-thumb:hover {
  background: var(--scrollbar-thumb-hover);
}

/*--------------------------------------------------
  Pball Container & Components
--------------------------------------------------*/
/* Main Container (Fixed Position) */
.pball-container {
  position: fixed;
  right: 12px;
  bottom: 95px;
  z-index: 10000;
  user-select: none;
  transform: scale(1);
  transform-origin: top left;
  width: fit-content;
  height: fit-content;
  pointer-events: none;
}

/* Allow interactions for designated children */
.pball-container > .pball-button,
.pball-panel.active {
  pointer-events: auto;
}

/* Interactive Button */
.pball-button {
  cursor: pointer;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  border: 2px solid var(--pball-border-color);
  background: var(--background-dark);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.pball-button:hover {
  transform: scale(1.3);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
}

/* Floating Panel */
.pball-panel {
  position: absolute;
  bottom: calc(100% + 10px);
  right: 0;
  width: 320px;
  background: var(--background-dark);
  border-radius: 16px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
  border: 1px solid var(--glass-effect);
  opacity: 0;
  visibility: hidden;
  transform: translateY(20px);
  transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s;
}
.pball-panel.active {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* Tab System */
.pball-tabs {
  display: flex;
  background: var(--background-darker);
  border-bottom: 1px solid var(--pball-border-color);
}
.pball-tabs .pball-tab {
  flex: 1;
  padding: 10px;
  text-align: center;
  font-size: 16px;
  cursor: pointer;
  color: var(--text-muted);
  transition: background 0.2s ease, color 0.2s ease;
}
.pball-tabs .pball-tab.active,
.pball-tabs .pball-tab:hover {
  background: var(--pball-border-color);
  color: var(--text-light);
}

/* Search Components */
.pball-search-container {
  position: relative;
  margin: 12px;
}
.pball-search-container .pball-search {
  width: 100%;
  padding: 8px 36px 8px 12px;
  border: 1px solid var(--pball-border-color);
  border-radius: 8px;
  background: var(--background-dark);
  color: var(--text-light);
  font-size: 15px;
  outline: none;
  transition: border-color 0.2s ease;
}
.pball-search-container .pball-search:focus {
  border-color: var(--pball-highlight-color);
}
.pball-search-container .pball-search::placeholder {
  color: var(--text-muted);
}
.pball-search-container .pball-clear-btn {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 18px;
  cursor: pointer;
  display: none;
}

/* Grid Layouts for Items */
.pball-grid {
  padding: 12px;
  display: grid;
  gap: 12px;
  max-height: 300px;
  overflow-y: auto;
}
.pball-grid.ball-items {
  grid-template-columns: repeat(3, 1fr);
}
.pball-grid.search-results {
  grid-template-columns: 1fr;
}

/* Item Card */
.pball-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: 50%;
  padding: 8px;
  cursor: default;
}
.pball-item img {
  pointer-events: auto;
  user-select: none;
  cursor: grab;
  width: 36px;
  height: 36px;
  transition: transform 0.2s ease;
}
.pball-item img:hover {
  transform: scale(1.3);
}
.pball-item img.dragging {
  opacity: 0.6;
  transform: scale(0.8);
  filter: drop-shadow(0 0 4px rgba(118, 199, 192, 0.5));
}
.pball-item .pball-label {
  margin-top: 6px;
  font-size: 13px;
  color: var(--text-light);
  text-align: center;
}

/* Browse Tab Components */
.browse-container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 12px;
  padding: 16px;
}
.browse-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  background: var(--background-darker);
  border: 1px solid var(--pball-border-color);
  border-radius: 12px;
  padding: 12px;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  cursor: pointer;
  text-align: center;
}
.browse-tile:hover {
  transform: translateY(-3px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
.browse-tile img {
  width: 64px;
  height: 64px;
  margin-bottom: 6px;
}
.browse-tile .tile-label {
  font-size: 14px;
  font-weight: 500;
  color: var(--text-light);
  text-transform: capitalize;
}

/* Advanced Card Components */
.poke-card {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 12px;
}
.poke-card .section-title {
  font-size: 20px;
  font-weight: 700;
  margin-bottom: 12px;
  color: var(--text-light);
}
.poke-card .stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 12px;
}

/* Evolution Chain */
.evolution-chain {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  padding: 8px;
}
.evolution-chain .evolution-item {
  text-align: center;
  padding: 6px;
  background: var(--background-darker);
  border-radius: 8px;
  transition: transform 0.2s ease;
  margin-bottom: 4px;
}
.evolution-chain .evolution-item img {
  width: 64px;
  height: 64px;
  object-fit: contain;
  margin-bottom: 4px;
}
.evolution-chain .evolution-item p {
  font-size: 12px;
  color: var(--text-light);
  margin: 0;
}

/* Moves Section */
.moves-section {
  max-height: 300px;
  overflow-y: auto;
}

/*--------------------------------------------------
  Timer Container & Components
--------------------------------------------------*/
/* Timer Container with Glassmorphism & Grid Layout */
.spawn-timer {
  position: absolute;
  bottom: calc(100% - 4.7rem);
  right: 4.8rem;
  padding: 0.75rem 1rem;
  transform: perspective(500px) rotateX(5deg);
  z-index: 10001;
  display: flex; /* Changed from grid to flex */
  align-items: center; /* Vertical alignment */
  gap: 1rem; /* Space between elements */
  transition: transform var(--timer-transition-speed) ease;
}

.spawn-timer:hover {
  transform: perspective(500px) rotateX(0deg);
}
.spawn-timer::before {
  content: '';
  position: absolute;
  top: -0.5rem;
  left: -0.5rem;
  right: -0.5rem;
  bottom: -0.5rem;

  pointer-events: none;
}


/* Timer Header: Centered Layout */
.timer-header {
  display: flex;
  grid-template-columns: 1fr;
  justify-items: center;
  align-items: center;
  gap: 0.25rem;
  width: 100%;
}

/* Timer Label with Elegant Typography */
.timer-label {
  font-family: var(--timer-font-label);
  font-size: var(--timer-font-size-label);
  color: var(--timer-label-text-color);
  text-transform: uppercase;
  letter-spacing: 0.09375rem;
  padding: 0.375rem 0.625rem;
  border-radius: 0.375rem;
  border: 0.0625rem solid rgba(58, 90, 109, 0.4);
  background: rgba(255, 255, 255, 0.05);
  text-shadow: 0 0.0625rem 0.125rem rgba(0, 0, 0, 0.4),
               0 0.5rem 0.5rem rgba(148, 180, 196, 0.3);
  transition: background var(--timer-transition-speed) ease;
}
.timer-label:hover {
  background: rgba(255, 255, 255, 0.15);
}

/* Countdown Display (Neon & Interactive) */
.countdown-display {
  font-family: var(--timer-font-led);
  font-size: var(--timer-font-size-countdown);
  font-weight: bold;
  color: var(--timer-color-led);
  padding: 0.375rem 0.75rem;
  border-radius: 0.375rem;
  letter-spacing: 0.125rem;
  min-width: 7.5rem;
  text-align: center;
  background: linear-gradient(135deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.05));
  box-shadow: 0 0.375rem 0.75rem rgba(0, 255, 55, 0.4),
              inset 0 0.25rem 0.5rem rgba(0, 255, 55, 0.3);
  position: relative;
  line-height: 1;
  transition: transform var(--timer-transition-speed) ease, box-shadow var(--timer-transition-speed) ease;
}
.countdown-display:hover {
  transform: scale(1.05);
  box-shadow: 0 0.5rem 1rem rgba(0, 255, 55, 0.6),
              inset 0 0.375rem 0.75rem rgba(0, 255, 55, 0.4);
}
.countdown-display::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(circle, rgba(0, 255, 55, 0.2) 0%, transparent 70%);
  border-radius: inherit;
  z-index: -1;
  transition: opacity var(--timer-transition-speed) ease;
}
.countdown-display::after {
  content: '';
  position: absolute;
  top: -50%;
  left: -50%;
  width: 200%;
  height: 200%;
  z-index: -2;
}

/* Low Time Warning (Enhanced Neon Pulse) */
.countdown-display.low-time {
  color: var(--timer-color-low);
  text-shadow: 0 0.5rem 0.75rem rgba(255, 64, 64, 0.8),
               0 0.25rem 0.375rem rgba(255, 64, 64, 0.9);
  animation: led-pulse 0.6s ease-in-out infinite, emergency-glow 1.2s ease-in-out infinite;
}
@keyframes led-pulse {
  0%, 100% { text-shadow: 0 0.5rem 0.75rem rgba(255, 64, 64, 0.8); }
  50% { text-shadow: 0 0.75rem 1rem rgba(255, 64, 64, 1); }
}
@keyframes emergency-glow {
  0%, 100% { box-shadow: 0 0.375rem 0.75rem rgba(255, 64, 64, 0.3); }
  50% { box-shadow: 0 0.75rem 1rem rgba(255, 64, 64, 0.6); }
}

/* Utility: Spinner Animation */
.spinner {
  margin: 1.5rem auto;
  border: 0.25rem solid var(--pball-border-color);
  border-top: 0.25rem solid var(--pball-highlight-color);
  border-radius: 50%;
  width: 2.5rem;
  height: 2.5rem;
  animation: spin 1s linear infinite;
}
@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/*--------------------------------------------------
  Responsive Adjustments
--------------------------------------------------*/
@media (max-width: 768px) {
  .poke-card {
    padding: 16px;
    gap: 16px;
  }
  .section-title {
    font-size: 18px;
  }
  .stats-radar-chart canvas {
    max-width: 100%;
  }
}

/* Responsive adjustments */
@media (max-width: 600px) {
  .spawn-timer {
    gap: 0.75rem; /* Reduced gap for mobile */
    padding: 0.5rem 0.75rem;
  }

  .timer-label {
    font-size: 0.6rem; /* Slightly smaller text */
    padding: 0.2rem 0.4rem;
  }

  .countdown-display {
    font-size: 1.5rem; /* Slightly smaller countdown */
    min-width: 4.5rem;
  }
}

  `;
  document.head.appendChild(style);
}

    async waitForChat() {
      return new Promise((resolve) => {
        if (document.querySelector('[data-test-selector="chat-input"]')) {
          return resolve();
        }
        const observer = new MutationObserver(() => {
          if (document.querySelector('[data-test-selector="chat-input"]')) {
            observer.disconnect();
            resolve();
          }
        });
        observer.observe(document.body, { childList: true, subtree: true });
      });
    }

    createInterface() {
      this.container = document.createElement('div');
      this.container.className = 'pball-container';
      this.button = this.createMainButton();
      this.panel = this.createPanel();
      this.container.append(this.button, this.panel);
      document.body.appendChild(this.container);
    }

    createMainButton() {
      const button = document.createElement('img');
      button.draggable = false;
      button.className = 'pball-button';
      button.src = this.catchBalls.poke.image;
      return button;
    }

    createPanel() {
      const panel = document.createElement('div');
      panel.className = 'pball-panel';
      panel.draggable = false;

      const tabsContainer = document.createElement('div');
      tabsContainer.className = 'pball-tabs';

      const catchTab = document.createElement('div');
      catchTab.className = 'pball-tab active';
      catchTab.textContent = 'Catch';
      catchTab.dataset.tab = 'catch';
      tabsContainer.appendChild(catchTab);

      const shopTab = document.createElement('div');
      shopTab.className = 'pball-tab';
      shopTab.textContent = 'Shop';
      shopTab.dataset.tab = 'shop';
      tabsContainer.appendChild(shopTab);

      const browseTab = document.createElement('div');
      browseTab.className = 'pball-tab';
      browseTab.textContent = 'Browse';
      browseTab.dataset.tab = 'browse';
      tabsContainer.appendChild(browseTab);

      const advancedTab = document.createElement('div');
      advancedTab.className = 'pball-tab';
      advancedTab.textContent = 'Advanced';
      advancedTab.dataset.tab = 'advanced';
      tabsContainer.appendChild(advancedTab);

      const searchContainer = document.createElement('div');
      searchContainer.className = 'pball-search-container';

      this.searchInput = document.createElement('input');
      this.searchInput.type = 'text';
      this.searchInput.className = 'pball-search';
      this.searchInput.placeholder = 'Search...';
      this.searchInput.setAttribute('aria-label', 'Search Pokémon');

      this.clearBtn = document.createElement('button');
      this.clearBtn.className = 'pball-clear-btn';
      this.clearBtn.textContent = '×';
      this.clearBtn.setAttribute('aria-label', 'Clear Search');

      searchContainer.append(this.searchInput, this.clearBtn);

      this.gridContainer = document.createElement('div');
      this.gridContainer.className = 'pball-grid';

      panel.append(tabsContainer, searchContainer, this.gridContainer);
      return panel;
    }

createTimerElement() {
  // Create and insert timer element
  this.timerContainer = document.createElement('div');
  this.timerContainer.className = 'spawn-timer';
  this.timerContainer.innerHTML = `
    <div class="timer-header">
      <div class="countdown-display">88:88</div>
    </div>
  `;
  this.container.appendChild(this.timerContainer);
  this.initTimer();
}

initTimer() {
  // Save references and default values
  this.backendUrl = 'https://poketwitch.bframework.de/info/events/last_spawn/';
  this.countdownElement = this.timerContainer.querySelector('.countdown-display');
  this.remainingSeconds = 0;
  this.tickInterval = null;
  this.apiInterval = null;
  this.isFetching = false;

  // Start the per-second countdown
  this.startTick();

  // Get the initial time from the API
  this.fetchTimer();

  // Update the timer from the API every minute
  this.apiInterval = setInterval(() => {
    this.fetchTimer();
  }, 60000);

  // Emergency refresh on click (ignoring if a fetch is already in progress)
  this.timerContainer.addEventListener('click', () => {
    if (!this.isFetching) {
      this.fetchTimer();
    }
  });
}

startTick() {
  // Clear any existing tick interval
  if (this.tickInterval) clearInterval(this.tickInterval);

  // Update the display every second using the local remainingSeconds counter
  this.tickInterval = setInterval(() => {
    if (this.remainingSeconds > 0) {
      this.remainingSeconds--;
      this.updateDisplay(this.remainingSeconds);
    } else {
      // If timer has run out, you might decide to call fetchTimer() here to refresh
      this.updateDisplay(0);
    }
  }, 1000);
}

updateDisplay(seconds) {
  if (isNaN(seconds) || seconds < 0) {
    this.countdownElement.textContent = '--:--';
    this.countdownElement.classList.remove('low-time');
    return;
  }
  const mins = Math.floor(seconds / 60);
  const secs = seconds % 60;
  this.countdownElement.textContent =
    `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
  // Toggle warning state when time is low (<= 30 seconds)
  this.countdownElement.classList.toggle('low-time', seconds <= 30);
}

fetchTimer() {
  // Prevent overlapping API calls
  if (this.isFetching) return;
  this.isFetching = true;

  fetch(this.backendUrl)
    .then(response => {
      if (!response.ok) throw new Error('Network error');
      return response.json();
    })
    .then(data => {
      const newTime = parseInt(data.next_spawn, 10);
      if (isNaN(newTime)) throw new Error('Invalid timer data');
      // Reset the remaining seconds with the new value from the backend.
      this.remainingSeconds = newTime;
      this.updateDisplay(this.remainingSeconds);
    })
    .catch(error => {
      console.error('Timer error:', error);
      this.remainingSeconds = -1;
      this.updateDisplay(NaN);
    })
    .finally(() => {
      this.isFetching = false;
    });
}


    renderGrid() {
      if (this.currentTab === 'advanced') {
        this.gridContainer.classList.remove('ball-items');
        this.gridContainer.classList.add('search-results');
        this.renderAdvancedInstruction();
      } else if (this.currentTab === 'browse') {
        this.gridContainer.classList.remove('ball-items');
        this.gridContainer.classList.add('search-results');
        this.renderBrowse();
      } else {
        this.gridContainer.classList.remove('search-results');
        this.gridContainer.classList.add('ball-items');
        this.gridContainer.innerHTML = '';
        const balls = this.currentTab === 'catch' ? this.catchBalls : this.shopBalls;
        Object.entries(balls).forEach(([key, ball]) => {
          const item = document.createElement('div');
          item.className = 'pball-item';
          item.dataset.label = ball.tooltip.toLowerCase();

          const img = document.createElement('img');
          img.src = ball.image;
          img.dataset.ballType = ball.command;
          img.draggable = true;

          const label = document.createElement('div');
          label.className = 'pball-label';
          label.textContent = ball.tooltip;

          item.append(img, label);
          this.gridContainer.appendChild(item);
        });
        this.filterGrid();
      }
    }

    renderAdvancedInstruction() {
      this.gridContainer.innerHTML = '';
      const info = document.createElement('div');
      info.style.padding = '12px';
      info.style.textAlign = 'center';
      info.style.color = 'var(--text-light)';
      info.textContent = 'Enter a Pokémon name and press Enter for detailed info.';
      this.gridContainer.appendChild(info);
    }

    renderBrowse() {
      this.gridContainer.innerHTML = '';
      if (!this.pokemonList) {
        this.gridContainer.innerHTML = '<div class="spinner"></div>';
        fetch('https://pokeapi.co/api/v2/pokemon?limit=20000')
          .then(response => response.json())
          .then(data => {
            this.pokemonList = data.results;
            this.renderBrowseGrid();
          })
          .catch(err => {
            this.gridContainer.innerHTML = `<div style="padding:12px; color: var(--text-light);">Error loading Pokémon list</div>`;
          });
      } else {
        this.renderBrowseGrid();
      }
    }

    renderBrowseGrid() {
      this.gridContainer.innerHTML = '';
      this.gridContainer.classList.add('browse-container');

      const query = this.searchInput.value.trim().toLowerCase();
      const filtered = this.pokemonList.filter(poke => poke.name.includes(query));

      filtered.forEach(poke => {
        const tile = document.createElement('div');
        tile.className = 'browse-tile';
        tile.dataset.label = poke.name.toLowerCase();

        const idMatch = poke.url.match(/\/pokemon\/(\d+)\//);
        const id = idMatch ? idMatch[1] : '';

        const img = document.createElement('img');
        img.src = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${id}.png`;

        const label = document.createElement('div');
        label.className = 'tile-label';
        label.textContent = poke.name;

        tile.append(img, label);

        tile.addEventListener('click', (e) => {
          e.stopPropagation();
          this.panel.classList.add('active');
          this.changeTab('advanced');
          this.searchInput.value = poke.name;
          this.searchAdvancedPokemon(poke.name);
        });

        this.gridContainer.appendChild(tile);
      });

      if (filtered.length === 0) {
        this.gridContainer.innerHTML = `<div style="padding:12px; color: var(--text-light);">No Pokémon match your search.</div>`;
      }
    }

    addEventListeners() {
      this.button.addEventListener('mousedown', this.dragStart);

      this.button.addEventListener('click', (e) => {
        if (this.wasDragging) {
          this.wasDragging = false;
          return;
        }
        e.stopPropagation();
        this.panel.classList.toggle('active');
        if (this.panel.classList.contains('active')) {
          this.searchInput.focus();
        }
      });

      document.addEventListener('click', (e) => {
        if (!this.container.contains(e.target)) {
          this.panel.classList.remove('active');
        }
      });

      this.panel.addEventListener('dragstart', (e) => {
        const ballImg = e.target.closest('.pball-item img');
        if (ballImg) {
          e.dataTransfer.setData('text/plain', ballImg.dataset.ballType);

          const dragImg = new Image();
          dragImg.src = ballImg.src;
          dragImg.style.width = '36px';
          dragImg.style.height = '36px';

          dragImg.style.position = 'absolute';
          dragImg.style.left = '-9999px';
          document.body.appendChild(dragImg);

          e.dataTransfer.setDragImage(dragImg, 18, 18);

          setTimeout(() => document.body.removeChild(dragImg), 0);

          ballImg.classList.add('dragging');

          const onDragEnd = () => {
            ballImg.classList.remove('dragging');
            document.removeEventListener('dragend', onDragEnd);
          };
          document.addEventListener('dragend', onDragEnd);
        }
      });

      const chatInput = document.querySelector('#chatInput');

      if (chatInput) {
        chatInput.addEventListener('dragover', (e) => {
          e.preventDefault();
          e.dataTransfer.dropEffect = 'copy';
        });

        chatInput.addEventListener('drop', (e) => {
          e.preventDefault();
          const ballType = e.dataTransfer.getData('text/plain');

          if (ballType) {
            chatInput.value += ` ${ballType}`;
          }
        });
      }

      const tabs = this.panel.querySelectorAll('.pball-tab');
      tabs.forEach(tab => {
        tab.addEventListener('click', (e) => {
          e.stopPropagation();
          this.changeTab(tab.dataset.tab);
        });
      });

      this.searchInput.addEventListener('input', () => {
        if (this.currentTab !== 'advanced') {
          this.filterGrid();
          if (this.currentTab === 'browse') {
            this.renderBrowseGrid();
          }
        }
        this.clearBtn.style.display = this.searchInput.value.trim() ? 'block' : 'none';
      });

      this.clearBtn.addEventListener('click', () => {
        this.searchInput.value = '';
        this.clearBtn.style.display = 'none';
        if (this.currentTab !== 'advanced') {
          this.filterGrid();
          if (this.currentTab === 'browse') {
            this.renderBrowseGrid();
          }
        }
      });

      this.searchInput.addEventListener('keydown', (e) => {
        if (this.currentTab === 'advanced' && e.key === 'Enter') {
          this.searchAdvancedPokemon(this.searchInput.value.trim());
        }
      });
    }

    changeTab(tabName) {
      this.currentTab = tabName;
      const tabs = this.panel.querySelectorAll('.pball-tab');
      tabs.forEach(tab => {
        tab.classList.toggle('active', tab.dataset.tab === tabName);
      });
      if (tabName === 'advanced') {
        this.searchInput.placeholder = 'Enter Pokémon name for detailed info...';
      } else if (tabName === 'browse') {
        this.searchInput.placeholder = 'Filter Pokémon...';
      } else {
        this.searchInput.placeholder = 'Search...';
      }
      this.searchInput.value = '';
      this.clearBtn.style.display = 'none';
      this.renderGrid();
    }

    filterGrid() {
      const query = this.searchInput.value.trim().toLowerCase();
      const items = this.gridContainer.querySelectorAll('.pball-item, .browse-tile');
      items.forEach(item => {
        if (!query || item.dataset.label.includes(query)) {
          item.style.display = 'flex';
        } else {
          item.style.display = 'none';
        }
      });
    }

    getChatInput() {
      return document.querySelector('[data-a-target="chat-input"]');
    }

    insertCommand(ballType) {
      const chatInput = this.getChatInput();
      if (!chatInput) return;
      chatInput.focus();
      this.clearChatInput();
      this.insertText(ballType);
      this.triggerInputEvent(chatInput);
    }

    clearChatInput() {
      const chatInput = this.getChatInput();
      if (chatInput) {
        chatInput.value = '';
        this.triggerInputEvent(chatInput);
      }
    }

    insertText(text) {
      document.execCommand('insertText', false, text);
    }

    triggerInputEvent(element) {
      element.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
    }

    searchAdvancedPokemon(name) {
      if (!name) return;
      this.gridContainer.innerHTML = '<div class="spinner"></div>';
      fetch(`https://pokeapi.co/api/v2/pokemon/${name.toLowerCase()}`)
        .then(response => {
          if (!response.ok) { throw new Error("Pokémon not found"); }
          return response.json();
        })
        .then(data => {
          return fetch(data.species.url)
            .then(res => {
              if (!res.ok) { throw new Error("Species data not found"); }
              return res.json().then(speciesData => ({ data, speciesData }));
            });
        })
        .then(({ data, speciesData }) => {
          return fetch(speciesData.evolution_chain.url)
            .then(res => {
              if (!res.ok) { throw new Error("Evolution chain not found"); }
              return res.json().then(evoData => ({ data, speciesData, evoData }));
            });
        })
        .then(({ data, speciesData, evoData }) => {
          this.displayAdvancedPokemonData(data, speciesData, evoData);
        })
        .catch(err => {
          this.gridContainer.innerHTML = `<div style="padding:12px; color: var(--text-light);">${err.message}</div>`;
        });
    }

displayAdvancedPokemonData(data, speciesData, evoData) {
  this.gridContainer.innerHTML = '';
  const card = document.createElement('div');
  card.className = 'poke-card';

  // Constrain the card width to fit within the pop-up
  card.style.maxWidth = '100%'; // Ensure it doesn't exceed the pop-up width
  card.style.overflowX = 'hidden'; // Prevent horizontal scrolling

  // Header Section
  const header = this.createCardHeader(data);
  card.appendChild(header);

  // Single Column Layout for Advanced Tab
  card.style.display = 'flex';
  card.style.flexDirection = 'column';
  card.style.gap = '16px'; // Reduced gap for compact layout

  // Add sections in a single column
  card.append(
    this.createBasicInfoSection(data),
    this.createStatsRadarChart(data),
    this.createAbilitiesSection(data),
    this.createTypeRelationsGrid(data),
    this.createPokedexEntrySection(speciesData),
    this.createEvolutionVisualization(evoData.chain),
    this.createMovesSection(data)
  );

  // Add Held Items and Forms sections if they exist
  if (data.held_items && data.held_items.length > 0) {
    card.appendChild(this.createHeldItemsSection(data));
  }
  if (data.forms && data.forms.length > 0) {
    card.appendChild(this.createFormsSection(data));
  }

  this.gridContainer.appendChild(card);
}

createCardHeader(data) {
  const header = document.createElement('header');
  header.className = 'poke-card-header';
  header.style.display = 'grid';
  header.style.gridTemplateColumns = 'auto 1fr';
  header.style.gap = '24px';
  header.style.alignItems = 'center';
  header.style.marginBottom = '24px';

  // Image with Badge
  const imgContainer = document.createElement('div');
  imgContainer.style.position = 'relative';
  const img = document.createElement('img');
  img.className = 'poke-image';
  img.style.width = '90px';
  img.style.height = '90px';
  img.style.borderRadius = '16px';
  img.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
  img.src = data.sprites.other?.['official-artwork']?.front_default || data.sprites.front_default;

  // Type Badges
  const typeBadges = document.createElement('div');
  typeBadges.style.display = 'flex';
  typeBadges.style.gap = '4px';
  typeBadges.style.position = 'absolute';
  typeBadges.style.bottom = '-23px';
  typeBadges.style.left = '55%';
  typeBadges.style.transform = 'translateX(-50%)';
  data.types.forEach(type => {
    const badge = document.createElement('span');
    badge.className = 'type-badge';
    badge.textContent = type.type.name.toUpperCase();
    badge.style.background = this.getTypeColor(type.type.name);
    badge.style.padding = '3px 3px';
    badge.style.borderRadius = '5px';
    badge.style.fontSize = '12px';
    badge.style.fontWeight = '700';
    badge.style.color = '#fff';
    badge.style.textShadow = '0 1px 2px rgba(0,0,0,0.3)';
    typeBadges.appendChild(badge);
  });

  imgContainer.append(img, typeBadges);

  // Title Section
  const titleSection = document.createElement('div');
  const title = document.createElement('h1');
  title.className = 'poke-title';
  title.style.fontSize = '32px';
  title.style.margin = '0 0 8px';
  title.textContent = data.name.charAt(0).toUpperCase() + data.name.slice(1);

  const details = document.createElement('div');
  details.style.display = 'grid';
  details.style.gridTemplateColumns = 'repeat(3, auto)';
  details.style.gap = '16px';
  details.innerHTML = `
    <div class="detail-item">
      <span class="detail-label">ID</span>
      <span class="detail-value">#${data.id.toString().padStart(3, '0')}</span>
    </div>
    <div class="detail-item">
      <span class="detail-label">EXP</span>
      <span class="detail-value">${data.base_experience}</span>
    </div>
    <div class="detail-item">
      <span class="detail-label">SPECIES</span>
      <span class="detail-value">${data.species.name}</span>
    </div>
  `;

  titleSection.append(title, details);
  header.append(imgContainer, titleSection);
  return header;
}

createPokedexEntrySection(speciesData) {
  const section = document.createElement('div');
  section.className = 'pokedex-entry-section';
  section.innerHTML = `<h3 class="section-title">POKÉDEX ENTRY</h3>`;

  // Find the English flavor text
  const entry = speciesData.flavor_text_entries.find(e => e.language.name === 'en');
  const flavorText = entry ? entry.flavor_text.replace(/\f|\n/g, ' ') : 'No entry available.';

  // Create the entry container
  const entryContainer = document.createElement('div');
  entryContainer.className = 'pokedex-entry';
  entryContainer.style.background = 'var(--background-darker)';
  entryContainer.style.padding = '16px';
  entryContainer.style.borderRadius = '8px';
  entryContainer.style.fontSize = '14px';
  entryContainer.style.lineHeight = '1.5';
  entryContainer.style.color = 'var(--text-muted)';
  entryContainer.textContent = flavorText;

  section.appendChild(entryContainer);
  return section;
}

createBasicInfoSection(data) {
  const section = document.createElement('div');
  section.className = 'info-grid';
  section.innerHTML = `
    <h3 class="section-title">PHYSICAL TRAITS</h3>
    <div class="metric">
      <i class="icon-height"></i>
      <span class="label">Height</span>
      <span class="value">${data.height / 10}m</span>
    </div>
    <div class="metric">
      <i class="icon-weight"></i>
      <span class="label">Weight</span>
      <span class="value">${data.weight / 10}kg</span>
    </div>
    <div class="metric">
      <i class="icon-stats"></i>
      <span class="label">Total Stats</span>
      <span class="value">${data.stats.reduce((sum, s) => sum + s.base_stat, 0)}</span>
    </div>
  `;
  return section;
}


createAbilitiesSection(data) {
  const section = document.createElement('div');
  section.className = 'abilities-section';
  section.innerHTML = `<h3 class="section-title">ABILITIES</h3>`;

  const abilitiesGrid = document.createElement('div');
  abilitiesGrid.className = 'abilities-grid';
  abilitiesGrid.style.display = 'grid';
  abilitiesGrid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(140px, 1fr))';
  abilitiesGrid.style.gap = '12px';

  data.abilities.forEach(ability => {
    const abilityCard = document.createElement('div');
    abilityCard.className = 'ability-card';
    abilityCard.style.background = 'var(--background-darker)';
    abilityCard.style.padding = '12px';
    abilityCard.style.borderRadius = '8px';
    abilityCard.style.textAlign = 'center';
    abilityCard.style.position = 'relative';

    const abilityName = document.createElement('div');
    abilityName.textContent = ability.ability.name.replace(/-/g, ' ');
    abilityName.style.fontWeight = '500';
    abilityName.style.textTransform = 'capitalize';

    if (ability.is_hidden) {
      const hiddenBadge = document.createElement('div');
      hiddenBadge.textContent = 'Hidden';
      hiddenBadge.style.position = 'absolute';
      hiddenBadge.style.top = '4px';
      hiddenBadge.style.right = '4px';
      hiddenBadge.style.background = '#FF6B6B';
      hiddenBadge.style.color = '#FFF';
      hiddenBadge.style.fontSize = '10px';
      hiddenBadge.style.padding = '2px 6px';
      hiddenBadge.style.borderRadius = '12px';
      abilityCard.appendChild(hiddenBadge);
    }

    abilityCard.appendChild(abilityName);
    abilitiesGrid.appendChild(abilityCard);

    // Add tooltip for ability description
    abilityCard.addEventListener('mouseenter', () => {
      fetch(ability.ability.url)
        .then(res => res.json())
        .then(abilityData => {
          const description = abilityData.effect_entries.find(e => e.language.name === 'en')?.effect || 'No description available.';
          this.showTooltip(abilityCard, description);
        });
    });

    abilityCard.addEventListener('mouseleave', () => {
      this.hideTooltip();
    });
  });

  section.appendChild(abilitiesGrid);
  return section;
}

showTooltip(element, text) {
  if (this.tooltip) this.tooltip.remove();

  this.tooltip = document.createElement('div');
  this.tooltip.className = 'tooltip';
  this.tooltip.textContent = text;
  this.tooltip.style.position = 'absolute';
  this.tooltip.style.background = 'var(--background-dark)';
  this.tooltip.style.color = 'var(--text-light)';
  this.tooltip.style.padding = '8px 12px';
  this.tooltip.style.borderRadius = '6px';
  this.tooltip.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.3)';
  this.tooltip.style.zIndex = '10000';
  this.tooltip.style.maxWidth = '240px';
  this.tooltip.style.fontSize = '14px';

  const rect = element.getBoundingClientRect();
  this.tooltip.style.top = `${rect.bottom + window.scrollY + 8}px`;
  this.tooltip.style.left = `${rect.left + window.scrollX}px`;

  document.body.appendChild(this.tooltip);
}

hideTooltip() {
  if (this.tooltip) {
    this.tooltip.remove();
    this.tooltip = null;
  }
}

createMovesSection(data) {
  const section = document.createElement('div');
  section.className = 'moves-section';
  section.innerHTML = `<h3 class="section-title">MOVES</h3>`;

  const searchInput = document.createElement('input');
  searchInput.type = 'text';
  searchInput.placeholder = 'Search moves...';
  searchInput.style.width = '100%';
  searchInput.style.padding = '8px 12px';
  searchInput.style.marginBottom = '12px';
  searchInput.style.borderRadius = '6px';
  searchInput.style.background = 'var(--background-darker)';
  searchInput.style.color = 'var(--text-light)';
  searchInput.style.border = '1px solid var(--border-color)';

  const movesList = document.createElement('div');
  movesList.className = 'moves-list';
  movesList.style.maxHeight = '200px';
  movesList.style.overflowY = 'auto';
  movesList.style.display = 'grid';
  movesList.style.gap = '8px';

  data.moves.forEach(move => {
    const moveItem = document.createElement('div');
    moveItem.className = 'move-item';
    moveItem.textContent = move.move.name.replace(/-/g, ' ');
    moveItem.style.padding = '8px 12px';
    moveItem.style.background = 'var(--background-darker)';
    moveItem.style.borderRadius = '6px';
    moveItem.style.textTransform = 'capitalize';
    movesList.appendChild(moveItem);
  });

  searchInput.addEventListener('input', () => {
    const query = searchInput.value.trim().toLowerCase();
    Array.from(movesList.children).forEach(move => {
      move.style.display = move.textContent.toLowerCase().includes(query) ? 'block' : 'none';
    });
  });

  section.append(searchInput, movesList);
  return section;
}

createHeldItemsSection(data) {
  const section = document.createElement('div');
  section.className = 'held-items-section';
  section.innerHTML = `<h3 class="section-title">HELD ITEMS</h3>`;

  const itemsGrid = document.createElement('div');
  itemsGrid.className = 'items-grid';
  itemsGrid.style.display = 'grid';
  itemsGrid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(120px, 1fr))';
  itemsGrid.style.gap = '12px';

  data.held_items.forEach(item => {
    const itemCard = document.createElement('div');
    itemCard.className = 'item-card';
    itemCard.textContent = item.item.name.replace(/-/g, ' ');
    itemCard.style.padding = '12px';
    itemCard.style.background = 'var(--background-darker)';
    itemCard.style.borderRadius = '8px';
    itemCard.style.textAlign = 'center';
    itemCard.style.textTransform = 'capitalize';
    itemsGrid.appendChild(itemCard);
  });

  section.appendChild(itemsGrid);
  return section;
}

createFormsSection(data) {
  const section = document.createElement('div');
  section.className = 'forms-section';
  section.innerHTML = `<h3 class="section-title">FORMS</h3>`;

  const formsGrid = document.createElement('div');
  formsGrid.className = 'forms-grid';
  formsGrid.style.display = 'grid';
  formsGrid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(120px, 1fr))';
  formsGrid.style.gap = '12px';

  data.forms.forEach(form => {
    const formCard = document.createElement('div');
    formCard.className = 'form-card';
    formCard.textContent = form.name.replace(/-/g, ' ');
    formCard.style.padding = '12px';
    formCard.style.background = 'var(--background-darker)';
    formCard.style.borderRadius = '8px';
    formCard.style.textAlign = 'center';
    formCard.style.textTransform = 'capitalize';
    formsGrid.appendChild(formCard);
  });

  section.appendChild(formsGrid);
  return section;
}




createStatsRadarChart(data) {
  const section = document.createElement('div');
  section.className = 'stats-radar-card';
  section.innerHTML = `
    <div class="stats-header">
      <h3 class="section-title">Stat Distribution</h3>
      <div class="stats-summary">
        <span class="total-stats">Total: ${data.stats.reduce((sum, s) => sum + s.base_stat, 0)}</span>
        <div class="type-badge" style="background: ${this.getTypeColor(data.types[0].type.name)}">
          ${data.types[0].type.name.toUpperCase()}
        </div>
      </div>
    </div>
  `;

  // Chart container with aspect ratio constraints
  const chartContainer = document.createElement('div');
  chartContainer.className = 'radar-container';
  chartContainer.style.position = 'relative';
  chartContainer.style.height = 'clamp(280px, 35vh, 400px)';
  chartContainer.style.margin = '16px 0';

  const canvas = document.createElement('canvas');
  canvas.setAttribute('aria-label', 'Pokémon stat radar chart');
  canvas.style.touchAction = 'none';

  // Dynamic gradient based on Pokémon type
  const typeColor = this.getTypeColor(data.types[0].type.name);
  const gradient = {
    light: this.hexToRgba(typeColor, 0.3),
    dark: this.hexToRgba(typeColor, 0.1)
  };

  // Chart.js loader with error handling
  if (!window.Chart) {
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/chart.js';
    script.onload = () => this.drawEnhancedRadar(canvas, data, gradient);
    script.onerror = () => this.showChartError(chartContainer);
    document.head.appendChild(script);
  } else {
    this.drawEnhancedRadar(canvas, data, gradient);
  }

  chartContainer.appendChild(canvas);
  section.appendChild(chartContainer);
  return section;
}

drawEnhancedRadar(canvas, data, gradient) {
  try {
    const ctx = canvas.getContext('2d');
    const stats = data.stats.map(s => s.base_stat);
    const labels = data.stats.map(s => ({
      full: s.stat.name.replace(/-/g, ' '),
      short: this.getStatAbbreviation(s.stat.name)
    }));

    // Create gradient fill
    const chartGradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
    chartGradient.addColorStop(0, gradient.light);
    chartGradient.addColorStop(1, gradient.dark);

    new Chart(ctx, {
      type: 'radar',
      data: {
        labels: labels.map(l => l.short),
        datasets: [{
          data: stats,
          backgroundColor: chartGradient,
          borderColor: this.hexToRgba(gradient.light, 0.8),
          borderWidth: 1.8,
          pointBackgroundColor: '#ffffff',
          pointBorderColor: gradient.light,
          pointHoverRadius: 8,
          pointRadius: 4,
          pointHitRadius: 12,
          fill: true
        }]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: {
          duration: 800,
          easing: 'easeOutQuint'
        },
        scales: {
          r: {
            beginAtZero: true,
            max: Math.ceil(Math.max(...stats) / 10) * 10 + 10,
            ticks: {
              display: false,
              count: 5,
              z: 1
            },
            grid: {
              color: 'rgba(255, 255, 255, 0.12)',
              circular: true,
              lineWidth: 0.8
            },
            pointLabels: {
              color: '#ffffff',
              font: {
                size: 13,
                weight: '500'
              },
              callback: (value, index) => [`${value}`, stats[index]],
              padding: 18
            },
            angleLines: {
              color: 'rgba(255, 255, 255, 0.08)',
              lineWidth: 0.8
            }
          }
        },
        plugins: {
          legend: { display: false },
          tooltip: {
            enabled: true,
            intersect: false,
            callbacks: {
              title: (items) => labels[items[0].dataIndex].full,
              label: (context) => `Base Stat: ${context.raw}`
            },
            bodyFont: { size: 13 },
            titleFont: { size: 12 },
            padding: 14,
            backgroundColor: 'rgba(28, 28, 34, 0.96)',
            borderColor: 'rgba(255, 255, 255, 0.12)',
            borderWidth: 1,
            cornerRadius: 8,
            boxShadow: '0 4px 12px rgba(0,0,0,0.24)'
          },
          annotation: {
            annotations: {
              avgLine: {
                type: 'line',
                borderColor: 'rgba(255, 255, 255, 0.2)',
                borderWidth: 1,
                borderDash: [4, 4],
                scaleID: 'r',
                value: stats.reduce((a, b) => a + b, 0) / stats.length
              }
            }
          }
        },
        onHover: (event, elements) => {
          canvas.style.cursor = elements.length ? 'pointer' : 'default';
        }
      }
    });
  } catch (error) {
    this.showChartError(canvas.parentElement);
  }
}

// Helper methods
hexToRgba(hex, alpha = 1) {
  const [r, g, b] = hex.match(/\w\w/g).map(x => parseInt(x, 16));
  return `rgba(${r},${g},${b},${alpha})`;
}

getStatAbbreviation(statName) {
  const abbreviations = {
    'hp': 'HP', 'attack': 'ATK', 'defense': 'DEF',
    'special-attack': 'SP.ATK', 'special-defense': 'SP.DEF',
    'speed': 'SPD'
  };
  return abbreviations[statName] || statName.slice(0, 3).toUpperCase();
}

showChartError(container) {
  container.innerHTML = `
    <div class="chart-error">
      <svg class="error-icon" viewBox="0 0 24 24" width="48" height="48">
        <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>
      </svg>
      <div class="error-message">
        <h4>Chart Unavailable</h4>
        <p>Failed to load stat visualization</p>
      </div>
    </div>
  `;
}

createTypeRelationsGrid(data) {
  const section = document.createElement('div');
  section.className = 'type-relations-grid';
  section.innerHTML = `<h3 class="section-title">TYPE INTERACTIONS</h3>`;

  const grid = document.createElement('div');
  grid.style.display = 'grid';
  grid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(160px, 1fr))';
  grid.style.gap = '12px';

  data.types.forEach(type => {
    const typeCard = document.createElement('div');
    typeCard.className = 'type-card';
    typeCard.innerHTML = `
      <div class="type-header">${type.type.name.toUpperCase()}</div>
      <div class="damage-relations">
        <div class="strengths">
          <h4>STRONG VS</h4>
          <div class="types-list"></div>
        </div>
        <div class="weaknesses">
          <h4>WEAK TO</h4>
          <div class="types-list"></div>
        </div>
      </div>
    `;

          // Add this block to target the header specifically
    const typeHeader = typeCard.querySelector('.type-header');
    typeHeader.style.background = this.getTypeColor(type.type.name);

    // Async load damage relations
    fetch(type.type.url)
      .then(res => res.json())
      .then(typeData => {
        const strengths = typeData.damage_relations.double_damage_to;
        const weaknesses = typeData.damage_relations.double_damage_from;

        strengths.forEach(t => {
          const badge = this.createTypeBadge(t.name);
          typeCard.querySelector('.strengths .types-list').appendChild(badge);
        });

        weaknesses.forEach(t => {
          const badge = this.createTypeBadge(t.name);
          typeCard.querySelector('.weaknesses .types-list').appendChild(badge);
        });
      });

    grid.appendChild(typeCard);
  });

  section.appendChild(grid);
  return section;
}

createTypeBadge(typeName) {
  const badge = document.createElement('span');
  badge.className = 'type-badge small';
  badge.textContent = typeName.toUpperCase();
  badge.style.background = this.getTypeColor(typeName);
  badge.style.padding = '2px 8px';
  badge.style.borderRadius = '12px';
  badge.style.fontSize = '10px';
  return badge;
}

getTypeColor(typeName) {
  const typeColors = {
    normal: '#A8A878',
    fire: '#F08030',
    water: '#6890F0',
    electric: '#F8D030',
    grass: '#78C850',
    ice: '#98D8D8',
    fighting: '#C03028',
    poison: '#A040A0',
    ground: '#E0C068',
    flying: '#A890F0',
    psychic: '#F85888',
    bug: '#A8B820',
    rock: '#B8A038',
    ghost: '#705898',
    dragon: '#7038F8',
    dark: '#705848',
    steel: '#B8B8D0',
    fairy: '#EE99AC'
  };
  return typeColors[typeName] || '#68A090';
}

createEvolutionVisualization(chain) {
  const section = document.createElement('div');
  section.className = 'evolution-chain';
  section.innerHTML = `<h3 class="section-title">EVOLUTION LINE</h3>`;

  const stages = this.parseEvolutionChain(chain);
  const container = document.createElement('div');
  container.style.display = 'flex';
  container.style.justifyContent = 'center';
  container.style.gap = '0px';
  container.style.padding = '16px 0';

  stages.forEach((stage, index) => {
    const stageDiv = document.createElement('div');
    stageDiv.style.display = 'flex';
    stageDiv.style.flexDirection = 'column';
    stageDiv.style.alignItems = 'center';
    stageDiv.style.gap = '8px';

    if (index > 0) {
      const arrow = document.createElement('div');
      arrow.textContent = '→';
      arrow.style.fontSize = '24px';
      arrow.style.opacity = '0.6';
      container.appendChild(arrow);
    }

    const sprite = document.createElement('img');
    sprite.src = `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${stage.id}.png`;
    sprite.style.width = '64px';
    sprite.style.height = '64px';

    const name = document.createElement('div');
    name.textContent = stage.name;
    name.style.fontWeight = '500';

    stageDiv.append(sprite, name);
    container.appendChild(stageDiv);
  });

  section.appendChild(container);
  return section;
}

parseEvolutionChain(chain, result = []) {
  const id = chain.species.url.split('/').slice(-2, -1)[0];
  result.push({ name: chain.species.name, id });

  if (chain.evolves_to.length > 0) {
    chain.evolves_to.forEach(e => this.parseEvolutionChain(e, result));
  }
  return result.filter((v, i, a) => a.findIndex(t => t.id === v.id) === i);
}
  }

  new PokeballHelper();
})();