Twitch Pokemon Community Game Helper - NO TIMER

Twitch PokéBall Drag and Drop to chat! !pokecatch <balltype> Pokemoncommunitygame Timer and Spawn Helper. Pokemon stats and more!

// ==UserScript==
// @name         Twitch Pokemon Community Game Helper - NO TIMER
// @namespace    http://tampermonkey.net/
// @version      19
// @description  Twitch PokéBall Drag and Drop to chat! !pokecatch <balltype> Pokemoncommunitygame Timer and Spawn Helper. Pokemon stats and more!
// @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://i.postimg.cc/T20dR1qH/f547e065261b657c49d5702826b0deca.png', quantity: 0 },
                check: { command: '!pokecheck', tooltip: 'Poke Check', image: 'https://i.postimg.cc/0N7vhyyn/ea9752334aa08543e2f148c0a903719e.png', quantity: 0 },
                poke: { command: '!pokecatch', tooltip: 'Poke Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/poke_ball.png', quantity: 0 },
                great: { command: '!pokecatch greatball', tooltip: 'Great Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/great_ball.png', quantity: 0 },
                ultra: { command: '!pokecatch ultraball', tooltip: 'Ultra Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/ultra_ball.png', quantity: 0 },
                premier: { command: '!pokecatch premierball', tooltip: 'Premier Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/premier_ball.png', quantity: 0 },
                basic: { command: '!pokecatch basicball', tooltip: 'Basic Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/basic_ball.png', quantity: 0 },
                heavy: { command: '!pokecatch heavyball', tooltip: 'Heavy Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/heavy_ball.png', quantity: 0 },
                feather: { command: '!pokecatch featherball', tooltip: 'Feather Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/feather_ball.png', quantity: 0 },
                timer: { command: '!pokecatch timerball', tooltip: 'Timer Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/timer_ball.png', quantity: 0 },
                quick: { command: '!pokecatch quickball', tooltip: 'Quick Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/quick_ball.png', quantity: 0 },
                nest: { command: '!pokecatch nestball', tooltip: 'Nest Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/nest_ball.png', quantity: 0 },
                fast: { command: '!pokecatch fastball', tooltip: 'Fast Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/fast_ball.png', quantity: 0 },
                heal: { command: '!pokecatch healball', tooltip: 'Heal Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/heal_ball.png', quantity: 0 },
                repeat: { command: '!pokecatch repeatball', tooltip: 'Repeat Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/repeat_ball.png', quantity: 0 },
                friend: { command: '!pokecatch friendball', tooltip: 'Friend Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/friend_ball.png', quantity: 0 },
                frozen: { command: '!pokecatch frozenball', tooltip: 'Frozen Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/frozen_ball.png', quantity: 0 },
                night: { command: '!pokecatch nightball', tooltip: 'Night Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/night_ball.png', quantity: 0 },
                phantom: { command: '!pokecatch phantomball', tooltip: 'Phantom Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/phantom_ball.png', quantity: 0 },
                cipher: { command: '!pokecatch cipherball', tooltip: 'Cipher Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/cipher_ball.png', quantity: 0 },
                magnet: { command: '!pokecatch magnetball', tooltip: 'Magnet Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/magnet_ball.png', quantity: 0 },
                net: { command: '!pokecatch netball', tooltip: 'Net Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/net_ball.png', quantity: 0 },
                luxury: { command: '!pokecatch luxuryball', tooltip: 'Luxury Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/luxury_ball.png', quantity: 0 },
                stone: { command: '!pokecatch stoneball', tooltip: 'Stone Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/stone_ball.png', quantity: 0 },
                level: { command: '!pokecatch levelball', tooltip: 'Level Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/level_ball.png', quantity: 0 },
                clone: { command: '!pokecatch cloneball', tooltip: 'Clone Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/clone_ball.png', quantity: 0 },
                sun: { command: '!pokecatch sunball', tooltip: 'Sun Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/sun_ball.png', quantity: 0 },
                fantasy: { command: '!pokecatch fantasyball', tooltip: 'Fantasy Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/fantasy_ball.png', quantity: 0 },
                mach: { command: '!pokecatch machball', tooltip: 'Mach Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/mach_ball.png', quantity: 0 },
                geo: { command: '!pokecatch geoball', tooltip: 'Geo Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/geo_ball.png', quantity: 0 },
                dive: { command: '!pokecatch diveball', tooltip: 'Dive Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/dive_ball.png', quantity: 0 },
                master: { command: '!pokecatch masterball', tooltip: 'Master Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/master_ball.png', quantity: 0 },
                cherish: { command: '!pokecatch cherishball', tooltip: 'Cherish Ball', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/cherish_ball.png', quantity: 0 },
                greatCherish: { command: '!pokecatch greatcherishball', tooltip: 'Great Cherish', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/great_cherish_ball.png', quantity: 0 },
                ultraCherish: { command: '!pokecatch ultracherishball', tooltip: 'Ultra Cherish', image: 'https://poketwitch.bframework.de/static/twitchextension/items/ball/ultra_cherish_ball.png', quantity: 0 }
            };

            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' },
                gift: { command: '!pokegift', tooltip: 'Poke Gift', image: 'https://i.postimg.cc/CxNNP2Zz/Pngtree-gift-box-3d-illustration-6508903.png' },
                v5: { command: ' 5', tooltip: '5', image: 'https://i.postimg.cc/wM8LT5tC/pngaaa-com-3588470.png' },
                v10: { command: ' 10', tooltip: '10', image: 'https://i.postimg.cc/NjJXrv97/pngaaa-com-2133853.png' },
                v25: { command: ' 25', tooltip: '25', image: 'https://i.postimg.cc/wTFySYV8/pngaaa-com-1433934.png' },
                v50: { command: ' 50', tooltip: '50', image: 'https://i.postimg.cc/bNqSCw19/pngaaa-com-973335.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;

// Bind drag methods.
this.dragStart = this.dragStart.bind(this);
this.drag = this.drag.bind(this);
this.dragEnd = this.dragEnd.bind(this);

// Initialize UI and observers.
this.init();
this.gridContainer = document.getElementById('grid-container');
this.tooltip = null;
this.initIntersectionObserver();
this.initInventoryObserver();
}

initIntersectionObserver() {
    const lazyImages = document.querySelectorAll('img.lazy');
    const observer = new IntersectionObserver((entries, obs) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                const img = entry.target;
                img.src = img.dataset.src;
                img.addEventListener('load', () => {
                    img.classList.add('fade-in', 'visible');
                });
                obs.unobserve(img);
            }
        });
    });
    lazyImages.forEach(img => observer.observe(img));
}

init() {
    this.setupStyles();
    this.waitForChat().then(() => {
        this.createInterface();
        // Removed timer element creation.
        this.addEventListeners();
        this.renderGrid();
        this.initSearchButtons();
        this.imageUpdateStarted = false;
        // Removed spawn timer update.
        this.updateInventoryFromDOM();
    });
}

handleSearch(query) {
    switch (this.currentTab) {
        case 'advanced':
            this.searchAdvancedPokemon(query);
            break;
        case 'browse':
            this.renderBrowseGrid();
            break;
        default:
            this.filterGrid();
    }
}

initSearchButtons() {
    document.querySelectorAll('.pball-search-container').forEach(container => {
        const input = container.querySelector('.pball-search');
        if (!input || container.dataset.initialized) return;

        const btnContainer = document.createElement('div');
        btnContainer.className = 'search-buttons';

        const enterButton = Object.assign(document.createElement('button'), {
            className: 'search-enter-button',
            innerHTML: '✔',
            title: 'Search (Enter)',
            onclick: () => this.handleSearch(input.value.trim())
        });

        const clearButton = Object.assign(document.createElement('button'), {
            className: 'pball-clear-btn',
            innerHTML: '×',
            title: 'Clear search',
            style: 'display: none;',
            onclick: () => {
                input.value = '';
                input.focus();
                this.handleSearch('');
                clearButton.style.display = 'none';
            }
        });

        input.addEventListener('input', () => {
            clearButton.style.display = input.value ? 'flex' : 'none';
        });

        input.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') enterButton.click();
        });

        btnContainer.append(enterButton, clearButton);
        container.append(btnContainer);
        container.dataset.initialized = true;
    });
}

createInterface() {
    let inventoryDisplay = document.getElementById('inventory-display');
    if (!inventoryDisplay) {
        inventoryDisplay = document.createElement('div');
        inventoryDisplay.id = 'inventory-display';
        Object.assign(inventoryDisplay.style, {
            position: 'fixed',
            top: '10px',
            right: '10px',
            backgroundColor: '#fff',
            border: '1px solid #ccc',
            padding: '10px',
            zIndex: '9999'
        });
        document.body.appendChild(inventoryDisplay);
    }
    inventoryDisplay.innerHTML = `
        <h3>Inventory</h3>
        <div id="dollars-display">Poke Dollars: 0</div>
        <ul id="balls-list"></ul>
    `;
}

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 = `
  /* ============================================
     Ultra Stunning UI & Theme – Dark Transparent with Soft White Illuminations
     ============================================ */

  /*--------------------------------------------------
    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 {
    --color-primary: rgba(255, 255, 255, 0.8);
    --color-secondary: rgba(255, 255, 255, 0.8);
    --color-accent: rgba(255, 255, 255, 0.6);
    --color-dark: #1c1c1e;
    --color-darker: #141414;
    --color-card: rgba(20, 20, 20, 0.8);
    --color-border: rgba(255, 255, 255, 0.1);
    --color-glass: rgba(255, 255, 255, 0.05);
    --gradient-accent: linear-gradient(90deg, rgba(255,255,255,0.5), rgba(255,255,255,0.2));
    --gradient-bg: radial-gradient(circle at top left, rgba(10,10,10,1), rgba(0,0,0,1));
    --color-text: #fefefe;
    --font-base: 'Roboto', sans-serif;
    --font-led: 'Segment7Standard', monospace;
    --font-label: 'Press Start 2P', cursive;
    --font-size-base: clamp(0.9rem, 1vw + 0.8rem, 1.1rem);
    --border-radius-small: 4px;
    --border-radius-medium: 12px;
    --border-radius-large: 16px;
    --spacing-small: 8px;
    --spacing-medium: 16px;
    --spacing-large: 24px;
    --transition-fast: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    --transition-medium: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    --box-shadow-light: 0 2px 12px rgba(0, 0, 0, 0.4);
    --box-shadow-heavy: 0 4px 20px rgba(0, 0, 0, 0.6);
    --backdrop-blur: blur(10px);
    --neon-glow: drop-shadow(0 0 8px rgba(255,255,255,0.7)) drop-shadow(0 0 8px rgba(255,255,255,0.7));
    --soft-glow: drop-shadow(0 0 10px rgba(255,255,255,0.8));
    --breakpoint-md: 768px;
    --breakpoint-sm: 600px;
  }

  *, *::before, *::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: var(--font-base);
    transition: background var(--transition-fast), color var(--transition-fast);
  }

  html {
    scroll-behavior: smooth;
  }

  body {
    background: var(--gradient-bg);
    color: var(--color-text);
    font-size: var(--font-size-base);
    line-height: 1.5;
  }

  /*--------------------------------------------------
    Scrollbar Styles
  --------------------------------------------------*/
  ::-webkit-scrollbar {
    width: 8px;
    height: 8px;
  }
  ::-webkit-scrollbar-track {
    background: var(--color-darker);
    border-radius: var(--border-radius-medium);
  }
  ::-webkit-scrollbar-thumb {
    background: var(--color-border);
    border-radius: var(--border-radius-medium);
    border: 1px solid var(--color-dark);
  }
  ::-webkit-scrollbar-thumb:hover {
    background: rgba(255,255,255,0.3);
  }
  * {
    scrollbar-width: thin;
    scrollbar-color: var(--color-border) var(--color-darker);
  }

  /*--------------------------------------------------
    Main UI Components
  --------------------------------------------------*/
  .pball-container {
    position: fixed;
    bottom: calc(var(--spacing-large) + 50px);
    right: var(--spacing-medium);
    z-index: 10000;
    pointer-events: none;
    transform: scale(1);
    transform-origin: top right;
    width: fit-content;
    height: fit-content;
  }
  .pball-container > * {
    pointer-events: auto;
  }
  .pball-button {
    position: relative;
    width: 60px;
    height: 60px;
    border-radius: 50%;
    cursor: pointer;
    transition: transform var(--transition-fast), filter var(--transition-fast);
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .pball-button img {
    width: 48px;
    height: 48px;
    object-fit: contain;
    transition: transform var(--transition-fast), filter var(--transition-fast);
  }
  .pball-button:hover,
  .pball-button:focus-visible {
    transform: scale(1.25) rotate(3deg);
    filter: var(--soft-glow);
    outline: none;
  }
  .pball-button:hover img,
  .pball-button:focus-visible img {
    transform: scale(1.1);
  }

  /*--------------------------------------------------
    Panel & Tab System
  --------------------------------------------------*/
  .pball-panel {
    position: absolute;
    bottom: calc(100% + var(--spacing-small));
    right: 0;
    width: 340px;
    height: 500px;
    min-width: 300px;
    min-height: 300px;
    overflow: auto;
    background: var(--color-card);
    backdrop-filter: var(--backdrop-blur);
    border-radius: var(--border-radius-large);
    border: 1px solid var(--color-border);
    box-shadow: var(--box-shadow-heavy);
    opacity: 0;
    visibility: hidden;
    transform: translateY(var(--spacing-small));
    transition: opacity var(--transition-medium), transform var(--transition-medium), visibility var(--transition-medium);
  }
  .pball-panel.active {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }
  .pball-tabs {
    display: flex;
    background: var(--color-darker);
    border-bottom: 1px solid var(--color-border);
    border-top-left-radius: var(--border-radius-large);
    border-top-right-radius: var(--border-radius-large);
    overflow: hidden;
  }
  .pball-tab {
    position: relative;
    flex: 1;
    padding: var(--spacing-small);
    text-align: center;
    font-size: 15px;
    cursor: pointer;
    color: var(--color-text);
    transition: background var(--transition-fast), color var(--transition-fast);
  }
  .pball-tab.active::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: 50%;
    width: 60%;
    height: 3px;
    background: var(--color-primary);
    box-shadow: 0 0 12px var(--color-primary);
    border-radius: 2px;
    transform: translateX(-50%);
  }

  /*--------------------------------------------------
    Search & Input Components
  --------------------------------------------------*/
  .pball-search-container {
    position: relative;
    margin: var(--spacing-medium);
    background: var(--color-card);
    backdrop-filter: var(--backdrop-blur);
    border-radius: var(--border-radius-medium);
    border: 1px solid var(--color-border);
    overflow: hidden;
  }
  .pball-search {
    width: 100%;
    padding: calc(var(--spacing-small) + 2px) var(--spacing-medium);
    padding-right: 70px;
    border: none;
    background: transparent;
    color: var(--color-text);
    font-size: 15px;
    outline: none;
  }
  .search-buttons {
    position: absolute;
    right: var(--spacing-small);
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    gap: var(--spacing-small);
  }

  /*--------------------------------------------------
    Grid Layouts & Item Cards
  --------------------------------------------------*/
  .pball-grid {
    padding: var(--spacing-medium);
    display: grid;
    gap: var(--spacing-medium);
    max-height: 320px;
    overflow-y: auto;
  }
  .pball-panel.shop .pball-grid,
  .pball-panel.catch-shop .pball-grid {
    grid-template-columns: repeat(3, minmax(80px, 1fr));
    justify-content: center;
  }
  .pball-grid.ball-items {
    grid-template-columns: repeat(auto-fit, minmax(50px, 1fr));
    justify-items: center;
  }

  /*--------------------------------------------------
    Item Cards & Catch Tab Overrides
  --------------------------------------------------*/
  .pball-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    border-radius: 50%;
    padding: var(--spacing-small);
    transition: transform var(--transition-fast);
  }
  .pball-item img {
    width: 40px;
    height: 40px;
    transition: transform var(--transition-fast);
    cursor: grab;
  }
  .pball-item img:hover,
  .pball-item img:focus-visible {
    transform: scale(1.2);
    outline: none;
  }
  .pball-item img:active,
  .pball-item img.grabbing {
    filter: var(--neon-glow);
    animation: neonPulse 0.26s infinite alternate;
  }
  @keyframes neonPulse {
    0% {
      filter: drop-shadow(0 0 8px rgba(255,255,255,0.7)) drop-shadow(0 0 8px rgba(255,255,255,0.7));
    }
    100% {
      filter: drop-shadow(0 0 12px rgba(255,255,255,0.7)) drop-shadow(0 0 12px rgba(255,255,255,0.7));
    }
  }
  .pball-item .pball-label {
    margin-top: var(--spacing-small);
    font-size: 14px;
    color: var(--color-text);
    text-align: center;
  }
  .catch-shop .pball-item {
    flex-direction: row;
    justify-content: flex-start;
    align-items: center;
  }
  .catch-shop .pball-item img {
    margin-right: var(--spacing-small);
  }
  .catch-shop .pball-item .pball-label {
    margin-top: 0;
    text-align: left;
  }

  /*--------------------------------------------------
    Pokémon Card Styles - Transparent Version
  --------------------------------------------------*/
  .pokemon-card {
    background: transparent !important;
    border-radius: 12px;
    padding: 10px;
    text-align: center;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    position: relative;
    overflow: hidden;
    cursor: pointer;
  }
  .pokemon-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 6px 16px rgba(255,255,255,0.5);
  }
  .dex-number {
    position: absolute;
    top: 6px;
    left: 8px;
    font-weight: bold;
    background: rgba(0, 0, 0, 0.4);
    padding: 4px 8px;
    border-radius: 8px;
    color: #fff;
  }
  .pokemon-image {
    max-width: 100px;
    max-height: 100px;
    object-fit: contain;
    transition: transform var(--transition-fast);
    background: transparent !important;
  }
  .pokemon-card:hover .pokemon-image,
  .pokemon-card:hover .pball-label,
  .pball-item:hover img {
    filter: drop-shadow(0px 0px 10px rgba(255,255,255,0.8));
    transition: filter var(--transition-fast);
  }

  /*--------------------------------------------------
    Utility Classes
  --------------------------------------------------*/
  .spinner {
    margin: 1.5rem auto;
    border: 4px solid var(--color-border);
    border-top: 4px solid var(--color-primary);
    border-radius: 50%;
    width: 2.8rem;
    height: 2.8rem;
    animation: spin 1s linear infinite;
  }
  .animate-fadeIn {
    opacity: 0;
    transform: translateY(20px);
    animation: fadeInUp 0.5s forwards;
  }

  @keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
  }
  @keyframes fadeInUp {
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }

  /*--------------------------------------------------
    Responsive Adjustments for Auto-Alignment
  --------------------------------------------------*/
  @media (max-width: var(--breakpoint-md)) {
    .pball-panel {
      width: 90%;
      height: auto;
      bottom: var(--spacing-medium);
      right: var(--spacing-medium);
    }
  }
  `;
  document.head.appendChild(style);
}

// -----------------------------
// Helper: Debounce Function
// -----------------------------
debounce(func, wait) {
  let timeout;
  return function(...args) {
    const context = this;
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(context, args), wait);
  };
}

// -----------------------------
// Wait for Chat Input to be Available
// -----------------------------
async waitForChat() {
  return new Promise((resolve) => {
    const chatSelector = '[data-test-selector="chat-input"]';
    if (document.querySelector(chatSelector)) {
      return resolve();
    }
    const observer = new MutationObserver((mutations, obs) => {
      if (document.querySelector(chatSelector)) {
        obs.disconnect();
        resolve();
      }
    });
    observer.observe(document.body, { childList: true, subtree: true });
  });
}

// -----------------------------
// Create Interface Elements
// -----------------------------
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);
}

// -----------------------------
// Create Main Button (Static)
// -----------------------------
createMainButton() {
  const button = document.createElement('div');
  button.className = 'pball-button';

  const icon = document.createElement('img');
  // Use the static pokeball image
  icon.src = this.catchBalls.poke.image;
  icon.style.width = '46px';
  icon.style.height = '46px';
  icon.style.filter = 'drop-shadow(0 2px 4px rgba(0,0,0,0.3))';

  button.appendChild(icon);
  this.buttonIcon = icon;
  return button;
}

// -----------------------------
// Create Panel with Tabs, Search, and Grid
// -----------------------------
createPanel() {
  const panel = document.createElement('div');
  panel.className = 'pball-panel';
  panel.draggable = false;

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

  const createTab = (name, isActive = false) => {
    const tab = document.createElement('div');
    tab.className = `pball-tab${isActive ? ' active' : ''}`;
    tab.textContent = name;
    tab.dataset.tab = name.toLowerCase();
    tab.addEventListener('click', () => {
      this.switchTab(name.toLowerCase());
    });
    return tab;
  };

  tabsContainer.appendChild(createTab('Catch', true));
  tabsContainer.appendChild(createTab('Shop'));
  tabsContainer.appendChild(createTab('Pokemon'));
  tabsContainer.appendChild(createTab('Moves'));
  tabsContainer.appendChild(createTab('Advanced'));

  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.setAttribute('aria-label', 'Clear Search');

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

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

  this.tabContainers = {};
  ['catch', 'shop', 'pokemon', 'moves', 'advanced'].forEach(tabName => {
    const container = document.createElement('div');
    container.className = 'tab-content';
    container.dataset.tab = tabName;
    container.style.display = tabName === 'catch' ? 'block' : 'none';
    this.tabContainers[tabName] = container;
    this.gridContainer.appendChild(container);
  });

  const footer = document.createElement('div');
  Object.assign(footer.style, {
    position: 'absolute',
    bottom: '0',
    left: '0',
    width: '100%',
    padding: '4px 12px',
    borderTop: '1px solid rgba(255,255,255,0.1)',
    fontSize: '15px',
    color: '#666',
    textAlign: 'center'
  });

  const message = document.createTextNode('Like the extension? ');
  const cashTag = document.createElement('span');
  cashTag.textContent = '$yeetsquadcuz';
  Object.assign(cashTag.style, {
    color: '#888',
    fontWeight: '500',
    cursor: 'pointer',
    transition: 'color 0.2s ease',
    display: 'inline-flex',
    alignItems: 'center'
  });
  cashTag.onmouseenter = () => cashTag.style.color = '#aaa';
  cashTag.onmouseleave = () => cashTag.style.color = '#888';

  const cashLogo = document.createElement('img');
  cashLogo.src = 'https://i.postimg.cc/qq9LWcjm/pngegg.png';
  Object.assign(cashLogo.style, {
    width: '14px',
    height: '14px',
    marginLeft: '4px'
  });
  cashTag.appendChild(cashLogo);

  cashTag.addEventListener('click', () => {
    const audio = new Audio('https://www.myinstants.com/media/sounds/yeet.mp3');
    audio.volume = 0.1;
    audio.play();
  });

  footer.appendChild(message);
  footer.appendChild(cashTag);

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

renderGrid() {
  // Cache control elements if they exist
  const movesControls = document.getElementById('moves-controls');
  const pokemonControls = document.getElementById('pokemon-controls');

  // Only remove controls if needed
  if (movesControls && this.currentTab !== 'moves') {
    movesControls.remove();
  }
  if (pokemonControls && this.currentTab !== 'pokemon') {
    pokemonControls.remove();
  }

  // Clear existing classes to avoid duplication
  this.gridContainer.classList.remove('ball-items', 'search-results');

  if (this.currentTab === 'advanced') {
    this.gridContainer.classList.add('search-results');
    this.renderAdvancedInstruction();
  } else if (this.currentTab === 'pokemon') {
    this.gridContainer.classList.add('search-results');
    this.renderPokemon();
  } else if (this.currentTab === 'moves') {
    this.gridContainer.classList.add('search-results');
    this.renderMoves();
  } else {
    // For "catch" and "shop" tabs
    this.gridContainer.classList.add('ball-items');
    // Clear out the container
    this.gridContainer.innerHTML = '';

    const balls = this.currentTab === 'catch' ? this.catchBalls : this.shopBalls;
    const fragment = document.createDocumentFragment();

    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);
      fragment.appendChild(item);
    });

    this.gridContainer.appendChild(fragment);
    this.filterGrid();
  }
}

renderMoves() {
  // Clear the grid container.
  this.gridContainer.innerHTML = '';
  // Render the custom controls panel above the grid.
  this.renderMovesControls();

  // Try to load from localStorage first.
  const cachedMoves = localStorage.getItem('movesList');
  if (cachedMoves) {
    this.movesList = JSON.parse(cachedMoves);
    this.renderMovesGrid();
  } else if (!this.movesList) {
    this.gridContainer.innerHTML += '<div class="spinner"></div>';
    fetch('https://pokeapi.co/api/v2/move?limit=1000')
      .then(response => response.json())
      .then(data => {
        this.movesList = data.results;
        // Cache the moves list in localStorage.
        localStorage.setItem('movesList', JSON.stringify(data.results));
        this.renderMovesGrid();
      })
      .catch(err => {
        this.gridContainer.innerHTML = `<div style="padding:12px; color: var(--text-light);">Error loading moves list</div>`;
      });
  } else {
    // Use the already fetched moves list.
    this.renderMovesGrid();
  }
}


renderMovesControls() {
  let controlsContainer = document.getElementById('moves-controls');
  if (!controlsContainer) {
    controlsContainer = document.createElement('div');
    controlsContainer.id = 'moves-controls';
    Object.assign(controlsContainer.style, {
      display: 'flex',
      flexWrap: 'nowrap',
      justifyContent: 'center', // Changed to center
      alignItems: 'center',
      marginBottom: '10px',
      backgroundColor: '#202020',
      color: '#fff',
      padding: '3px',
      borderRadius: '5px',
      gap: '4px',
      overflowX: 'auto',
      width: '100%' // Ensure full width for proper centering
    });
    this.gridContainer.parentNode.insertBefore(controlsContainer, this.gridContainer);
  }
  controlsContainer.innerHTML = '';

  // --- Filter Buttons ---
  const filterOptions = ['All', 'Physical', 'Special', 'Status'];
  const filterGroup = document.createElement('div');
  filterGroup.style.display = 'flex';
  filterGroup.style.gap = '4px';
  filterGroup.style.margin = '0';
  filterOptions.forEach(option => {
    const btn = document.createElement('button');
    btn.textContent = option;
    btn.dataset.filter = option.toLowerCase();
    Object.assign(btn.style, {
      backgroundColor: '#202020',
      color: '#fff',
      border: '1px solid #666',
      borderRadius: '3px',
      padding: '2px 4px',
      cursor: 'pointer',
      fontSize: '10px',
      height: '24px',
      transition: 'background-color 0.2s ease',
      whiteSpace: 'nowrap'
    });
    btn.addEventListener('click', () => {
      Array.from(filterGroup.children).forEach(child => {
        child.style.backgroundColor = '#202020';
      });
      btn.style.backgroundColor = '#444';
      this.selectedDamageFilter = btn.dataset.filter;
      this.renderMovesGrid();
    });
    filterGroup.appendChild(btn);
  });
  controlsContainer.appendChild(filterGroup);

  // --- Sort Dropdown ---
  const sortContainer = document.createElement('div');
  sortContainer.style.display = 'flex';
  sortContainer.style.alignItems= 'center';
  sortContainer.style.gap = '3px';
  const sortLabel = document.createElement('label');
  sortLabel.textContent = 'Sort:';
  Object.assign(sortLabel.style, {
    fontSize: '10px',
    color: '#fff',
    whiteSpace: 'nowrap',
    marginLeft: '8px' // Added spacing between filter buttons and sort
  });
  const sortSelect = document.createElement('select');
  sortSelect.id = 'moves-sort';
  Object.assign(sortSelect.style, {
    backgroundColor: '#202020',
    color: '#fff',
    border: '1px solid #666',
    borderRadius: '3px',
    padding: '2px',
    fontSize: '10px',
    height: '24px',
    cursor: 'pointer',
    minWidth: '72px'
  });
  sortSelect.innerHTML = `
    <option value="name-asc">A–Z</option>
    <option value="name-desc">Z–A</option>
  `;
  sortSelect.addEventListener('change', () => {
    this.renderMovesGrid();
  });
  sortContainer.appendChild(sortLabel);
  sortContainer.appendChild(sortSelect);
  controlsContainer.appendChild(sortContainer);
}

// Render the moves grid using the custom controls (all moves displayed, with search highlighting)
renderMovesGrid() {
  this.gridContainer.innerHTML = '';
  this.gridContainer.classList.add('browse-container');
  const query = this.searchInput.value.trim().toLowerCase();

  // Get the selected damage filter from the buttons (default to 'all' if not set)
  const damageFilter = this.selectedDamageFilter || 'all';
  const sortOption = document.getElementById('moves-sort')?.value || 'name-asc';

  // Filter moves by search query.
  let filtered = this.movesList.filter(move => move.name.includes(query));

  // Further filter by damage class if not set to 'all'
  if (damageFilter !== 'all') {
    filtered = filtered.filter(move => {
      if (this.moveDetailCache && this.moveDetailCache.has(move.url)) {
        const moveData = this.moveDetailCache.get(move.url);
        return moveData.damage_class.name === damageFilter;
      }
      // Include moves without loaded details by default.
      return true;
    });
  }

  // Sort moves alphabetically.
  if (sortOption === 'name-asc') {
    filtered.sort((a, b) => a.name.localeCompare(b.name));
  } else if (sortOption === 'name-desc') {
    filtered.sort((a, b) => b.name.localeCompare(a.name));
  }

  // Build the grid using a document fragment.
  const fragment = document.createDocumentFragment();
  filtered.forEach(move => {
    const tile = document.createElement('div');
    tile.className = 'browse-tile';
    tile.dataset.label = move.name.toLowerCase();

    // Card container for move info.
    const content = document.createElement('div');
    content.className = 'move-card';
    Object.assign(content.style, {
      padding: '12px',
      borderRadius: '12px',
      background: 'var(--background-darker)',
      display: 'flex',
      flexDirection: 'column',
      alignItems: 'center',
      cursor: 'pointer',
      transition: 'box-shadow 0.2s ease-in-out, transform 0.2s ease-in-out'
    });

    // Hover glow effect.
    content.addEventListener('mouseenter', () => {
      content.style.boxShadow = '0 0 20px rgba(255, 255, 255, 0.6)';
      content.style.transform = 'scale(1.05)';
    });
    content.addEventListener('mouseleave', () => {
      content.style.boxShadow = '0 0 0px rgba(255, 255, 255)';
      content.style.transform = 'scale(1)';
    });

    // Highlight matching search text in move title.
    let titleText = move.name;
    if (query) {
      const regex = new RegExp(`(${query})`, 'gi');
      titleText = move.name.replace(regex, '<mark>$1</mark>');
    }
    const title = document.createElement('div');
    title.className = 'move-title';
    Object.assign(title.style, {
      fontSize: '28px',
      fontWeight: 'bold',
      marginBottom: '8px',
      color: 'var(--text-glow)',
      textShadow: '0 0 5px rgba(255, 255, 255, 0.8)',
      textAlign: 'center'
    });
    title.innerHTML = titleText;

    // Container for badges.
    const badgeContainer = document.createElement('div');
    badgeContainer.className = 'badge-container';
    Object.assign(badgeContainer.style, {
      display: 'flex',
      gap: '8px'
    });

    content.append(title, badgeContainer);
    tile.appendChild(content);

    // Click event to switch to advanced view.
    tile.addEventListener('click', (e) => {
      e.stopPropagation();
      this.panel.classList.add('active');
      this.changeTab('advanced');
      this.searchInput.value = move.name;
      this.searchAdvancedMove(move.name);
    });

    // Fetch move details for badges using caching.
    if (this.moveDetailCache && this.moveDetailCache.has(move.url)) {
      const moveData = this.moveDetailCache.get(move.url);
      const typeBadge = this.createBadge(moveData.type.name, this.getTypeColor(moveData.type.name));
      const damageColor = moveData.damage_class.name === 'physical' ? '#F08030' :
                          moveData.damage_class.name === 'special' ? '#6890F0' : '#A8A878';
      const damageBadge = this.createBadge(moveData.damage_class.name, damageColor);
      badgeContainer.append(typeBadge, damageBadge);
    } else {
      fetch(move.url)
        .then(response => response.json())
        .then(moveData => {
          if (!this.moveDetailCache) this.moveDetailCache = new Map();
          this.moveDetailCache.set(move.url, moveData);
          const typeBadge = this.createBadge(moveData.type.name, this.getTypeColor(moveData.type.name));
          const damageColor = moveData.damage_class.name === 'physical' ? '#F08030' :
                              moveData.damage_class.name === 'special' ? '#6890F0' : '#A8A878';
          const damageBadge = this.createBadge(moveData.damage_class.name, damageColor);
          badgeContainer.append(typeBadge, damageBadge);
        })
        .catch(err => {
          console.error('Error fetching move details for grid tile:', err);
        });
    }
    fragment.appendChild(tile);
  });
  this.gridContainer.appendChild(fragment);

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

// -----------------------------
// Advanced Move Details Section
// -----------------------------
async searchAdvancedMove(moveName) {
  try {
    const normalizedMove = moveName.trim().toLowerCase();
    const response = await fetch(`https://pokeapi.co/api/v2/move/${normalizedMove}`);
    if (!response.ok) throw new Error('Move not found');
    const moveData = await response.json();
    this.displayAdvancedMoveData(moveData);
  } catch (error) {
    console.error("Error fetching move details:", error);
    this.gridContainer.innerHTML = `<div style="padding:12px; color: var(--text-light);">Error loading move details</div>`;
  }
}

displayAdvancedPokemonData(data, speciesData, evoData) {
  this.gridContainer.innerHTML = '';
  const card = document.createElement('div');
  card.className = 'poke-card animate-fadeIn';
  card.style.maxWidth = '100%';
  card.style.overflowX = 'hidden';
  card.style.display = 'flex';
  card.style.flexDirection = 'column';
  card.style.gap = '16px';

  // Assemble the card sections with animations
  card.append(
    this.createCardHeader(data),
    this.createBasicInfoSection(data),
    this.createStatsRadarChart(data),
    this.createAbilitiesSection(data),
    // Remove old type relations grid
    // Instead, add our new advanced analysis section:
    this.createAdvancedBattleAnalysisTab(data),
    this.createPokedexEntrySection(speciesData),
    this.createEvolutionVisualization(evoData.chain),
    this.createMovesSection(data)
  );

  // Append optional sections
  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);
  this.initIntersectionObserver(); // Reinitialize for new lazy images

  // Trigger an animation frame for smoother entrance
  requestAnimationFrame(() => {
    card.classList.add('visible');
  });
}

displayAdvancedMoveData(moveData) {
  this.gridContainer.innerHTML = '';
  const card = document.createElement('div');
  card.className = 'poke-card animate-fadeIn';
  card.style.maxWidth = '100%';
  card.style.overflowX = 'hidden';
  card.style.display = 'flex';
  card.style.flexDirection = 'column';
  card.style.gap = '16px';

  card.append(
    this.createMoveCardHeader(moveData),
    this.createMoveBasicInfoSection(moveData),
    this.createMoveStatsChart(moveData),
    this.createMoveDescriptionSection(moveData)
  );

  this.gridContainer.appendChild(card);
  requestAnimationFrame(() => card.classList.add('visible'));
}

// -----------------------------
// Helper: Create a Badge Element
// -----------------------------
createBadge(text, backgroundColor) {
  const badge = document.createElement('span');
  badge.className = 'type-badge';
  badge.textContent = text.toUpperCase();
  badge.style.background = backgroundColor;
  badge.style.padding = '3px 3px';
  badge.style.borderRadius = '5px';
  badge.style.fontSize = '10px';
  badge.style.fontWeight = '700';
  badge.style.color = '#fff';
  badge.style.textShadow = '0 1px 2px rgba(0,0,0,0.3)';
  return badge;
}

// -----------------------------
// Header: Move Title, Badges, and Numeric Details
// -----------------------------
createMoveCardHeader(moveData) {
  const header = document.createElement('header');
  header.className = 'poke-card-header animate-slideDown';
  header.setAttribute('role', 'banner');

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

  const badgeContainer = document.createElement('div');
  badgeContainer.style.display = 'flex';
  badgeContainer.style.gap = '8px';
  badgeContainer.style.marginBottom = '16px';

  const typeBadge = this.createBadge(moveData.type.name, this.getTypeColor(moveData.type.name));
  const damageColor = moveData.damage_class.name === 'physical'
    ? '#F08030'
    : moveData.damage_class.name === 'special'
      ? '#6890F0'
      : '#A8A878';
  const damageBadge = this.createBadge(moveData.damage_class.name, damageColor);

  badgeContainer.append(typeBadge, damageBadge);

  const details = document.createElement('div');
  details.style.display = 'grid';
  details.style.gridTemplateColumns = 'repeat(4, auto)';
  details.style.gap = '16px';
  details.innerHTML = `
    <div class="detail-item">
      <span class="detail-label">POWER</span>
      <span class="detail-value">${moveData.power !== null ? moveData.power : '—'}</span>
    </div>
    <div class="detail-item">
      <span class="detail-label">ACC</span>
      <span class="detail-value">${moveData.accuracy !== null ? moveData.accuracy : '—'}</span>
    </div>
    <div class="detail-item">
      <span class="detail-label">PP</span>
      <span class="detail-value">${moveData.pp}</span>
    </div>
    <div class="detail-item">
      <span class="detail-label">PRI</span>
      <span class="detail-value">${moveData.priority}</span>
    </div>
  `;

  header.append(title, badgeContainer, details);
  return header;
}

// -----------------------------
// Basic Info Section: Additional Move Details
// -----------------------------
createMoveBasicInfoSection(moveData) {
  const section = document.createElement('section');
  section.className = 'info-grid animate-fadeInUp';
  section.innerHTML = `
    <h3 class="section-title">BASIC INFO</h3>
    <div class="metric">
      <span class="label">CATEGORY</span>
      <span class="value">${moveData.damage_class.name.toUpperCase()}</span>
    </div>
    <div class="metric">
      <span class="label">TARGET</span>
      <span class="value">${moveData.target.name.replace(/-/g, ' ').toUpperCase()}</span>
    </div>
    <div class="metric">
      <span class="label">EFFECT CHANCE</span>
      <span class="value">${moveData.effect_chance !== null ? moveData.effect_chance + '%' : '—'}</span>
    </div>
  `;
  return section;
}

// -----------------------------
// Statistics Chart Section: Visualize Move Data with Chart.js
// -----------------------------
createMoveStatsChart(moveData) {
  const section = document.createElement('section');
  section.className = 'stats-chart-card animate-fadeInUp';
  section.innerHTML = `<h3 class="section-title">STATISTICS</h3>`;

  const chartContainer = document.createElement('div');
  chartContainer.className = 'chart-container';
  chartContainer.style.position = 'relative';
  chartContainer.style.height = '220px';
  chartContainer.style.width = '100%';
  chartContainer.style.margin = '16px 0';

  const canvas = document.createElement('canvas');
  canvas.setAttribute('aria-label', 'Move statistics chart');
  canvas.style.touchAction = 'none';
  canvas.style.width = '100%';
  canvas.style.height = '100%';

  const renderChart = () => {
    const ctx = canvas.getContext('2d');
    const labels = [];
    const dataValues = [];

    if (moveData.power !== null) {
      labels.push('Power');
      dataValues.push(moveData.power);
    }
    if (moveData.accuracy !== null) {
      labels.push('Accuracy');
      dataValues.push(moveData.accuracy);
    }
    labels.push('PP');
    dataValues.push(moveData.pp);
    labels.push('Priority');
    dataValues.push(moveData.priority);

    new Chart(ctx, {
      type: 'bar',
      data: {
        labels: labels,
        datasets: [{
          label: moveData.name.toUpperCase(),
          data: dataValues,
          backgroundColor: [
            'rgba(145,70,255,0.6)',
            'rgba(245,25,255,0.6)',
            'rgba(255,159,64,0.6)',
            'rgba(255,64,64,0.6)'
          ],
          borderColor: [
            'rgba(145,70,255,1)',
            'rgba(245,25,255,1)',
            'rgba(255,159,64,1)',
            'rgba(255,64,64,1)'
          ],
          borderWidth: 1
        }]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        scales: {
          y: { beginAtZero: true }
        },
        plugins: {
          legend: { display: false }
        }
      }
    });
  };

  if (typeof Chart === 'undefined') {
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/chart.js';
    script.onload = renderChart;
    script.onerror = () => {
      console.error("Failed to load Chart.js");
      section.innerHTML += `<div style="padding:12px; color: var(--text-light);">Failed to load chart library.</div>`;
    };
    document.head.appendChild(script);
  } else {
    renderChart();
  }

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

// -----------------------------
// Move Description Section: Display Move Effect Text
// -----------------------------
createMoveDescriptionSection(moveData) {
  const section = document.createElement('section');
  section.className = 'move-description-section animate-fadeInUp';
  section.innerHTML = `<h3 class="section-title">MOVE DESCRIPTION</h3>`;

  const effectEntry = moveData.effect_entries.find(e => e.language.name === 'en');
  const effectText = effectEntry
    ? effectEntry.effect.replace(/\n|\f/g, ' ')
    : 'No description available.';

  const descContainer = document.createElement('div');
  descContainer.className = 'move-description';
  descContainer.style.background = 'var(--background-darker)';
  descContainer.style.padding = '16px';
  descContainer.style.borderRadius = '8px';
  descContainer.style.fontSize = '14px';
  descContainer.style.lineHeight = '1.5';
  descContainer.style.color = 'var(--text-muted)';
  descContainer.textContent = effectText;

  section.appendChild(descContainer);
  return section;
}

// -----------------------------
// Instruction & Browse Sections
// -----------------------------
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));
  const fragment = document.createDocumentFragment();

  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);
    });

    fragment.appendChild(tile);
  });

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

// -----------------------------
// Render Pokémon with Sorting & Filtering via Dropdowns (with BST)
// -----------------------------
renderPokemon() {
  this.gridContainer.innerHTML = '';
  // Render the custom controls panel for Pokémon filters and sort.
  this.renderPokemonControls();
  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.renderPokemonGrid();
      })
      .catch(err => {
        this.gridContainer.innerHTML = `<div style="padding:12px; color: var(--text-light);">Error loading Pokémon list</div>`;
      });
  } else {
    this.renderPokemonGrid();
  }
}

renderPokemonControls() {
  let controlsContainer = document.getElementById('pokemon-controls');
  if (!controlsContainer) {
    controlsContainer = document.createElement('div');
    controlsContainer.id = 'pokemon-controls';
    Object.assign(controlsContainer.style, {
      display: 'flex',
      flexDirection: 'row',
      flexWrap: 'nowrap',
      justifyContent: 'flex-start',
      alignItems: 'center',
      gap: '2px', // Slightly increased from 3px
      marginBottom: '5px',
      backgroundColor: '#202020',
      color: '#fff',
      padding: '1.8px', // Slightly increased from 2px
      borderRadius: '5px',
      overflowX: 'auto'
    });
    this.gridContainer.parentNode.insertBefore(controlsContainer, this.gridContainer);
  }

  controlsContainer.innerHTML = '';

  // Updated helper function with slight size increases
  const createControl = (labelText, id, optionsHTML, onChange) => {
    const container = document.createElement('div');
    Object.assign(container.style, {
      display: 'flex',
      flexDirection: 'column',
      alignItems: 'center',
      gap: '1.2px', // Slightly increased from 1px
      flexShrink: '0',
      minWidth: '50px', // Increased from 48px
      maxWidth: '76px' // Increased from 72px
    });

    const label = document.createElement('div');
    label.textContent = labelText;
    Object.assign(label.style, {
      fontSize: '11px', // Increased from 10px
      textAlign: 'center',
      whiteSpace: 'nowrap',
      padding: '0 2px'
    });

    const select = document.createElement('select');
    select.id = id;
    Object.assign(select.style, {
      backgroundColor: '#202020',
      color: '#fff',
      border: '1px solid #666',
      borderRadius: '3px',
      padding: '2px', // Increased from 1px
      fontSize: '10px', // Increased from 9px
      width: '92%',
      height: '22px', // Increased from 22px
      cursor: 'pointer'
    });
    select.innerHTML = optionsHTML;
    select.addEventListener('change', onChange);
    container.appendChild(label);
    container.appendChild(select);
    return container;
  };

  // Create all controls
  const typeControl = createControl('Type', 'pokemon-type-filter', `
    <option value="all">All</option>
    <option value="normal">Normal</option>
    <option value="fire">Fire</option>
    <option value="water">Water</option>
    <option value="grass">Grass</option>
    <option value="electric">Electric</option>
    <option value="ice">Ice</option>
    <option value="fighting">Fighting</option>
    <option value="poison">Poison</option>
    <option value="ground">Ground</option>
    <option value="flying">Flying</option>
    <option value="psychic">Psychic</option>
    <option value="bug">Bug</option>
    <option value="rock">Rock</option>
    <option value="ghost">Ghost</option>
    <option value="dragon">Dragon</option>
    <option value="dark">Dark</option>
    <option value="steel">Steel</option>
    <option value="fairy">Fairy</option>
  `, () => {
    this.selectedTypeFilter = document.getElementById('pokemon-type-filter').value;
    this.renderPokemonGrid();
  });

  const weightControl = createControl('Weight', 'pokemon-weight-filter', `
    <option value="all">All</option>
    <option value="light">&lt;30kg</option>
    <option value="medium">30-70kg</option>
    <option value="heavy">&gt;70kg</option>
  `, () => {
    this.selectedWeightFilter = document.getElementById('pokemon-weight-filter').value;
    this.renderPokemonGrid();
  });

  const heightControl = createControl('Height', 'pokemon-height-filter', `
    <option value="all">All</option>
    <option value="short">&lt;1m</option>
    <option value="medium">1-2m</option>
    <option value="tall">&gt;2m</option>
  `, () => {
    this.selectedHeightFilter = document.getElementById('pokemon-height-filter').value;
    this.renderPokemonGrid();
  });

  const bstControl = createControl('BST', 'pokemon-bst-filter', `
    <option value="all">All</option>
    <option value="low">&lt;400</option>
    <option value="medium">400-600</option>
    <option value="high">&gt;600</option>
  `, () => {
    this.selectedBSTFilter = document.getElementById('pokemon-bst-filter').value;
    this.renderPokemonGrid();
  });

  const sortControl = createControl('Sort', 'pokemon-sort', `
    <option value="name-asc">Name A-Z</option>
    <option value="name-desc">Name Z-A</option>
    <option value="dex-asc">Dex Ascending</option>
    <option value="dex-desc">Dex Descending</option>
    <option value="bst-asc">BST Ascending</option>
    <option value="bst-desc">BST Descending</option>
  `, () => {
    this.renderPokemonGrid();
  });

  // Append controls together
  controlsContainer.appendChild(typeControl);
  controlsContainer.appendChild(weightControl);
  controlsContainer.appendChild(heightControl);
  controlsContainer.appendChild(bstControl);
  controlsContainer.appendChild(sortControl);
}

/**
 * This function should be called when switching tabs to ensure
 * the sorting controls are removed and re-rendered correctly.
 */
switchTab(tabName) {
  console.log(`Switching to tab: ${tabName}`);

  // Clear and rebuild controls
  this.renderPokemonControls();
  this.renderPokemonGrid(); // Re-render grid to reflect any new filters
}


// Render the Pokémon grid with search, filtering (Type, Weight, Height, BST), and sorting.
renderPokemonGrid() {
  this.gridContainer.innerHTML = '';
  this.gridContainer.classList.add('browse-container');
  const query = this.searchInput.value.trim().toLowerCase();
  let filtered = this.pokemonList.filter(poke => poke.name.includes(query));

  // Filter by Type if selected.
  if (this.selectedTypeFilter && this.selectedTypeFilter !== 'all') {
    filtered = filtered.filter(poke => {
      if (this.pokemonDetailCache && this.pokemonDetailCache.has(poke.url)) {
        const pokeData = this.pokemonDetailCache.get(poke.url);
        return pokeData.types.some(typeInfo => typeInfo.type.name === this.selectedTypeFilter);
      }
      return true;
    });
  }

  // Filter by Weight if selected.
  if (this.selectedWeightFilter && this.selectedWeightFilter !== 'all') {
    filtered = filtered.filter(poke => {
      if (this.pokemonDetailCache && this.pokemonDetailCache.has(poke.url)) {
        const pokeData = this.pokemonDetailCache.get(poke.url);
        const weightKg = pokeData.weight / 10;
        if (this.selectedWeightFilter === 'light') {
          return weightKg < 30;
        } else if (this.selectedWeightFilter === 'medium') {
          return weightKg >= 30 && weightKg <= 70;
        } else if (this.selectedWeightFilter === 'heavy') {
          return weightKg > 70;
        }
      }
      return true;
    });
  }

  // Filter by Height if selected.
  if (this.selectedHeightFilter && this.selectedHeightFilter !== 'all') {
    filtered = filtered.filter(poke => {
      if (this.pokemonDetailCache && this.pokemonDetailCache.has(poke.url)) {
        const pokeData = this.pokemonDetailCache.get(poke.url);
        const heightM = pokeData.height / 10;
        if (this.selectedHeightFilter === 'short') {
          return heightM < 1;
        } else if (this.selectedHeightFilter === 'medium') {
          return heightM >= 1 && heightM <= 2;
        } else if (this.selectedHeightFilter === 'tall') {
          return heightM > 2;
        }
      }
      return true;
    });
  }

  // Filter by BST if selected.
  if (this.selectedBSTFilter && this.selectedBSTFilter !== 'all') {
    filtered = filtered.filter(poke => {
      if (this.pokemonDetailCache && this.pokemonDetailCache.has(poke.url)) {
        const pokeData = this.pokemonDetailCache.get(poke.url);
        const totalStats = pokeData.stats.reduce((sum, stat) => sum + stat.base_stat, 0);
        if (this.selectedBSTFilter === 'low') {
          return totalStats < 400;
        } else if (this.selectedBSTFilter === 'medium') {
          return totalStats >= 400 && totalStats <= 600;
        } else if (this.selectedBSTFilter === 'high') {
          return totalStats > 600;
        }
      }
      return true;
    });
  }

  // Sort the filtered Pokémon.
  const sortOption = document.getElementById('pokemon-sort')?.value || 'name-asc';
  if (sortOption === 'name-asc') {
    filtered.sort((a, b) => a.name.localeCompare(b.name));
  } else if (sortOption === 'name-desc') {
    filtered.sort((a, b) => b.name.localeCompare(a.name));
  } else if (sortOption === 'dex-asc') {
    filtered.sort((a, b) => {
      const idA = parseInt(a.url.match(/\/pokemon\/(\d+)\//)[1]);
      const idB = parseInt(b.url.match(/\/pokemon\/(\d+)\//)[1]);
      return idA - idB;
    });
  } else if (sortOption === 'dex-desc') {
    filtered.sort((a, b) => {
      const idA = parseInt(a.url.match(/\/pokemon\/(\d+)\//)[1]);
      const idB = parseInt(b.url.match(/\/pokemon\/(\d+)\//)[1]);
      return idB - idA;
    });
  } else if (sortOption === 'bst-asc') {
    filtered.sort((a, b) => {
      const bstA = (this.pokemonDetailCache && this.pokemonDetailCache.has(a.url))
        ? this.pokemonDetailCache.get(a.url).stats.reduce((sum, stat) => sum + stat.base_stat, 0)
        : 0;
      const bstB = (this.pokemonDetailCache && this.pokemonDetailCache.has(b.url))
        ? this.pokemonDetailCache.get(b.url).stats.reduce((sum, stat) => sum + stat.base_stat, 0)
        : 0;
      return bstA - bstB;
    });
  } else if (sortOption === 'bst-desc') {
    filtered.sort((a, b) => {
      const bstA = (this.pokemonDetailCache && this.pokemonDetailCache.has(a.url))
        ? this.pokemonDetailCache.get(a.url).stats.reduce((sum, stat) => sum + stat.base_stat, 0)
        : 0;
      const bstB = (this.pokemonDetailCache && this.pokemonDetailCache.has(b.url))
        ? this.pokemonDetailCache.get(b.url).stats.reduce((sum, stat) => sum + stat.base_stat, 0)
        : 0;
      return bstB - bstA;
    });
  }

  // Build the grid as before.
  const fragment = document.createDocumentFragment();
  filtered.forEach(poke => {
    const tile = document.createElement('div');
    tile.className = 'pokemon-card';

    // Hover effects.
    tile.addEventListener('mouseenter', () => {
      tile.style.boxShadow = '0 0 15px rgba(255, 255, 255, 0.6)';
      tile.style.transform = 'scale(1.05)';
    });
    tile.addEventListener('mouseleave', () => {
      tile.style.boxShadow = '0 4px 10px rgba(0, 0, 0, 0.1)';
      tile.style.transform = 'scale(1)';
    });

    // Dex Number (Top Left)
    const dexNumber = document.createElement('div');
    dexNumber.className = 'dex-number';
    tile.appendChild(dexNumber);

    // Image Container
    const imageContainer = document.createElement('div');
    imageContainer.className = 'pokemon-image-container';
    tile.appendChild(imageContainer);

    // Pokémon Image with lazy loading.
    const img = document.createElement('img');
    img.className = 'pokemon-image';
    img.loading = 'lazy';
    imageContainer.appendChild(img);

    // Extract Pokémon ID from URL.
    const idMatch = poke.url.match(/\/pokemon\/(\d+)\//);
    const id = idMatch ? idMatch[1] : '';

    // Try several image sources with fallbacks.
    const imageSources = [
      `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/showdown/${id}.gif`,
      `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/${id}.png`,
      `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/${id}.svg`,
      `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${id}.png`,
      `https://via.placeholder.com/150x150?text=No+Image`
    ];
    let imgIndex = 0;
    function loadNextImage() {
      if (imgIndex >= imageSources.length) return;
      img.src = imageSources[imgIndex];
      img.onerror = () => {
        imgIndex++;
        loadNextImage();
      };
    }
    loadNextImage();

    // Pokémon Name
    const nameLabel = document.createElement('div');
    nameLabel.className = 'pokemon-name';
    nameLabel.textContent = poke.name;
    tile.appendChild(nameLabel);

    // Type Badges (Centered)
    const typesContainer = document.createElement('div');
    typesContainer.className = 'pokemon-types';
    tile.appendChild(typesContainer);

    // Basic Info container (for height, weight, BST, etc.)
    const basicInfoContainer = document.createElement('div');
    basicInfoContainer.className = 'pokemon-info';
    basicInfoContainer.textContent = 'Loading info...';
    tile.appendChild(basicInfoContainer);

    // Function to update the tile with fetched Pokémon details.
    const updatePokemonDetails = (detail) => {
      dexNumber.textContent = `#${detail.id}`;
      typesContainer.innerHTML = '';
      detail.types.forEach(typeInfo => {
        const badge = this.createBadge(
          typeInfo.type.name,
          this.getTypeColor(typeInfo.type.name)
        );
        typesContainer.appendChild(badge);
      });
      const height = (detail.height / 10).toFixed(1);
      const weight = (detail.weight / 10).toFixed(1);
      const baseExp = detail.base_experience;
      const totalStats = detail.stats.reduce((sum, stat) => sum + stat.base_stat, 0);
      basicInfoContainer.innerHTML = `
        <span> ${height}m | ${weight}kg </span>
        <span> Exp: ${baseExp} | BST: ${totalStats} </span>
      `;
    };

    // Fetch details if not already cached.
    if (this.pokemonDetailCache && this.pokemonDetailCache.has(poke.url)) {
      const cachedDetail = this.pokemonDetailCache.get(poke.url);
      updatePokemonDetails(cachedDetail);
    } else {
      fetch(poke.url)
        .then(response => response.json())
        .then(detail => {
          if (!this.pokemonDetailCache) this.pokemonDetailCache = new Map();
          this.pokemonDetailCache.set(poke.url, detail);
          updatePokemonDetails(detail);
        })
        .catch(err => {
          console.error("Error fetching Pokémon detail:", err);
          basicInfoContainer.textContent = 'Info not available';
        });
    }

    // Click event for showing detailed view.
    tile.addEventListener('click', (e) => {
      e.stopPropagation();
      this.panel.classList.add('active');
      this.changeTab('advanced');
      this.searchInput.value = poke.name;
      this.searchAdvancedPokemon(poke.name);
    });

    fragment.appendChild(tile);
  });

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


// -----------------------------
// Add Global Event Listeners
// -----------------------------
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);
    });
  });

  // Use debounced handler for the search input
  const debouncedSearch = this.debounce(() => {
    if (this.currentTab !== 'advanced') {
      this.filterGrid();
      if (this.currentTab === 'pokemon') {
        this.renderPokemonGrid();
      }
    }
    this.clearBtn.style.display = this.searchInput.value.trim() ? 'block' : 'none';
  }, 300);
  this.searchInput.addEventListener('input', debouncedSearch);

  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());
    }
  });
}

// 2. Update changeTab() to handle the new "moves" tab
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 === 'pokemon') {
    this.searchInput.placeholder = 'Filter Pokémon...';
  } else if (tabName === 'moves') {
    this.searchInput.placeholder = 'Filter moves...';
  } 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>`;
          });
      }

createAdvancedBattleAnalysisTab(data) {
  const container = document.createElement('section');
  container.className = 'advanced-battle-analysis animate-fadeInUp';
  container.style.padding = '1rem';
  container.style.borderTop = '1px solid var(--color-border)';

  // Section title
  const title = document.createElement('h3');
  title.className = 'section-title';
  title.textContent = 'Comprehensive Type Interactions';
  container.appendChild(title);

  // Grid container for type cards
  const grid = document.createElement('div');
  grid.style.display = 'grid';
  grid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(250px, 1fr))';
  grid.style.gap = '16px';

  // Create an interactive card for each type
  data.types.forEach(typeObj => {
    const typeCard = document.createElement('div');
    typeCard.className = 'advanced-type-card';
    typeCard.style.border = '1px solid var(--color-border)';
    typeCard.style.borderRadius = '8px';
    typeCard.style.background = 'var(--color-card)';
    typeCard.style.boxShadow = 'var(--box-shadow-light)';
    typeCard.style.overflow = 'hidden';
    typeCard.style.display = 'flex';
    typeCard.style.flexDirection = 'column';

    // Card header with type name and color
    const header = document.createElement('div');
    header.textContent = typeObj.type.name.toUpperCase();
    header.style.background = this.getTypeColor(typeObj.type.name);
    header.style.color = '#fff';
    header.style.padding = '8px';
    header.style.fontWeight = 'bold';
    header.style.textAlign = 'center';
    typeCard.appendChild(header);

    // Tab header container for "Offense" and "Defense"
    const tabHeaderContainer = document.createElement('div');
    tabHeaderContainer.style.display = 'flex';

    // Both buttons now use the card's background
    const offenseTab = document.createElement('button');
    offenseTab.textContent = 'Offense';
    offenseTab.style.flex = '1';
    offenseTab.style.padding = '8px';
    offenseTab.style.border = 'none';
    offenseTab.style.cursor = 'pointer';
    offenseTab.style.background = 'var(--color-card)';
    offenseTab.style.fontWeight = 'bold';
    offenseTab.style.transition = 'border-bottom 0.2s ease';

    const defenseTab = document.createElement('button');
    defenseTab.textContent = 'Defense';
    defenseTab.style.flex = '1';
    defenseTab.style.padding = '8px';
    defenseTab.style.border = 'none';
    defenseTab.style.cursor = 'pointer';
    defenseTab.style.background = 'var(--color-card)';
    defenseTab.style.fontWeight = 'bold';
    defenseTab.style.transition = 'border-bottom 0.2s ease';

    tabHeaderContainer.append(offenseTab, defenseTab);
    typeCard.appendChild(tabHeaderContainer);

    // Create content containers for each tab
    const offenseContent = document.createElement('div');
    offenseContent.style.padding = '8px';
    offenseContent.style.display = 'block';

    const defenseContent = document.createElement('div');
    defenseContent.style.padding = '8px';
    defenseContent.style.display = 'none';

    typeCard.appendChild(offenseContent);
    typeCard.appendChild(defenseContent);

    // Fetch the type data and build the interactive details
    fetch(typeObj.type.url)
      .then(res => res.json())
      .then(typeData => {
        // --- Offense details ---
        const offenseSections = [
          { label: 'Double Damage To', data: typeData.damage_relations.double_damage_to },
          { label: 'Half Damage To', data: typeData.damage_relations.half_damage_to },
          { label: 'No Damage To', data: typeData.damage_relations.no_damage_to }
        ];
        offenseSections.forEach(section => {
          const sectionTitle = document.createElement('h4');
          sectionTitle.textContent = section.label;
          sectionTitle.style.marginBottom = '4px';
          sectionTitle.style.fontSize = '0.9rem';
          offenseContent.appendChild(sectionTitle);

          const sectionContent = document.createElement('div');
          sectionContent.style.display = 'flex';
          sectionContent.style.flexWrap = 'wrap';
          sectionContent.style.gap = '4px';
          if (section.data.length > 0) {
            section.data.forEach(item => {
              const badge = document.createElement('span');
              badge.textContent = item.name.toUpperCase();
              badge.style.background = this.getTypeColor(item.name);
              badge.style.color = '#fff';
              badge.style.padding = '2px 6px';
              badge.style.borderRadius = '4px';
              badge.style.fontSize = '0.8rem';
              sectionContent.appendChild(badge);
            });
          } else {
            const noData = document.createElement('span');
            noData.textContent = 'None';
            noData.style.fontSize = '0.8rem';
            sectionContent.appendChild(noData);
          }
          offenseContent.appendChild(sectionContent);
        });

        // --- Defense details ---
        const defenseSections = [
          { label: 'Double Damage From', data: typeData.damage_relations.double_damage_from },
          { label: 'Half Damage From', data: typeData.damage_relations.half_damage_from },
          { label: 'No Damage From', data: typeData.damage_relations.no_damage_from }
        ];
        defenseSections.forEach(section => {
          const sectionTitle = document.createElement('h4');
          sectionTitle.textContent = section.label;
          sectionTitle.style.marginBottom = '4px';
          sectionTitle.style.fontSize = '0.9rem';
          defenseContent.appendChild(sectionTitle);

          const sectionContent = document.createElement('div');
          sectionContent.style.display = 'flex';
          sectionContent.style.flexWrap = 'wrap';
          sectionContent.style.gap = '4px';
          if (section.data.length > 0) {
            section.data.forEach(item => {
              const badge = document.createElement('span');
              badge.textContent = item.name.toUpperCase();
              badge.style.background = this.getTypeColor(item.name);
              badge.style.color = '#fff';
              badge.style.padding = '2px 6px';
              badge.style.borderRadius = '4px';
              badge.style.fontSize = '0.8rem';
              sectionContent.appendChild(badge);
            });
          } else {
            const noData = document.createElement('span');
            noData.textContent = 'None';
            noData.style.fontSize = '0.8rem';
            sectionContent.appendChild(noData);
          }
          defenseContent.appendChild(sectionContent);
        });
      })
      .catch(err => {
        console.error('Error fetching type interactions:', err);
        offenseContent.textContent = 'Unable to load data.';
        defenseContent.textContent = 'Unable to load data.';
      });

    // --- Tab switching functionality ---
    // Instead of changing the background, we use a bottom border to indicate the active tab.
    offenseTab.addEventListener('click', () => {
      offenseTab.style.borderBottom = '2px solid var(--color-primary)';
      defenseTab.style.borderBottom = 'none';
      offenseContent.style.display = 'block';
      defenseContent.style.display = 'none';
    });
    defenseTab.addEventListener('click', () => {
      defenseTab.style.borderBottom = '2px solid var(--color-primary)';
      offenseTab.style.borderBottom = 'none';
      offenseContent.style.display = 'none';
      defenseContent.style.display = 'block';
    });

    // Set initial active state for offense tab
    offenseTab.style.borderBottom = '2px solid var(--color-primary)';

    grid.appendChild(typeCard);
  });

  container.appendChild(grid);
  return container;
}


  /**
   * Helper function to get a color based on the Pokémon type.
   * @param {string} typeName
   * @returns {string} The color string.
   */
  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';
  }

  // CARD HEADER WITH IMAGE, NAME, AND DETAILS
  createCardHeader(data) {
    const header = document.createElement('header');
    header.className = 'poke-card-header animate-slideDown';
    header.setAttribute('role', 'banner');

    // Image container with lazy loading and type badges
    const imgContainer = document.createElement('div');
    imgContainer.style.position = 'relative';

    const img = document.createElement('img');
    img.className = 'poke-image lazy';
    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.dataset.src =
      data.sprites.other?.['official-artwork']?.front_default ||
      data.sprites.front_default;
    img.src = ''; // initially empty—loaded via IntersectionObserver

    // Type badges positioned over the image
    const typeBadges = document.createElement('div');
    typeBadges.style.display = 'flex';
    typeBadges.style.gap = '4px';
    typeBadges.style.position = 'absolute';
    typeBadges.style.bottom = '60px';
    typeBadges.style.left = '80%';
    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 and details 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;
  }

  // POKÉDEX ENTRY SECTION
  createPokedexEntrySection(speciesData) {
    const section = document.createElement('section');
    section.className = 'pokedex-entry-section animate-fadeInUp';
    section.innerHTML = `<h3 class="section-title">POKÉDEX ENTRY</h3>`;
    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.';
    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;
  }

  // BASIC PHYSICAL INFORMATION SECTION
  createBasicInfoSection(data) {
    const section = document.createElement('section');
    section.className = 'info-grid animate-fadeInUp';
    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;
  }
// ABILITIES SECTION WITH TOOLTIP (using async/await)
createAbilitiesSection(data) {
  const section = document.createElement('section');
  section.className = 'abilities-section animate-fadeInUp';
  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';
    abilityCard.style.cursor = 'pointer';

    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);

    // Use async/await for fetching ability descriptions
    abilityCard.addEventListener('mouseenter', async () => {
      try {
        const res = await fetch(ability.ability.url);
        const abilityData = await res.json();
        const description =
          abilityData.effect_entries.find(e => e.language.name === 'en')?.effect ||
          'No description available.';
        this.showTooltip(abilityCard, description);
      } catch (err) {
        console.error('Error fetching ability data:', err);
      }
    });
    abilityCard.addEventListener('mouseleave', () => {
      this.hideTooltip();
    });
  });
  section.appendChild(abilitiesGrid);
  return section;
}

// Show tooltip with smooth fade-in/out transitions using a solid background color
showTooltip(element, text) {
  if (this.tooltip) this.tooltip.remove();
  this.tooltip = document.createElement('div');
  this.tooltip.className = 'tooltip animate-fadeIn';
  this.tooltip.textContent = text;
  const rect = element.getBoundingClientRect();
  const viewportHeight = window.innerHeight;
  const tooltipHeight = 100; // estimated height
  let topPosition = rect.bottom + 8;
  if (topPosition + tooltipHeight > viewportHeight) {
    topPosition = rect.top - tooltipHeight - 8;
  }
  Object.assign(this.tooltip.style, {
    background: 'var(--color-card)', // Updated to a solid color
    color: 'var(--text-light)',
    borderRadius: '6px',
    padding: '8px 12px',
    position: 'fixed',
    top: `${topPosition}px`,
    left: `${rect.left}px`,
    maxWidth: '240px',
    zIndex: '10000',
    boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
    pointerEvents: 'none',
    opacity: '0',
    transition: 'opacity 0.3s ease'
  });
  document.body.appendChild(this.tooltip);
  requestAnimationFrame(() => {
    this.tooltip.style.opacity = '1';
  });
}

hideTooltip() {
  if (this.tooltip) {
    this.tooltip.style.opacity = '0';
    setTimeout(() => {
      if (this.tooltip) {
        this.tooltip.remove();
        this.tooltip = null;
      }
    }, 300);
  }
}

// MOVES SECTION WITH SEARCH AND DETAILED TOOLTIP
createMovesSection(data) {
  const section = document.createElement('section');
  section.className = 'moves-section animate-fadeInUp';
  section.innerHTML = `<h3 class="section-title">MOVES</h3>`;

  // Search container (same as before)
  const searchContainer = document.createElement('div');
  searchContainer.className = 'pball-search-container';
  const searchInput = document.createElement('input');
  searchInput.className = 'pball-search';
  searchInput.placeholder = 'Search moves...';
  searchInput.setAttribute('aria-label', 'Search moves');
  const searchButtons = document.createElement('div');
  searchButtons.className = 'search-buttons';
  const enterButton = document.createElement('button');
  enterButton.className = 'search-enter-button';
  enterButton.textContent = '✔';
  const clearButton = document.createElement('button');
  clearButton.className = 'pball-clear-btn';
  clearButton.textContent = 'X';

  let searchTimeout;
  const handleSearch = () => {
    clearTimeout(searchTimeout);
    searchTimeout = setTimeout(() => {
      const query = searchInput.value.trim().toLowerCase();
      Array.from(movesList.children).forEach(move => {
        move.style.display = move.textContent.toLowerCase().includes(query)
          ? 'block'
          : 'none';
      });
    }, 300);
  };

  searchInput.addEventListener('input', handleSearch);
  enterButton.addEventListener('click', handleSearch);
  clearButton.addEventListener('click', () => {
    searchInput.value = '';
    handleSearch();
  });
  searchButtons.append(enterButton, clearButton);
  searchContainer.append(searchInput, searchButtons);
  section.append(searchContainer);

  // Moves list container with virtual scroll behavior
  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';
moveItem.style.cursor = 'pointer';

    // Fetch and show move details on hover
    moveItem.addEventListener('mouseenter', async () => {
      try {
        const res = await fetch(move.move.url);
        const moveData = await res.json();
        const effectEntry = moveData.effect_entries.find(
          e => e.language.name === 'en'
        );
        const effectText = effectEntry
          ? effectEntry.short_effect.replace(/\n|\f/g, ' ')
          : 'No description available.';
        // Format move details
        const details = `
Name: ${moveData.name.replace(/-/g, ' ')}
Type: ${moveData.type.name.toUpperCase()}
Category: ${moveData.damage_class.name.toUpperCase()}
Power: ${moveData.power || '—'}
Accuracy: ${moveData.accuracy || '—'}
PP: ${moveData.pp}
Priority: ${moveData.priority}
Effect: ${effectText}
        `;
        this.showTooltip(moveItem, details);
      } catch (error) {
        console.error('Error fetching move data:', error);
      }
    });

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

    movesList.appendChild(moveItem);
  });
  section.appendChild(movesList);
  return section;
}


  // HELD ITEMS SECTION
  createHeldItemsSection(data) {
    const section = document.createElement('section');
    section.className = 'held-items-section animate-fadeInUp';
    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;
  }

  // FORMS SECTION
  createFormsSection(data) {
    const section = document.createElement('section');
    section.className = 'forms-section animate-fadeInUp';
    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;
  }

  // STATS RADAR CHART SECTION (with Chart.js and animated rendering)
  createStatsRadarChart(data) {
    const section = document.createElement('section');
    section.className = 'stats-radar-card animate-fadeInUp';
    section.innerHTML = `
      <div class="stats-header">
        <h3 class="section-title">Stat Distribution</h3>
          </span>
          </div>
        </div>
      </div>
    `;
    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';

    const typeColor = this.getTypeColor(data.types[0].type.name);
    const gradient = {
      light: this.hexToRgba(typeColor, 0.3),
      dark: this.hexToRgba(typeColor, 0.1)
    };

    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)
      }));
      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);
    }
  }

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

  // Abbreviate stat names
  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();
  }

  // Display error if Chart.js fails
  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>
    `;
  }

  // TYPE INTERACTIONS SECTION
  createTypeRelationsGrid(data) {
    const section = document.createElement('section');
    section.className = 'type-relations-grid animate-fadeInUp';
    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>
      `;
      const typeHeader = typeCard.querySelector('.type-header');
      typeHeader.style.background = this.getTypeColor(type.type.name);
      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;
  }

  // Create a small type badge element
  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;
  }

  // Get a color based on the Pokémon type
  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';
  }
// EVOLUTION CHAIN VISUALIZATION WITH MULTIPLE IMAGE SOURCES & FALLBACK HANDLING
createEvolutionVisualization(chain) {
  const section = document.createElement('section');
  section.className = 'evolution-chain animate-fadeInUp';
  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.className = 'lazy';
    sprite.alt = stage.name;
    sprite.style.width = '64px';
    sprite.style.height = '64px';
    // Initially hidden until the image loads
    sprite.style.opacity = '0';

    // Attach onload event to reveal the image once it loads
    sprite.onload = () => {
      sprite.style.opacity = '1';
    };

    // Set multiple image sources and fallback handling
    const imageSources = [
      `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/showdown/${stage.id}.gif`, // Animated
      `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/${stage.id}.png`, // Official
      `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/${stage.id}.svg`, // SVG
      `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${stage.id}.png`, // Default
      `https://via.placeholder.com/150x150?text=No+Image` // Placeholder
    ];

    let imgIndex = 0;
    function loadNextImage() {
      if (imgIndex >= imageSources.length) return;
      sprite.src = imageSources[imgIndex++];
      // If the current image fails to load, try the next one
      sprite.onerror = loadNextImage;
    }
    loadNextImage();

    const name = document.createElement('div');
    name.textContent = stage.name;
    name.style.fontWeight = '500';
    stageDiv.append(sprite, name);
    container.appendChild(stageDiv);
  });
  section.appendChild(container);
  // If you have a lazy loading observer, you may reinitialize it here:
  // this.initIntersectionObserver();
  return section;
}

// Recursively parse the evolution chain into an array of stages
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();
  })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址