Mudanças entre as edições de "Widget:Character.Base"

De Wiki Gla
Ir para navegação Ir para pesquisar
(Criou página com '<style> →‎=========================== BASE STYLES & RESET ===========================: img { pointer-events: none; user-select: none;...')
 
m
Linha 1: Linha 1:
<style>
<!-- ===========================
     /* ===========================
    MAIN SKILLS SYSTEM
      BASE STYLES & RESET
    =========================== -->
  =========================== */
<script>
    img {
     (function () {
         pointer-events: none;
        // ===========================
         user-select: none;
        // Utility Functions
    }
        // ===========================
        const $ = (s, root = document) => root.querySelector(s);
        const $$ = (s, root = document) => Array.from(root.querySelectorAll(s));
        const ensureRemoved = sel => { Array.from(document.querySelectorAll(sel)).forEach(n => n.remove()); };
         const onceFlag = (el, key) => { if (!el) return false; if (el.dataset[key]) return false; el.dataset[key] = '1'; return true; };
         const addOnce = (el, ev, fn) => {
            if (!el) return;
            const attr = `data-wired-${ev}`;
            if (el.hasAttribute(attr)) return;
            el.addEventListener(ev, fn);
            el.setAttribute(attr, '1');
        };


    video {
        // Extra helpers
         max-height: none;
        function filePathURL(fileName) {
    }
            const f = encodeURIComponent((fileName || 'Nada.png').replace(/^Arquivo:|^File:/, ''));
            const base = (window.mw && mw.util && typeof mw.util.wikiScript === 'function')
                ? mw.util.wikiScript()
                : (window.mw && window.mw.config ? (mw.config.get('wgScript') || '/index.php') : '/index.php');
            return `${base}?title=Especial:FilePath/${f}`;
         }
        function getLangKey() {
            const skillsRoot = document.getElementById('skills');
            const raw = (document.documentElement.lang || skillsRoot?.dataset.i18nDefault || 'pt').toLowerCase();
            return raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
        }
        function chooseDescFrom(obj) {
            const lang = getLangKey();
            const pack = obj.desc_i18n || { pt: obj.descPt, en: obj.descEn, es: obj.descEs, pl: obj.descPl };
            return (pack && (pack[lang] || pack.pt || pack.en || pack.es || pack.pl)) || (obj.desc || '');
        }
        function renderSubAttributesFromObj(s, L) {
            const chip = (label, val) => (val ? `<div class="attr-row"><span class="attr-label">${label}</span><span class="attr-value">${val}</span></div>` : '');
            const pve = (s.powerpve || '').toString().trim();
            const pvp = (s.powerpvp || '').toString().trim();
            const en = (s.energy || '').toString().trim();
            const cd = (s.cooldown || '').toString().trim();
            const rows = [
                cd ? chip(L.cooldown, cd) : '',
                en ? chip((en.startsWith('-') ? L.energy_cost : L.energy_gain), en.replace(/^\+?/, '')) : '',
                pve ? chip(L.power, pve) : '',
                pvp ? chip(L.power_pvp, pvp) : '',
            ].filter(Boolean);
            return rows.length ? `<div class="attr-list">${rows.join('')}</div>` : '';
        }


    .mw-body {
        // ===========================
         padding: unset !important;
        // DOM Setup & Initialization
    }
        // ===========================
         const skillsTab = $('#skills');
        const skinsTab = $('#skins');
        const weaponTab = $('#weapon');


    .mw-body-content {
        // Clean up existing elements
         line-height: 1.5;
        ensureRemoved('.top-rail');
    }
        ensureRemoved('.content-card');
        ensureRemoved('.video-placeholder');
         Array.from(document.querySelectorAll('.card-skins-title, .card-skins .card-skins-title, .cardskins-title, .rail-title')).forEach(t => {
            if ((t.textContent || '').trim().toLowerCase().includes('skins')) {
                t.remove();
            }
        });


    .mw-body-content p {
        // Setup skills tab structure
        display: none;
        if (skillsTab) {
    }
            const iconBar = skillsTab.querySelector('.icon-bar');
            if (iconBar) {
                const rail = document.createElement('div');
                rail.className = 'top-rail skills';
                rail.appendChild(iconBar);
                skillsTab.prepend(rail);
            }


    /* ===========================
            const details = skillsTab.querySelector('.skills-details');
      CHARACTER CONTAINER & BACKGROUND
            const videoContainer = skillsTab.querySelector('.video-container');
  =========================== */
            const card = document.createElement('div');
    .banner {
            card.className = 'content-card skills-grid';
         display: none !important;
            if (details) card.appendChild(details);
    }
            if (videoContainer) card.appendChild(videoContainer);
            skillsTab.appendChild(card);
         }


    .character-box {
        // Setup weapon tab structure (mirror skills)
         color: #000;
         if (weaponTab) {
        font-family: 'Noto Sans', sans-serif;
            const iconBarW = weaponTab.querySelector('.icon-bar');
        width: 100%;
            if (iconBarW) {
        margin: auto;
                const railW = document.createElement('div');
        position: relative;
                railW.className = 'top-rail weapon';
        user-select: none;
                railW.appendChild(iconBarW);
        /* background-image será definido inline pelo Módulo (via |background=Arquivo.ext|) */
                weaponTab.prepend(railW);
        background-position: center top;
            }
        background-repeat: no-repeat;
        background-size: cover;
        z-index: 1;
    }


    .character-box p {
            const detailsW = weaponTab.querySelector('.skills-details');
        display: unset;
            const videoContainerW = weaponTab.querySelector('.video-container');
    }
            const cardW = document.createElement('div');
            cardW.className = 'content-card skills-grid';
            if (detailsW) cardW.appendChild(detailsW);
            if (videoContainerW) cardW.appendChild(videoContainerW);
            weaponTab.appendChild(cardW);
        }


    .character-box::before {
        // Setup skins tab structure
        content: "";
        if (skinsTab) {
        position: absolute;
            const wrapper = skinsTab.querySelector('.skins-carousel-wrapper');
        inset: 0;
            const rail = document.createElement('div');
        pointer-events: none;
            rail.className = 'top-rail skins';
        background: linear-gradient(to bottom, rgba(0, 0, 0, .45), rgba(0, 0, 0, .60));
            const title = document.createElement('div');
        z-index: 0;
            title.className = 'rail-title';
    }
            title.textContent = 'Skins & Spotlights';
            rail.appendChild(title);
            if (wrapper) {
                const card = document.createElement('div');
                card.className = 'content-card';
                card.appendChild(wrapper);
                skinsTab.prepend(rail);
                skinsTab.appendChild(card);
            } else {
                skinsTab.prepend(rail);
            }
        }


    /* ===========================
        // ===========================
      CHARACTER HEADER & INFO
        // Video Management
  =========================== */
        // ===========================
    .character-header {
        const iconsBar = $('#skills') ? $('.icon-bar', $('#skills')) : null;
        position: relative;
         const iconItems = iconsBar ? Array.from(iconsBar.querySelectorAll('.skill-icon')) : [];
         overflow: hidden;
         const descBox = $('#skills') ? $('.desc-box', $('#skills')) : null;
         display: flex;
         const videoBox = $('#skills') ? $('.video-container', $('#skills')) : null;
        gap: 10px;
        flex-direction: column;
         z-index: 1;
    }


    .character-art {
        const videosCache = new Map();
         display: none !important;
         const nestedVideoElByIcon = new WeakMap();
         width: 0 !important;
         const barStack = [];
         height: 0 !important;
         let initialBarSnapshot = null;
         overflow: hidden !important;
         let totalVideos = 0, loadedVideos = 0, autoplay = false;
    }


    .character-topbar {
         // Track last clicked skill/subskill for language changes
         display: flex;
         window.__lastActiveSkillIcon = null;
         flex-direction: column;
        align-items: flex-start;
        padding: 8px 20px 0;
        z-index: 1;
    }


    .character-name-box {
        // Placeholder management
         display: flex;
        let placeholder = videoBox ? videoBox.querySelector('.video-placeholder') : null;
         align-items: center;
        let placeholderCreatedOnLoad = false;
         gap: 14px;
         let placeholderConsumed = false;
    }
         if (!placeholder && videoBox) {
            placeholder = document.createElement('div');
            placeholder.className = 'video-placeholder';
            placeholder.innerHTML = '<img src="/images/d/d5/Icon_gla.png" alt="Carregando...">';
            videoBox.appendChild(placeholder);
            placeholderCreatedOnLoad = true;
         } else if (placeholder) {
            placeholderCreatedOnLoad = true;
        }
        if (!placeholder) placeholderConsumed = true;


    .topbar-icon {
        const removePlaceholderSmooth = () => {
        margin-top: 8px;
            if (!placeholder) return;
        width: 100px;
            if (placeholder.classList.contains('fade-out')) return;
        height: 100px;
            placeholder.classList.add('fade-out');
        object-fit: none;
            const onEnd = () => {
    }
                try { placeholder.style.display = 'none'; } catch (e) { }
                placeholder.removeEventListener('transitionend', onEnd);
            };
            placeholder.addEventListener('transitionend', onEnd, { once: true });
            setTimeout(() => { try { placeholder.style.display = 'none'; } catch (e) { } }, 600);
        };


    .character-name {
        const showPlaceholder = () => {
        color: #fff;
            if (!videoBox) return;
        font-size: 56px;
            if (!placeholder || !placeholderCreatedOnLoad) return;
        font-family: 'Orbitron', sans-serif;
            if (placeholderConsumed) return;
        font-weight: 900;
            placeholder.classList.remove('fade-out');
         text-shadow: 0 0 6px #000, 0 0 9px #000;
            placeholder.style.display = 'flex';
    }
            void placeholder.offsetWidth;
         };


    .topbar-description {
        // ===========================
        display: none;
        // Video Loading & Caching
    }
        // ===========================
        if (iconItems.length && videoBox) {
            iconItems.forEach(el => {
                const src = (el.dataset.video || '').trim();
                const idx = el.dataset.index || '';
                if (!src || videosCache.has(idx)) return;


    .class-tags {
                totalVideos++;
        display: flex;
                const v = document.createElement('video');
        gap: 9px;
                v.className = 'skill-video';
        flex-wrap: wrap;
                v.setAttribute('controls', '');
        margin-left: .28rem;
                v.setAttribute('preload', 'auto');
    }
                v.setAttribute('playsinline', '');
                v.style.display = 'none';
                v.dataset.index = idx;
                v.style.width = '100%';
                v.style.maxWidth = '100%';
                v.style.height = 'auto';
                v.style.aspectRatio = '16/9';
                v.style.objectFit = 'cover';
                const source = document.createElement('source');
                source.src = src;
                source.type = 'video/webm';
                v.appendChild(source);


    .class-tag {
                v.addEventListener('canplay', () => {
        background: #353420;
                    loadedVideos++;
        color: #fff;
                    if (loadedVideos === 1) { try { v.pause(); v.currentTime = 0; } catch (e) { } }
        outline: 2px solid #000;
                    const active = $('.skill-icon.active', iconsBar);
        padding: 1px 6px;
                    if (active && active.dataset.index === idx) {
        border-radius: 4px;
                        if (!placeholderConsumed) {
        font-size: .9em;
                            setTimeout(() => { removePlaceholderSmooth(); placeholderConsumed = true; }, 180);
        font-weight: 700;
                        }
        box-shadow: 0 0 2px rgb(0 0 0 / 70%);
                    }
    }
                    if (loadedVideos === totalVideos) autoplay = true;
                });


    .character-info .tier,
                v.addEventListener('error', () => {
    .character-info .class-tag {
                    loadedVideos++;
        font-size: 18px;
                    removePlaceholderSmooth();
        color: #bbb;
                    if (loadedVideos === totalVideos) autoplay = true;
    }
                });


    /* ===========================
                videoBox.appendChild(v);
      TABS SYSTEM
                videosCache.set(idx, v);
  =========================== */
            });
    .character-tabs {
         }
        margin: 4px 0 4px 8px;
        display: flex;
         gap: 12px;
    }


    .tab-btn {
        if (totalVideos === 0 && placeholder) {
        padding: 5px 20px;
            placeholder.style.display = 'none';
        background: #333;
            placeholder.classList.add('fade-out');
        color: #fff;
         }
        border: 2px solid transparent;
        border-radius: 8px;
        font-size: 20px;
        cursor: pointer;
        font-weight: 600;
         line-height: 1;
        transition: background .15s, border-color .15s;
    }


    .tab-btn.active {
        // ===========================
        background: #156bc7;
        // Skill Bar Wiring (root and nested)
        border-color: #156bc7;
        // ===========================
    }
        function wireTooltipsForNewIcons() {
            const tip = document.querySelector('.skill-tooltip');
            if (!tip) return;
            let lockUntil2 = 0;
            Array.from(document.querySelectorAll('.icon-bar .skill-icon')).forEach(icon => {
                if (icon.dataset.tipwired) return;
                icon.dataset.tipwired = '1';
                const label = icon.dataset.nome || icon.dataset.name || icon.title || '';
                if (label && !icon.hasAttribute('aria-label')) icon.setAttribute('aria-label', label);
                if (icon.hasAttribute('title')) icon.removeAttribute('title');
                const img = icon.querySelector('img');
                if (img) { const imgAlt = img.getAttribute('alt') || ''; const imgTitle = img.getAttribute('title') || ''; if (!label && (imgAlt || imgTitle)) icon.setAttribute('aria-label', imgAlt || imgTitle); img.setAttribute('alt', ''); if (img.hasAttribute('title')) img.removeAttribute('title'); }
                const measureAndPos = (el) => {
                    if (!el || tip.getAttribute('aria-hidden') === 'true') return;
                    tip.style.left = '0px'; tip.style.top = '0px';
                    const rect = el.getBoundingClientRect(); const tr = tip.getBoundingClientRect();
                    let left = Math.round(rect.left + (rect.width - tr.width) / 2);
                    left = Math.max(8, Math.min(left, window.innerWidth - tr.width - 8));
                    const coarse = (window.matchMedia && matchMedia('(pointer: coarse)').matches) || (window.innerWidth <= 600);
                    let top = coarse ? Math.round(rect.bottom + 10) : Math.round(rect.top - tr.height - 8);
                    if (top < 8) top = Math.round(rect.bottom + 10);
                    tip.style.left = left + 'px'; tip.style.top = top + 'px';
                };
                const show = (el, text) => { tip.textContent = text || ''; tip.setAttribute('aria-hidden', 'false'); measureAndPos(el); tip.style.opacity = '1'; };
                const hide = () => { tip.setAttribute('aria-hidden', 'true'); tip.style.opacity = '0'; tip.style.left = '-9999px'; tip.style.top = '-9999px'; };
                icon.addEventListener('mouseenter', () => show(icon, (icon.dataset.nome || icon.dataset.name || '')));
                icon.addEventListener('mousemove', () => { if (performance.now() >= lockUntil2) measureAndPos(icon); });
                icon.addEventListener('click', () => { lockUntil2 = performance.now() + 240; measureAndPos(icon); });
                icon.addEventListener('mouseleave', hide);
            });
        }


    .tab-content {
        function showVideoForIcon(el) {
        display: none;
            // Hide all existing videos
        padding: 0 8px 8px;
            if (videoBox) {
        position: relative;
                Array.from(videoBox.querySelectorAll('video.skill-video')).forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
        z-index: 2;
            }
        opacity: 0;
             if (window.__subskills) window.__subskills.hideAll?.(videoBox);
        transform: translateY(12px);
        transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),
             transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        will-change: opacity, transform;
    }


    .tab-content.active {
            const hasIdx = !!el.dataset.index;
        display: block;
            const hasVideo = !!(el.dataset.video && el.dataset.video.trim() !== '');
        opacity: 1;
            if (!videoBox || !hasVideo) {
        transform: translateY(0);
                if (videoBox) { videoBox.style.display = 'none'; if (placeholder) { placeholder.style.display = 'none'; placeholder.classList.add('fade-out'); } }
    }
                return;
            }


    /* Wrapper para transição de altura das abas */
            if (hasIdx && videosCache.has(el.dataset.index)) {
    .tabs-height-wrapper {
                const v = videosCache.get(el.dataset.index);
        position: relative;
                videoBox.style.display = 'block';
        overflow: hidden;
                v.style.display = 'block';
        will-change: height;
                try { v.currentTime = 0; } catch (e) { }
        -webkit-backface-visibility: hidden;
                const suppress = document.body.dataset.suppressSkillPlay === '1';
        backface-visibility: hidden;
                if (!suppress) { if (autoplay) v.play().catch(() => { }); } else { try { v.pause(); } catch (e) { } }
        -webkit-transform: translateZ(0);
                return;
        transform: translateZ(0);
            }
    }


    /* Previne scroll e mudanças bruscas durante transição */
            // Nested or custom icon video
    body.transitioning-tabs {
            let v = nestedVideoElByIcon.get(el);
        overflow-x: hidden !important;
            if (!v) {
    }
                v = document.createElement('video');
                v.className = 'skill-video';
                v.setAttribute('controls', ''); v.setAttribute('preload', 'auto'); v.setAttribute('playsinline', '');
                v.style.display = 'none'; v.style.width = '100%'; v.style.height = 'auto'; v.style.aspectRatio = '16/9'; v.style.objectFit = 'cover';
                const src = document.createElement('source');
                src.src = el.dataset.video; src.type = 'video/webm';
                v.appendChild(src);
                videoBox.appendChild(v);
                nestedVideoElByIcon.set(el, v);
            }
            videoBox.style.display = 'block';
            v.style.display = 'block';
            try { v.currentTime = 0; } catch (e) { }
            const suppress = document.body.dataset.suppressSkillPlay === '1';
            if (!suppress) { if (autoplay) v.play().catch(() => { }); } else { try { v.pause(); } catch (e) { } }
        }


    body.transitioning-tabs .character-box {
        function wireClicksForCurrentBar() {
        overflow: hidden;
            const currIcons = Array.from(iconsBar.querySelectorAll('.skill-icon'));
    }
            currIcons.forEach(el => {
                if (el.dataset.wired) return;
                el.dataset.wired = '1';
                const label = el.dataset.nome || el.dataset.name || '';
                el.setAttribute('aria-label', label);
                if (el.hasAttribute('title')) el.removeAttribute('title');
                const img = el.querySelector('img'); if (img) { img.setAttribute('alt', ''); if (img.hasAttribute('title')) img.removeAttribute('title'); }
                el.addEventListener('click', () => {
                    const skillsRoot = document.getElementById('skills');
                    const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
                    const L = i18nMap[getLangKey()] || i18nMap.pt || { cooldown: 'Recarga', energy_gain: 'Ganho de energia', energy_cost: 'Custo de energia', power: 'Poder', power_pvp: 'Poder PvP', level: 'Nível' };
                    const name = el.dataset.nome || el.dataset.name || '';
                    const level = (el.dataset.level || '').trim();


    .character-box {
                    // Pick description from current language (respects language changes)
        overflow: visible;
                    const lang = getLangKey();
    }
                    const descPack = {
                        pt: el.dataset.descPt || '',
                        en: el.dataset.descEn || '',
                        es: el.dataset.descEs || '',
                        pl: el.dataset.descPl || ''
                    };
                    const chosenDesc = descPack[lang] || descPack.pt || descPack.en || descPack.es || descPack.pl || el.dataset.desc || '';
                    const descHtml = chosenDesc.replace(/'''(.*?)'''/g, '<b>$1</b>');


    /* ===========================
                    const attrsHTML = el.dataset.atr ? renderAttributes(el.dataset.atr) : (el.dataset.subattrs ? renderSubAttributesFromObj(JSON.parse(el.dataset.subattrs), L) : '');
      SKILLS SYSTEM
                    if (descBox) {
  =========================== */
                        descBox.innerHTML = `
    .skills-container {
        <div class="skill-title"><h3>${name}</h3></div>
        display: flex;
        ${level ? `<div class=\"skill-level-line\"><span class=\"attr-label\">${L.level} ${level}</span></div>` : ''}
        gap: 20px;
        ${attrsHTML}
        align-items: flex-start;
        <div class="desc">${descHtml}</div>`;
    }
                    }
                    // Active state
                    currIcons.forEach(i => i.classList.remove('active')); el.classList.add('active'); if (!autoplay && loadedVideos > 0) autoplay = true;
                    // Track for language changes
                    window.__lastActiveSkillIcon = el;
                    // Video
                    showVideoForIcon(el);
                    // Nested skills UX: clicou na skill -> troca a barra se houver subs; se for 'back', volta
                    const subsRaw = el.dataset.subs || el.getAttribute('data-subs');
                    const isBack = el.dataset.back === 'true' || el.getAttribute('data-back') === 'true';
                    if (isBack && barStack.length) {
                        const prev = barStack.pop();
                        renderBarFromItems(prev.items);
                        const btn = document.querySelector('.skills-back-wrapper');
                        if (btn) btn.style.display = barStack.length ? 'block' : 'none';
                        return;
                    }
                    if (subsRaw && subsRaw.trim() !== '') {
                        try { const subs = JSON.parse(subsRaw); pushSubBarFrom(subs, el); } catch { /* no-op */ }
                    }
                });
            });
            // Removido: badges de +/− (abrir/voltar via clique direto no ícone)
            wireTooltipsForNewIcons();
        }


    .skills-details {
        function snapshotCurrentBarItemsFromDOM() {
        flex: 1;
            return Array.from(iconsBar.querySelectorAll('.skill-icon')).map(el => {
        display: flex;
                const img = el.querySelector('img');
        flex-direction: column;
                const iconURL = img ? img.src : '';
        gap: 10px;
                const subsRaw = el.dataset.subs || el.getAttribute('data-subs') || '';
        width: auto;
                let subs = null; try { subs = subsRaw ? JSON.parse(subsRaw) : null; } catch { subs = null; }
        justify-content: center;
                const subattrsRaw = el.dataset.subattrs || '';
    }
                return {
                    name: el.dataset.nome || el.dataset.name || '',
                    level: el.dataset.level || '',
                    desc: el.dataset.desc || '',
                    descPt: el.dataset.descPt || '',
                    descEn: el.dataset.descEn || '',
                    descEs: el.dataset.descEs || '',
                    descPl: el.dataset.descPl || '',
                    attrs: el.dataset.atr || el.dataset.attrs || '',
                    video: el.dataset.video || '',
                    iconURL,
                    subs,
                    subattrsStr: subattrsRaw,
                    noback: el.dataset.noback || null
                };
            });
        }


    /* ===========================
        function ensureBackButton() {
      SKILL ICONS & BAR
            const rail = iconsBar.closest('.top-rail.skills'); if (!rail) return null;
      =========================== */
            // Wrap rail in a dedicated container if not already wrapped
    .icon-bar {
            let wrap = rail.parentElement;
        display: flex;
            if (!wrap || !wrap.classList || !wrap.classList.contains('skills-rail-wrap')) {
        flex-wrap: nowrap;
                const parentNode = rail.parentNode;
        gap: 10px;
                const newWrap = document.createElement('div');
        width: 100%;
                newWrap.className = 'skills-rail-wrap';
        overflow-x: auto;
                // keep layout
        overflow-y: hidden;
                parentNode.insertBefore(newWrap, rail);
        padding: 6px 6px;
                newWrap.appendChild(rail);
        margin-bottom: 6px;
                wrap = newWrap;
        scrollbar-width: thin;
            }
        scrollbar-color: #ababab transparent;
            // Ensure back-wrapper exists as sibling layered behind
        scroll-behavior: smooth;
            let backWrap = wrap.querySelector('.skills-back-wrapper');
        justify-content: flex-start;
            if (!backWrap) {
        align-items: center;
                backWrap = document.createElement('div');
        position: relative;
                backWrap.className = 'skills-back-wrapper';
         z-index: 4;
                const btnInner = document.createElement('button');
    }
                btnInner.className = 'skills-back';
                btnInner.type = 'button';
                btnInner.setAttribute('aria-label', 'Voltar');
                // Use SVG double chevron for a crisper, larger icon
                btnInner.innerHTML = '<svg class="back-chevron" width="100%" height="100%" viewBox="0 0 36 32" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" preserveAspectRatio="xMidYMid meet"><path d="M10 2L4 16L10 30" stroke="currentColor" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 2L14 16L20 30" stroke="currentColor" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M30 2L24 16L30 30" stroke="currentColor" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
                backWrap.appendChild(btnInner);
                wrap.insertBefore(backWrap, rail);
                btnInner.addEventListener('click', () => {
                    if (!barStack.length) return;
                    const prev = barStack.pop();
                    renderBarFromItems(prev.items);
                    backWrap.style.display = barStack.length ? 'block' : 'none';
                    if (!barStack.length) btnInner.classList.remove('peek');
                });
            }
            // Check if current level has noback flag
            const currentLevel = barStack[barStack.length - 1];
            const shouldHide = currentLevel && currentLevel.noback;
            backWrap.style.display = (barStack.length && !shouldHide) ? 'block' : 'none';
            const btnInner = backWrap.querySelector('.skills-back');
            return btnInner;
         }


    /* Back handle for nested skill bars */
        function renderBarFromItems(items) {
    .top-rail.skills {
            iconsBar.innerHTML = '';
        position: relative;
            items.forEach((it, idx) => {
        overflow: visible;
                const node = document.createElement('div'); node.className = 'skill-icon';
        z-index: 10;
                node.dataset.nome = it.name || '';
        transition: width .24s ease, max-width .24s ease;
                if (it.level) node.dataset.level = it.level;
    }
                if (it.desc) node.dataset.desc = it.desc;
 
                if (it.descPt) node.dataset.descPt = it.descPt;
    .skills-back-wrapper {
                if (it.descEn) node.dataset.descEn = it.descEn;
        display: none;
                if (it.descEs) node.dataset.descEs = it.descEs;
        position: absolute;
                if (it.descPl) node.dataset.descPl = it.descPl;
        left: 50px;
                if (it.attrs) node.dataset.atr = it.attrs;
        /* move wrapper far to the right to hide button */
                if (it.video) node.dataset.video = it.video;
        top: 50%;
                if (it.subs) node.dataset.subs = JSON.stringify(it.subs);
        transform: translateY(-50%);
                if (it.subattrsStr) node.dataset.subattrs = it.subattrsStr;
        width: 70px;
                if (it.noback) node.dataset.noback = it.noback;
        height: 95%;
                // mark nested icons (no dataset.index)
        z-index: -1;
                if (!it.index) node.dataset.nested = '1';
        pointer-events: none;
                const img = document.createElement('img'); img.alt = ''; img.src = it.iconURL || (it.icon ? filePathURL(it.icon) : ''); node.appendChild(img);
    }
                iconsBar.appendChild(node);
 
            });
    .skills-back-wrapper .skills-back {
            // Animação de entrada (igual quando entra em subskills)
        display: flex;
            Array.from(iconsBar.children).forEach((c, i) => {
        position: absolute;
                c.style.opacity = '0'; c.style.transform = 'translateY(6px)';
        left: 0;
                requestAnimationFrame(() => {
        top: 0;
                    setTimeout(() => { c.style.transition = 'opacity .18s ease, transform .18s ease'; c.style.opacity = '1'; c.style.transform = 'translateY(0)'; }, i * 24);
        transform: translateX(-97%);
                });
        /* hidden - only tiny peek visible */
            });
        width: 70px;
            wireClicksForCurrentBar(); const b = ensureBackButton(); if (b) b.classList.add('peek');
        height: 100%;
        padding: 0;
        border-radius: 12px 0 0 12px;
        border: 2px solid rgba(255, 255, 255, .08);
        border-right: none;
        background: rgba(28, 28, 34, .95);
        color: rgba(255, 255, 255, 0.8);
        cursor: pointer;
        pointer-events: all;
        align-items: center;
        justify-content: center;
        box-shadow: 0 4px 12px rgba(0, 0, 0, .25);
        -webkit-backdrop-filter: blur(2px);
        backdrop-filter: blur(2px);
        transition: transform .22s ease, background .22s ease, color .22s ease;
        overflow: visible;
    }
 
    .skills-back-wrapper .skills-back:hover {
        transform: translateX(-150%);
        /* slide out further to the left on hover */
        background: rgba(40, 40, 48, .95);
        color: rgba(255, 255, 255, 1.0);
        box-shadow: 0 4px 16px rgba(0, 0, 0, .35), 0 0 8px rgba(255, 255, 255, 0.12);
        border-color: rgba(255, 255, 255, 0.12);
    }
 
    @keyframes backPeekPulse {
 
        0%,
        100% {
            box-shadow: 0 4px 12px rgba(0, 0, 0, .25);
         }
         }


         50% {
         function pushSubBarFrom(subs, parentIconEl) {
             box-shadow: 0 4px 16px rgba(0, 0, 0, .35), 0 0 8px rgba(255, 255, 255, 0.18);
             // Check if parent has noback flag
            const hasNoBack = parentIconEl && (parentIconEl.dataset.noback === 'true' || parentIconEl.getAttribute('data-noback') === 'true');
            // Save current
            barStack.push({ items: snapshotCurrentBarItemsFromDOM(), noback: hasNoBack }); ensureBackButton();
            // Build next items from JSON
            const skillsRoot = document.getElementById('skills');
            const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
            const L = i18nMap[getLangKey()] || i18nMap.pt || { cooldown: 'Recarga', energy_gain: 'Ganho de energia', energy_cost: 'Custo de energia', power: 'Poder', power_pvp: 'Poder PvP', level: 'Nível' };
            const items = (subs || []).map(s => {
                const name = (s.name || s.n || '').trim();
                const desc = chooseDescFrom(s).replace(/'''(.*?)'''/g, '<b>$1</b>');
                const attrsHTML = renderSubAttributesFromObj(s, L);
                // store sub-attrs object JSON to re-render attributes later
                return {
                    name,
                    level: (s.level || '').toString().trim(),
                    desc,
                    descPt: (s.descPt || (s.desc_i18n && s.desc_i18n.pt) || ''),
                    descEn: (s.descEn || (s.desc_i18n && s.desc_i18n.en) || ''),
                    descEs: (s.descEs || (s.desc_i18n && s.desc_i18n.es) || ''),
                    descPl: (s.descPl || (s.desc_i18n && s.desc_i18n.pl) || ''),
                    // keep raw obj for attrs
                    attrs: '',
                    icon: (s.icon || 'Nada.png'),
                    iconURL: filePathURL(s.icon || 'Nada.png'),
                    video: s.video ? filePathURL(s.video) : '',
                    subs: Array.isArray(s.subs) ? s.subs : null,
                    subattrs: s,
                    back: s.back === true ? 'true' : null
                };
            });
            // Render and attach subattrs object via data-subattrs
            iconsBar.innerHTML = '';
            items.forEach((it, iIdx) => {
                const node = document.createElement('div'); node.className = 'skill-icon'; node.dataset.nested = '1';
                node.dataset.nome = it.name || '';
                if (it.level) node.dataset.level = it.level;
                if (it.desc) node.dataset.desc = it.desc;
                if (it.descPt) node.dataset.descPt = it.descPt;
                if (it.descEn) node.dataset.descEn = it.descEn;
                if (it.descEs) node.dataset.descEs = it.descEs;
                if (it.descPl) node.dataset.descPl = it.descPl;
                if (it.video) node.dataset.video = it.video;
                if (it.subs) node.dataset.subs = JSON.stringify(it.subs);
                if (it.subattrs) node.dataset.subattrs = JSON.stringify(it.subattrs);
                if (it.back) node.dataset.back = it.back;
                const img = document.createElement('img'); img.alt = ''; img.src = it.iconURL; node.appendChild(img);
                iconsBar.appendChild(node);
            });
            // pequena animação de entrada
            Array.from(iconsBar.children).forEach((c, i) => {
                c.style.opacity = '0'; c.style.transform = 'translateY(6px)';
                requestAnimationFrame(() => {
                    setTimeout(() => { c.style.transition = 'opacity .18s ease, transform .18s ease'; c.style.opacity = '1'; c.style.transform = 'translateY(0)'; }, i * 24);
                });
            });
            wireClicksForCurrentBar(); const b2 = ensureBackButton(); if (b2) b2.classList.add('peek');
         }
         }
    }


    .skills-back-wrapper .skills-back.peek {
        // Reage à troca de idioma (emitida pelo char translator)
        animation: backPeekPulse 1.4s ease-in-out infinite;
        window.addEventListener('gla:langChanged', () => {
    }
            const skillsRoot = document.getElementById('skills');
            const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
            const lang = getLangKey();
            // Atualiza dataset.desc dos ícones da barra atual (skills)
            Array.from(iconsBar.querySelectorAll('.skill-icon')).forEach(icon => {
                const pack = {
                    pt: icon.dataset.descPt || '',
                    en: icon.dataset.descEn || '',
                    es: icon.dataset.descEs || '',
                    pl: icon.dataset.descPl || ''
                };
                const chosen = (pack[lang] || pack.pt || pack.en || pack.es || pack.pl || icon.dataset.desc || '').trim();
                if (chosen) icon.dataset.desc = chosen;
            });
            // Atualiza descrições salvas no stack (para futuras barras ao voltar)
            barStack.forEach(frame => {
                (frame.items || []).forEach(it => {
                    const pack = { pt: it.descPt, en: it.descEn, es: it.descEs, pl: it.descPl };
                    const chosen = (pack[lang] || pack.pt || pack.en || pack.es || pack.pl || it.desc || '');
                    it.desc = chosen;
                });
            });
            // Atualiza dataset.desc dos ícones da weapon tab (se existir)
            const weaponTab = document.getElementById('weapon');
            if (weaponTab) {
                const weaponIconBar = weaponTab.querySelector('.icon-bar');
                if (weaponIconBar) {
                    Array.from(weaponIconBar.querySelectorAll('.skill-icon')).forEach(icon => {
                        const pack = {
                            pt: icon.dataset.descPt || '',
                            en: icon.dataset.descEn || '',
                            es: icon.dataset.descEs || '',
                            pl: icon.dataset.descPl || ''
                        };
                        const chosen = (pack[lang] || pack.pt || pack.en || pack.es || pack.pl || icon.dataset.desc || '').trim();
                        if (chosen) icon.dataset.desc = chosen;
                    });
                }
            }
        });


    .skills-back-wrapper .skills-back .back-chevron {
        // Wire initial (root) bar and add + badges
         display: block;
         wireClicksForCurrentBar(); const b0 = ensureBackButton(); if (b0) { b0.classList.add('peek'); b0.style.alignSelf = 'stretch'; }
        width: 90%;
        height: 90%;
    }


    .icon-bar::-webkit-scrollbar {
        // ===========================
        height: 6px;
        // Tooltip System
    }
        // ===========================
        (function initSkillTooltip() {
            if (document.querySelector('.skill-tooltip')) return;
            const tip = document.createElement('div');
            tip.className = 'skill-tooltip';
            tip.setAttribute('role', 'tooltip');
            tip.setAttribute('aria-hidden', 'true');
            document.body.appendChild(tip);


    .icon-bar::-webkit-scrollbar-track {
            const lockUntilRef = { value: 0 };
        background: transparent;
    }


    .icon-bar::-webkit-scrollbar-thumb {
            function measureAndPos(el) {
        background: #151515;
                if (!el || tip.getAttribute('aria-hidden') === 'true') return;
        border-radius: 3px;
                tip.style.left = '0px';
    }
                tip.style.top = '0px';
                const rect = el.getBoundingClientRect();
                const tr = tip.getBoundingClientRect();
                let left = Math.round(rect.left + (rect.width - tr.width) / 2);
                left = Math.max(8, Math.min(left, window.innerWidth - tr.width - 8));
                const coarse = (window.matchMedia && matchMedia('(pointer: coarse)').matches) || (window.innerWidth <= 600);
                let top = coarse ? Math.round(rect.bottom + 10) : Math.round(rect.top - tr.height - 8);
                if (top < 8) top = Math.round(rect.bottom + 10);
                tip.style.left = left + 'px';
                tip.style.top = top + 'px';
            }


    /* Icon Variables */
            function show(el, text) {
    :root {
                tip.textContent = text || '';
        --icon-size: 39px;
                tip.setAttribute('aria-hidden', 'false');
        --icon-radius: 8px;
                measureAndPos(el);
        --icon-ring-w: 2px;
                tip.style.opacity = '1';
        --icon-idle: #bbb;
            }
        --icon-active: #FFD257;
        --icon-active-ring: rgba(255, 210, 87, .45);
        --icon-active-glow: rgba(255, 210, 87, .35);
        --skill-pane-height: unset;
    }


    .icon-bar .skill-icon {
            function hide() {
        width: var(--icon-size);
                tip.setAttribute('aria-hidden', 'true');
        height: var(--icon-size);
                tip.style.opacity = '0';
        position: relative;
                tip.style.left = '-9999px';
        flex: 0 0 auto;
                tip.style.top = '-9999px';
        border-radius: var(--icon-radius);
            }
        overflow: hidden;
        contain: paint;
        margin-top: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        -webkit-tap-highlight-color: transparent;
        background-clip: padding-box;
    }


    /* small + badge to indicate nested subs */
            // Export to global for subskills to use
    .icon-bar .skill-icon .sub-badge {
            window.__globalSkillTooltip = {
        position: absolute;
                show,
        right: 2px;
                hide,
        bottom: 2px;
                measureAndPos,
        width: 16px;
                lockUntil: lockUntilRef
        height: 16px;
            };
        border-radius: 50%;
        background: #156bc7;
        color: #fff;
        font-weight: 900;
        font-size: 12px;
        line-height: 16px;
        text-align: center;
        box-shadow: 0 2px 6px rgba(0, 0, 0, .4);
        cursor: pointer;
        z-index: 3;
        user-select: none;
    }


    /* back badge (−) to return to previous bar */
            Array.from(document.querySelectorAll('.icon-bar .skill-icon')).forEach(icon => {
    .icon-bar .skill-icon .back-badge {
                if (icon.dataset.tipwired) return;
        position: absolute;
                icon.dataset.tipwired = '1';
        right: 2px;
                const label = icon.dataset.nome || icon.dataset.name || icon.title || '';
        bottom: 2px;
                if (label && !icon.hasAttribute('aria-label')) icon.setAttribute('aria-label', label);
        width: 16px;
                if (icon.hasAttribute('title')) icon.removeAttribute('title');
        height: 16px;
        border-radius: 50%;
        background: #c74615;
        color: #fff;
        font-weight: 900;
        font-size: 14px;
        line-height: 16px;
        text-align: center;
        box-shadow: 0 2px 6px rgba(0, 0, 0, .4);
        cursor: pointer;
        z-index: 3;
        user-select: none;
    }


    .icon-bar .skill-icon img {
                const img = icon.querySelector('img');
        display: block;
                if (img) {
        width: 100%;
                    const imgAlt = img.getAttribute('alt') || '';
        height: 100%;
                    const imgTitle = img.getAttribute('title') || '';
        object-fit: cover;
                    if (!label && (imgAlt || imgTitle)) icon.setAttribute('aria-label', imgAlt || imgTitle);
        border-radius: inherit;
                    img.setAttribute('alt', '');
        clip-path: inset(0 round var(--icon-radius));
                    if (img.hasAttribute('title')) img.removeAttribute('title');
        -webkit-clip-path: inset(0 round var(--icon-radius));
                }
        will-change: transform;
        backface-visibility: hidden;
        transform: translateZ(0);
        transition: transform .12s ease;
    }


    /* Icon Ring Effects */
                icon.addEventListener('mouseenter', () => show(icon, label));
    .icon-bar .skill-icon::after {
                icon.addEventListener('mousemove', () => { if (performance.now() >= lockUntilRef.value) measureAndPos(icon); });
        content: "";
                icon.addEventListener('click', () => { lockUntilRef.value = performance.now() + 240; measureAndPos(icon); });
        position: absolute;
                icon.addEventListener('mouseleave', hide);
        inset: 0;
            });
        border-radius: inherit;
        box-shadow: inset 0 0 0 var(--icon-ring-w) var(--icon-idle);
        pointer-events: none;
        z-index: 2;
        transition: box-shadow .14s ease;
    }


    .icon-bar .skill-icon::before {
            // Also wire subskill icons present at load (kept in sync when sub-rail opens)
        content: "";
            Array.from(document.querySelectorAll('.subskills-rail .subicon')).forEach(sub => {
        position: absolute;
                if (sub.dataset.tipwired) return;
        inset: 0;
                sub.dataset.tipwired = '1';
        border-radius: inherit;
                const label = sub.getAttribute('title') || sub.getAttribute('aria-label') || '';
        pointer-events: none;
                if (label && !sub.hasAttribute('aria-label')) sub.setAttribute('aria-label', label);
        z-index: 1;
                if (sub.hasAttribute('title')) sub.removeAttribute('title');
        box-shadow: none;
                sub.addEventListener('mouseenter', () => show(sub, label));
        opacity: 0;
                sub.addEventListener('mousemove', () => { if (performance.now() >= lockUntilRef.value) measureAndPos(sub); });
        transition: opacity .14s ease, box-shadow .14s ease;
                sub.addEventListener('click', () => { lockUntilRef.value = performance.now() + 240; measureAndPos(sub); });
    }
                sub.addEventListener('mouseleave', hide);
            });


    .icon-bar .skill-icon:hover::after {
            window.addEventListener('scroll', () => {
        box-shadow: inset 0 0 0 var(--icon-ring-w) #e0e0e0;
                const visible = document.querySelector('.skill-tooltip[aria-hidden="false"]');
    }
                if (!visible) return;
                const target = document.querySelector('.subskills-rail .subicon:hover')
                    || document.querySelector('.subskills-rail .subicon.active')
                    || document.querySelector('.icon-bar .skill-icon:hover')
                    || document.querySelector('.icon-bar .skill-icon.active');
                measureAndPos(target);
            }, true);
            window.addEventListener('resize', () => {
                const target = document.querySelector('.subskills-rail .subicon:hover')
                    || document.querySelector('.subskills-rail .subicon.active')
                    || document.querySelector('.icon-bar .skill-icon:hover')
                    || document.querySelector('.icon-bar .skill-icon.active');
                measureAndPos(target);
            });
        })();


    .icon-bar .skill-icon.active::after {
        // ===========================
        box-shadow: inset 0 0 0 var(--icon-ring-w) var(--icon-active);
        // Tab System (com transição suave de altura)
    }
        // ===========================
        (function initTabs() {
            const tabs = Array.from(document.querySelectorAll('.tab-btn'));
            if (!tabs.length) return;
            const contents = Array.from(document.querySelectorAll('.tab-content'));
            const characterBox = document.querySelector('.character-box');


    .icon-bar .skill-icon.active::before {
            // Cria o wrapper UMA VEZ no início
        box-shadow: 0 0 10px 2px var(--icon-active-glow), 0 0 0 4px var(--icon-active-ring);
            let wrapper = characterBox.querySelector('.tabs-height-wrapper');
        opacity: 1;
            if (!wrapper) {
    }
                wrapper = document.createElement('div');
                wrapper.className = 'tabs-height-wrapper';


    .icon-bar .skill-icon.active img {
                // Move os conteúdos das abas para dentro do wrapper
        transform: scale(1.10);
                contents.forEach(c => {
    }
                    wrapper.appendChild(c);
                });


    @media (prefers-reduced-motion: reduce) {
                // Encontra onde inserir o wrapper (após as tabs)
        .icon-bar .skill-icon {
                const tabsElement = characterBox.querySelector('.character-tabs');
            transition: none;
                if (tabsElement && tabsElement.nextSibling) {
        }
                    characterBox.insertBefore(wrapper, tabsElement.nextSibling);
    }
                } else {
                    characterBox.appendChild(wrapper);
                }
            }


    /* ===========================
            // Função para animar a altura suavemente (retorna Promise)
      SKILL DESCRIPTION & CONTENT
            // NOVA ESTRATÉGIA: aba já está visível mas invisível (opacity:0, visibility:hidden)
      =========================== */
            async function smoothHeightTransition(fromTab, toTab) {
    .skill-title {
                if (!wrapper) return Promise.resolve();
        margin: 0 0 12px;
        display: flex;
        justify-content: center;
        align-items: center;
    }


    .skill-title h3 {
                // Salva o scroll atual
        margin: 0;
                const scrollY = window.scrollY;
        width: 100%;
        text-align: center;
        font-size: 1.6em;
        color: #fff;
    }


    .desc-box {
                // Mede a altura ATUAL
        padding: 12px 18px;
                const currentHeight = wrapper.getBoundingClientRect().height;
        background: transparent;
        border-radius: 6px;
        position: relative;
        box-shadow: none;
        color: #fff;
        transition: all .3s ease;
        z-index: 2;
        display: flex;
        flex-direction: column;
        overflow: hidden;
        text-shadow: none;
        height: auto;
        min-height: 0;
    }


    .desc-box h3 {
                // A aba toTab JÁ está display:block mas invisível
        font-size: 2.7em;
                // Aguarda ela renderizar COMPLETAMENTE na posição real
        margin: 0;
                await new Promise((resolve) => {
        text-align: center;
                    const videoContainers = toTab.querySelectorAll('.video-container');
        padding-top: 0;
                    const contentCard = toTab.querySelector('.content-card');
    }


    .desc {
                    if (videoContainers.length === 0) {
        font-size: 1.2em !important;
                        // Sem vídeos, aguarda 3 frames
        line-height: 1.7 !important;
                        requestAnimationFrame(() => {
        letter-spacing: .01em;
                            requestAnimationFrame(() => {
        overflow-x: hidden;
                                requestAnimationFrame(() => resolve());
        -webkit-overflow-scrolling: touch;
                            });
        scrollbar-gutter: stable;
                        });
        margin-top: 5px;
                        return;
        padding-right: 8px;
                    }
        color: #f1efe9;
        overflow-wrap: anywhere;
        word-break: break-word;
        white-space: normal;
        flex: 0 0 auto !important;
    }


    .desc b,
                    // COM vídeos: aguarda até que TUDO esteja renderizado
    .desc strong {
                    // Faz polling da altura até estabilizar
        font-weight: 700;
                    let lastHeight = 0;
        color: #fff;
                    let stableCount = 0;
    }
                    const checksNeeded = 3; // Altura precisa ficar estável por 3 checks
                    let totalChecks = 0;
                    const maxChecks = 15; // Máximo 15 checks (750ms)


    .desc::-webkit-scrollbar {
                    function checkStability() {
        width: 7px;
                        totalChecks++;
        height: 7px;
    }


    .desc::-webkit-scrollbar-thumb {
                        // Mede altura atual da aba
        background: #156bc7;
                        const currentTabHeight = toTab.scrollHeight;
        border-radius: 10px;
    }


    .desc::-webkit-scrollbar-track {
                        // Verifica se estabilizou
        background: #151515a8;
                        if (Math.abs(currentTabHeight - lastHeight) < 5) {
        border-radius: 10px;
                            stableCount++;
    }
                        } else {
                            stableCount = 0; // Resetar se mudou
                        }


    /* ===========================
                        lastHeight = currentTabHeight;
      ATTRIBUTES & STATS
      =========================== */
    .attrs,
    .attr-list {
        display: block;
    }


    .desc-box .attrs,
                        // Se estável por N checks ou atingiu máximo, resolve
    .desc-box .attr-list,
                        if (stableCount >= checksNeeded || totalChecks >= maxChecks) {
    .desc-box .attrs *,
                            resolve();
    .desc-box .attr-list * {
                        } else {
        text-shadow: none;
                            // Continua checando a cada 50ms
        font-family: 'Noto Sans', sans-serif;
                            setTimeout(checkStability, 50);
    }
                        }
                    }


    .attrs__row,
                    // Inicia checagem após um pequeno delay
    .attr-row {
                    setTimeout(checkStability, 50);
        display: flex;
                });
        align-items: center;
        gap: 8px;
        min-height: 22px;
        line-height: 1.2;
    }


    .attrs__row--empty,
                // AGORA mede a altura REAL (após tudo renderizado)
    .attr-row.is-empty {
                const nextHeight = toTab.getBoundingClientRect().height;
        display: none;
                const finalHeight = Math.max(nextHeight, 100);
    }


    .attrs__label,
                // Se alturas são similares (< 30px diferença), não anima
    .attr-label {
                if (Math.abs(finalHeight - currentHeight) < 30) {
        font-weight: 700;
                    wrapper.style.height = '';
        color: #f0c87b;
                    return Promise.resolve();
        font-size: .98rem;
                }
        white-space: nowrap;
        margin: 0;
    }


    .attrs__value,
                // Define altura inicial fixa
    .attr-value {
                wrapper.style.overflow = 'hidden';
        color: #fff;
                wrapper.style.height = currentHeight + 'px';
        font-weight: 800;
        font-size: .98rem;
        letter-spacing: .01em;
        margin: 0;
    }


    /* ===========================
                // Força reflow
      VIDEO CONTAINER
                wrapper.offsetHeight;
      =========================== */
    .video-container {
        position: relative;
        width: 100%;
        max-width: 100%;
        background: transparent;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 10px;
        box-shadow: none;
        overflow: hidden;
        padding: 0;
        z-index: 2;
    }


    .video-container>video {
                // Define transição
        width: 100%;
                wrapper.style.transition = 'height 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
        max-width: 100%;
        height: auto;
        aspect-ratio: 16 / 9;
        object-fit: contain;
        object-position: center;
        z-index: 2;
        display: block;
        border-radius: 6px;
        background: #000;
    }


    .video-placeholder {
                // Anima para a altura FINAL (já medida corretamente)
        position: absolute;
                requestAnimationFrame(() => {
        inset: 0;
                    wrapper.style.height = finalHeight + 'px';
        z-index: 6;
                    // Não altere a posição do scroll do usuário
        display: flex;
                });
        align-items: center;
        justify-content: center;
        pointer-events: none;
        opacity: 1;
        transition: opacity .28s ease;
        background: linear-gradient(rgba(0, 0, 0, 0.0), rgba(0, 0, 0, 0.0));
    }


    .video-placeholder img {
                // Limpa após transição
        max-width: 160px;
                return new Promise(resolve => {
        width: auto;
                    setTimeout(() => {
        height: auto;
                        wrapper.style.height = '';
        opacity: 0.98;
                        wrapper.style.transition = '';
        display: block;
                        wrapper.style.overflow = '';
    }
                        resolve();
                    }, 320);
                });
            }


    .video-placeholder.fade-out {
            tabs.forEach(btn => {
        opacity: 0;
                if (btn.dataset.wiredTab) return;
    }
                btn.dataset.wiredTab = '1';
                btn.addEventListener('click', () => {
                    const target = btn.getAttribute('data-tab');
                    const currentActive = contents.find(c => c.classList.contains('active'));
                    const nextActive = contents.find(c => c.id === target);


    /* ===========================
                    if (currentActive === nextActive) return; // Já está na aba
      LAYOUT COMPONENTS
      =========================== */
    .top-rail {
        display: flex;
        align-items: center;
        justify-content: center;
        width: max-content;
        max-width: 96vw;
        margin: 0px auto;
        padding: 5px 5px;
        background: rgba(28, 28, 34, .95);
        border: 2px solid rgba(255, 255, 255, .08);
        border-radius: 12px;
        box-shadow: 0 4px 12px rgba(0, 0, 0, .25);
        -webkit-backdrop-filter: blur(2px);
        backdrop-filter: blur(2px);
    }


    .top-rail .rail-title {
                    // Previne scroll durante a transição
        display: none;
                    document.body.classList.add('transitioning-tabs');
    }


    .top-rail.skins .rail-title {
                    // Desativa a aba atual (fade out)
        display: block;
                    if (currentActive) {
        font-weight: 800;
                        currentActive.style.opacity = '0';
        font-size: clamp(20px, 2.2vw, 28px);
                        currentActive.style.transform = 'translateY(-8px)';
        color: #fff;
                    }
        margin-right: auto;
    }


    .top-rail .icon-bar {
                    // Delay para fade out completar
        width: auto;
                    setTimeout(async () => {
        justify-content: center;
                        // Remove display de TODAS as abas inativas
        margin: 0;
                        contents.forEach(c => {
        overflow-x: auto;
                            if (c !== nextActive) {
    }
                                c.style.display = 'none';
                                c.classList.remove('active');
                            }
                        });


    .content-card {
                        // Ativa os botões
        width: min(1600px, 96vw);
                        tabs.forEach(b => b.classList.toggle('active', b === btn));
        max-width: 96vw;
        margin: 10px auto;
        background: rgba(28, 28, 34, .95);
        border-radius: 12px;
        box-shadow: 0 8px 24px rgba(0, 0, 0, .30);
        padding: 18px;
        z-index: 2;
    }


    .content-card.skills-grid {
                        // MOSTRA a nova aba INVISÍVEL na posição real
        display: grid;
                        if (nextActive) {
        grid-template-columns: minmax(320px, 60%) minmax(320px, 45%);
                            nextActive.classList.add('active');
        gap: 16px;
                            nextActive.style.display = 'block';
        align-items: start;
                            nextActive.style.opacity = '0';
        grid-auto-rows: auto;
                            nextActive.style.visibility = 'hidden';
        margin: 10px auto 0;
    }


    /* ===========================
                            // Força renderização completa ANTES de medir
      TIER SYSTEM
                            nextActive.offsetHeight;
      =========================== */
    .tier-bronze .topbar-icon,
    .tier-bronze .tier {
        outline: 2px solid #7b4e2f;
    }


    .tier-silver .topbar-icon,
                            // Pré-carrega/ativa conteúdo padrão da aba (ex.: vídeo) ANTES da medição
    .tier-silver .tier {
                            // Assim a altura final já considera o player visível
        outline: 2px solid #d6d2d2;
                            try {
    }
                                if (target === 'weapon' || target === 'skills') {
                                    const tabEl = document.getElementById(target);
                                    if (tabEl) {
                                        const activeIcon = tabEl.querySelector('.icon-bar .skill-icon.active');
                                        const firstIcon = tabEl.querySelector('.icon-bar .skill-icon');
                                        const toClick = activeIcon || firstIcon;
                                        if (toClick) {
                                            // Evita autoplay durante preparação
                                            const had = document.body.dataset.suppressSkillPlay;
                                            document.body.dataset.suppressSkillPlay = '1';
                                            toClick.click();
                                            // Mantém a flag até o fim da transição (limpamos mais abaixo)
                                            if (had) document.body.dataset.suppressSkillPlay = had;
                                        }
                                    }
                                }
                            } catch (e) { /* no-op */ }
                        }


    .tier-gold .topbar-icon,
                        // AGORA anima altura (com a aba já renderizada na posição correta)
    .tier-gold .tier {
                        if (currentActive && nextActive) {
        outline: 2px solid #fcd300;
                            await smoothHeightTransition(currentActive, nextActive);
    }
                        }


    .tier-diamond .topbar-icon,
                        // Após altura estar correta, MOSTRA a nova aba
    .tier-diamond .tier {
                        if (nextActive) {
        outline: 2px solid #60dae2;
                            nextActive.style.visibility = '';
    }
                            nextActive.style.transform = 'translateY(12px)';


    /* ===========================
                            // Anima entrada
      RESPONSIVE DESIGN & MOBILE
                            requestAnimationFrame(() => {
      =========================== */
                                nextActive.style.opacity = '1';
    @media (max-width:1100px) {
                                nextActive.style.transform = 'translateY(0)';
        .top-rail {
            flex-direction: column;
            align-items: stretch;
        }


        .top-rail .icon-bar {
                                // Limpa estilos inline e classe de transição
            order: 2;
                                setTimeout(() => {
            width: 100%;
                                    nextActive.style.opacity = '';
            flex-wrap: wrap;
                                    nextActive.style.transform = '';
        }
                                    document.body.classList.remove('transitioning-tabs');
                                    // Libera flag de autoplay suprimido (se aplicada acima)
                                    try { delete document.body.dataset.suppressSkillPlay; } catch { }
                                }, 300);
                            });
                        }
                    }, 120);


        .content-card.skills-grid {
                    // Executa ações após a transição completa
            grid-template-columns: 1fr;
                    setTimeout(() => {
             gap: 12px;
                        syncDescHeight();
         }
                        if (target === 'skins') {
                            // Pause all cached main skill videos
                            videosCache.forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
                            // Pause all nested/subskill videos
                            if (videoBox) {
                                videoBox.querySelectorAll('video.skill-video').forEach(v => {
                                    try { v.pause(); } catch (e) { }
                                    v.style.display = 'none';
                                });
                            }
                            // Hide subskill videos via API
                            if (window.__subskills) window.__subskills.hideAll?.(videoBox);
                            if (videoBox && placeholder) { placeholder.style.display = 'none'; placeholder.classList.add('fade-out'); }
                            // Pause weapon videos when entering skins
                            const weaponTabEl = document.getElementById('weapon');
                            if (weaponTabEl) {
                                const wvb = weaponTabEl.querySelector('.video-container');
                                if (wvb) wvb.querySelectorAll('video.skill-video').forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
                            }
                        } else if (target === 'weapon') {
                            // Initialize or refresh weapon tab
                            const weaponTab = document.getElementById('weapon');
                            if (weaponTab) {
                                const activeWeaponIcon = weaponTab.querySelector('.icon-bar .skill-icon.active');
                                const toClick = activeWeaponIcon || weaponTab.querySelector('.icon-bar .skill-icon');
                                if (toClick) toClick.click();
                                // Pause skills videos when entering weapon
                                videosCache.forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
                                if (videoBox) videoBox.querySelectorAll('video.skill-video').forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
                            }
                        } else {
                            const activeIcon = document.querySelector('.icon-bar .skill-icon.active');
                            if (activeIcon) activeIcon.click();
                            // Pause weapon videos when returning to skills
                            const weaponTabEl = document.getElementById('weapon');
                            if (weaponTabEl) {
                                const wvb = weaponTabEl.querySelector('.video-container');
                                if (wvb) wvb.querySelectorAll('video.skill-video').forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
                            }
                        }
                    }, 450); // Após transição completar (120ms + 300ms + buffer)
                });
             });
         })();


         .video-container {
         // ===========================
             width: 80%;
        // Skins Navigation
             max-width: 820px;
        // ===========================
             align-self: center;
        (function initSkinsArrows() {
        }
            const carousel = $('.skins-carousel');
    }
            const wrapper = $('.skins-carousel-wrapper');
            const left = $('.skins-arrow.left');
            const right = $('.skins-arrow.right');
            if (!carousel || !left || !right || !wrapper) return;
            if (wrapper.dataset.wired) return;
            wrapper.dataset.wired = '1';
             const scrollAmt = () => Math.round(carousel.clientWidth * 0.6);
             function setState() {
                const max = carousel.scrollWidth - carousel.clientWidth;
                const x = carousel.scrollLeft;
                const hasLeft = x > 5, hasRight = x < max - 5;
                left.style.display = hasLeft ? 'inline-block' : 'none';
                right.style.display = hasRight ? 'inline-block' : 'none';
                wrapper.classList.toggle('has-left', hasLeft);
                wrapper.classList.toggle('has-right', hasRight);
                carousel.style.justifyContent = (!hasLeft && !hasRight) ? 'center' : '';
            }
             function go(dir) {
                const max = carousel.scrollWidth - carousel.clientWidth;
                const next = dir < 0
                    ? Math.max(0, carousel.scrollLeft - scrollAmt())
                    : Math.min(max, carousel.scrollLeft + scrollAmt());
                carousel.scrollTo({ left: next, behavior: 'smooth' });
            }
            left.addEventListener('click', () => go(-1));
            right.addEventListener('click', () => go(1));
            carousel.addEventListener('scroll', setState);
            new ResizeObserver(setState).observe(carousel);
            setState();
        })();


    @media (max-aspect-ratio: 3/4) {
        // ===========================
        .character-header .character-art {
        // Utility Functions
            display: none;
        // ===========================
        }
        function renderAttributes(str) {
            const skillsRoot = document.getElementById('skills');
            const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
            const langRaw = (document.documentElement.lang || skillsRoot?.dataset.i18nDefault || 'pt').toLowerCase();
            const langKey = i18nMap[langRaw] ? langRaw : (i18nMap[langRaw.split('-')[0]] ? langRaw.split('-')[0] : 'pt');
            const L = i18nMap[langKey] || i18nMap.pt || {
                cooldown: 'Recarga', energy_gain: 'Ganho de energia', energy_cost: 'Custo de energia',
                power: 'Poder', power_pvp: 'Poder PvP', level: 'Nível'
            };


        .video-container {
            const vals = (str || '').split(',').map(v => v.trim());
             width: 80%;
             const pve = parseInt(vals[0], 10);
             border-radius: 3%;
             const pvp = parseInt(vals[1], 10);
             margin-top: 2%;
             const ene = parseInt(vals[2], 10);
             align-self: center;
             const cd = parseInt(vals[3], 10);
        }
    }


    @media (max-width:600px) {
             const rows = [];
        .content-card {
             if (!isNaN(cd)) rows.push([L.cooldown, cd]);
             box-sizing: border-box;
             if (!isNaN(ene) && ene !== 0) {
             max-width: calc(100vw - env(safe-area-inset-left) - env(safe-area-inset-right) - 16px);
                const label = ene > 0 ? L.energy_gain : L.energy_cost;
             width: 100%;
                rows.push([label, Math.abs(ene)]);
            margin: 10px auto;
             }
             padding: 12px;
             if (!isNaN(pve)) rows.push([L.power, pve]);
             border-radius: 10px;
             if (!isNaN(pvp)) rows.push([L.power_pvp, pvp]);
             overflow: hidden;
        }


        .content-card.skills-grid {
            if (!rows.length) return '';
            grid-template-columns: 1fr;
            const html = rows.map(([label, value]) => `
             gap: 12px;
    <div class="attr-row">
      <span class="attr-label">${label}</span>
      <span class="attr-value">${value}</span>
    </div>`).join('');
             return `<div class="attr-list">${html}</div>`;
         }
         }


         .top-rail {
         function syncDescHeight() {
            width: calc(100vw - env(safe-area-inset-left) - env(safe-area-inset-right));
             // no-op on purpose
             max-width: 100%;
            margin: 0 auto 8px;
            padding: 6px 8px;
            border-radius: 0;
            box-sizing: border-box;
            overflow: hidden;
         }
         }


         .top-rail .icon-bar {
         // ===========================
            width: 100%;
        // Event Listeners & Initialization
            padding: 0 6px;
        // ===========================
            gap: 12px;
        window.addEventListener('resize', syncDescHeight);
            justify-content: flex-start;
        if (videoBox) new ResizeObserver(syncDescHeight).observe(videoBox);
            overflow-x: auto;
            -webkit-overflow-scrolling: touch;
        }


         :root {
         iconItems.forEach(el => {
             --icon-size: 92px;
             const wired = !!el.dataset._sync_wired;
         }
            if (wired) return;
            el.dataset._sync_wired = '1';
            el.addEventListener('click', () => {
                Promise.resolve().then(syncDescHeight);
            });
         });


         .icon-bar .skill-icon {
         if (iconsBar) addOnce(iconsBar, 'wheel', (e) => {
             width: var(--icon-size);
             if (e.deltaY) {
            height: var(--icon-size);
                e.preventDefault();
             flex: 0 0 auto;
                iconsBar.scrollLeft += e.deltaY;
         }
             }
         });


         .video-container,
         // Initialize first skill
         .skins-carousel-wrapper,
         if (iconItems.length) {
        .skins-carousel {
            const first = iconItems[0];
            max-width: calc(100vw - env(safe-area-inset-left) - env(safe-area-inset-right) - 16px);
            if (first) {
             box-sizing: border-box;
                if (!first.classList.contains('active')) first.classList.add('active');
                setTimeout(() => first.click(), 0);
             }
         }
         }


         .video-container>video,
         // ===========================
        .video-container img,
        // Weapon Tab Setup (if exists)
        .skins-carousel img {
         // ===========================
            max-width: 100%;
         (function initWeaponTab() {
            width: 100%;
            const weaponTab = document.getElementById('weapon');
            height: auto;
            if (!weaponTab) return;
        }
 
        html,
        body,
        .mw-body,
        .mw-body-content {
            overflow-x: hidden;
        }
    }
 
    /* ===========================
      OVERFLOW PROTECTION
  =========================== */
    html,
    body,
    .mw-body,
    .mw-body-content {
         box-sizing: border-box;
        max-width: 100vw;
        overflow-x: hidden;
        -webkit-overflow-scrolling: touch;
    }
 
    .content-card,
    .top-rail,
    .video-container,
    .skins-carousel-wrapper,
    .skins-carousel {
        box-sizing: border-box;
        max-width: 100%;
    }
 
    .content-card *,
    .top-rail *,
    .video-container * {
        max-width: 100%;
        box-sizing: border-box;
    }
 
    /* ===========================
      ICON ENHANCEMENTS & OVERRIDES
      =========================== */
    :root {
        --icon-size: 50px;
        --icon-radius: 10px;
        --icon-idle: #cfcfcf;
        --icon-active: #FFD95A;
        --icon-active-ring: rgba(255, 217, 90, 0.45);
        --icon-active-glow: rgba(255, 217, 90, 0.30);
        --icon-ring-w: 2px;
    }
 
    .icon-bar .skill-icon {
         width: var(--icon-size) !important;
        height: var(--icon-size) !important;
        position: relative;
        border: none !important;
        outline: none !important;
        overflow: hidden;
        border-radius: var(--icon-radius);
        contain: paint;
        isolation: isolate;
        will-change: transform;
        transform: translateZ(0);
    }
 
    .icon-bar .skill-icon img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
        border-radius: inherit;
        -webkit-clip-path: inset(0 round var(--icon-radius));
        clip-path: inset(0 round var(--icon-radius));
        transition: transform .12s ease;
        backface-visibility: hidden;
        transform: translateZ(0);
    }
 
    .icon-bar .skill-icon::after {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: inherit;
        pointer-events: none;
        z-index: 2;
        box-shadow: inset 0 0 0 var(--icon-ring-w) var(--icon-idle) !important;
        transition: box-shadow .12s ease;
    }
 
    .icon-bar .skill-icon::before {
        content: "";
        position: absolute;
        inset: -2px;
        border-radius: calc(var(--icon-radius) + 2px);
        pointer-events: none;
        z-index: 1;
        box-shadow: none;
        opacity: 0;
        transition: opacity .12s ease, box-shadow .12s ease;
    }
 
    .icon-bar .skill-icon.active {
        transform: scale(1.08);
    }


    .icon-bar .skill-icon.active::after {
            const weaponIconBar = weaponTab.querySelector('.icon-bar');
        box-shadow: inset 0 0 0 var(--icon-ring-w) var(--icon-active) !important;
            const weaponIconItems = weaponIconBar ? Array.from(weaponIconBar.querySelectorAll('.skill-icon')) : [];
    }
            const weaponDescBox = weaponTab.querySelector('.desc-box');
            const weaponVideoBox = weaponTab.querySelector('.video-container');


    .icon-bar .skill-icon.active::before {
             if (!weaponIconBar || !weaponIconItems.length) return;
        opacity: 1;
        box-shadow:
             0 0 12px 3px var(--icon-active-glow),
            0 0 0 4px var(--icon-active-ring) !important;
    }


    .icon-bar .skill-icon.active img {
            const weaponVideosCache = new Map();
        transform: scale(1.08);
            let weaponTotalVideos = 0, weaponLoadedVideos = 0, weaponAutoplay = false;
    }


    .icon-bar .skill-icon:hover:not(.active)::after {
            // Load weapon videos
        box-shadow: inset 0 0 0 var(--icon-ring-w) #e6e6e6 !important;
            weaponIconItems.forEach(el => {
    }
                const src = (el.dataset.video || '').trim();
                const idx = el.dataset.index || '';
                if (!src || weaponVideosCache.has(idx)) return;


    /* ===========================
                weaponTotalVideos++;
      TOOLTIP SYSTEM
                const v = document.createElement('video');
  =========================== */
                v.className = 'skill-video';
    .skill-tooltip {
                v.setAttribute('controls', '');
        position: fixed;
                v.setAttribute('preload', 'auto');
        z-index: 9999;
                v.setAttribute('playsinline', '');
        pointer-events: none;
                v.style.display = 'none';
        padding: 6px 8px;
                v.dataset.index = idx;
        border-radius: 6px;
                v.style.width = '100%';
        background: rgba(17, 17, 17, .9);
                v.style.maxWidth = '100%';
        color: #fff;
                v.style.height = 'auto';
        font-size: 14px;
                v.style.aspectRatio = '16/9';
        box-shadow: 0 4px 12px rgba(0, 0, 0, .5);
                v.style.objectFit = 'cover';
        opacity: 0;
                const source = document.createElement('source');
        transition: opacity .12s ease, transform .08s ease;
                source.src = src;
        white-space: nowrap;
                source.type = 'video/webm';
    }
                v.appendChild(source);


    /* ===========================
                v.addEventListener('canplay', () => {
      BACKGROUND SYSTEM
                    weaponLoadedVideos++;
      =========================== */
                    if (weaponLoadedVideos === weaponTotalVideos) weaponAutoplay = true;
    .mw-body-content .character-box {
                });
        background-position: center top;
        background-repeat: no-repeat;
        background-size: cover;
        background-attachment: fixed;
        position: relative;
        z-index: 1;
    }


    .mw-body-content .character-box::before {
                v.addEventListener('error', () => {
        content: "";
                    weaponLoadedVideos++;
        position: absolute;
                    if (weaponLoadedVideos === weaponTotalVideos) weaponAutoplay = true;
        inset: 0;
                });
        pointer-events: none;
        background: linear-gradient(to bottom, rgba(0, 0, 0, .45), rgba(0, 0, 0, .60));
        z-index: 0;
    }


    /* ===========================
                weaponVideoBox.appendChild(v);
      FINAL ICON OVERRIDES & FIXES
                weaponVideosCache.set(idx, v);
      =========================== */
            });
    :root {
        --icon-idle: #d0d0d0;
        --icon-active: #FFD95A;
        --icon-active-ring: rgba(255, 217, 90, 0.50);
        --icon-active-glow: rgba(255, 217, 90, 0.30);
        --icon-ring-w: 2px;
    }


    .icon-bar .skill-icon::after {
            // Wire clicks for weapon icons
        box-shadow: inset 0 0 0 var(--icon-ring-w) var(--icon-idle) !important;
            weaponIconItems.forEach(el => {
        transition: box-shadow .12s ease !important;
                if (el.dataset.wired) return;
    }
                el.dataset.wired = '1';
                const label = el.dataset.nome || el.dataset.name || '';
                el.setAttribute('aria-label', label);
                if (el.hasAttribute('title')) el.removeAttribute('title');
                const img = el.querySelector('img');
                if (img) {
                    img.setAttribute('alt', '');
                    if (img.hasAttribute('title')) img.removeAttribute('title');
                }


    .icon-bar .skill-icon.active::after {
                el.addEventListener('click', () => {
        box-shadow: inset 0 0 0 var(--icon-ring-w) var(--icon-active) !important;
                    const skillsRoot = document.getElementById('skills');
    }
                    const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
                    const lang = getLangKey();
                    const L = i18nMap[lang] || i18nMap.pt || {
                        cooldown: 'Recarga',
                        energy_gain: 'Ganho de energia',
                        energy_cost: 'Custo de energia',
                        power: 'Poder',
                        power_pvp: 'Poder PvP',
                        level: 'Nível'
                    };
                    const name = el.dataset.nome || el.dataset.name || '';
                    const level = (el.dataset.level || '').trim();


    .icon-bar .skill-icon::before {
                    const descPack = {
        inset: -4px;
                        pt: el.dataset.descPt || '',
        border-radius: calc(var(--icon-radius) + 4px);
                        en: el.dataset.descEn || '',
        opacity: 0;
                        es: el.dataset.descEs || '',
        transition: opacity .12s ease, box-shadow .12s ease;
                        pl: el.dataset.descPl || ''
    }
                    };
                    const chosenDesc = descPack[lang] || descPack.pt || descPack.en || descPack.es || descPack.pl || el.dataset.desc || '';
                    const descHtml = chosenDesc.replace(/'''(.*?)'''/g, '<b>$1</b>');


    .icon-bar .skill-icon.active::before {
                    const attrsHTML = el.dataset.atr ? renderAttributes(el.dataset.atr) : '';
        opacity: 1 !important;
                    if (weaponDescBox) {
         box-shadow:
                        weaponDescBox.innerHTML = `
            0 0 14px 4px var(--icon-active-glow),
         <div class="skill-title"><h3>${name}</h3></div>
            0 0 0 calc(var(--icon-ring-w) * 2) var(--icon-active-ring) !important;
        ${level ? `<div class="skill-level-line"><span class="attr-label">${L.level} ${level}</span></div>` : ''}
    }
        ${attrsHTML}
        <div class="desc">${descHtml}</div>`;
                    }


    .icon-bar .skill-icon,
                    weaponIconItems.forEach(i => i.classList.remove('active'));
    .icon-bar .skill-icon img,
                    el.classList.add('active');
    .icon-bar .skill-icon::after,
                    if (!weaponAutoplay && weaponLoadedVideos > 0) weaponAutoplay = true;
    .icon-bar .skill-icon::before {
                    window.__lastActiveSkillIcon = el;
        -webkit-backface-visibility: hidden;
        backface-visibility: hidden;
        transform: translateZ(0);
    }


    .icon-bar .skill-icon:hover:not(.active)::after {
                    // Show video
        box-shadow: inset 0 0 0 var(--icon-ring-w) #e6e6e6 !important;
                    if (weaponVideoBox) {
    }
                        Array.from(weaponVideoBox.querySelectorAll('video.skill-video')).forEach(v => {
                            try { v.pause(); } catch (e) { }
                            v.style.display = 'none';
                        });
                    }


    .icon-bar .skill-icon.active {
                    const hasIdx = !!el.dataset.index;
        transform: scale(1.10) !important;
                    const hasVideo = !!(el.dataset.video && el.dataset.video.trim() !== '');
        z-index: 5;
                    if (!weaponVideoBox || !hasVideo) {
    }
                        if (weaponVideoBox) weaponVideoBox.style.display = 'none';
                        return;
                    }


    .icon-bar .skill-icon.active img {
                    if (hasIdx && weaponVideosCache.has(el.dataset.index)) {
        transform: none !important;
                        const v = weaponVideosCache.get(el.dataset.index);
    }
                        weaponVideoBox.style.display = 'block';
                        v.style.display = 'block';
                        try { v.currentTime = 0; } catch (e) { }
                        const suppress = document.body.dataset.suppressSkillPlay === '1';
                        if (!suppress && weaponAutoplay) v.play().catch(() => { });
                    }
                });
            });


    /* ===========================
            // Wire tooltips for weapon icons
      FINAL TOOLTIP STYLES
            const tip = document.querySelector('.skill-tooltip');
      =========================== */
            if (tip) {
    .skill-tooltip {
                weaponIconItems.forEach(icon => {
        position: fixed;
                    if (icon.dataset.tipwired) return;
        z-index: 10000;
                    icon.dataset.tipwired = '1';
        pointer-events: none;
                    const label = icon.dataset.nome || icon.dataset.name || icon.title || '';
        padding: 8px 10px;
                    if (label && !icon.hasAttribute('aria-label')) icon.setAttribute('aria-label', label);
        border-radius: 8px;
                    if (icon.hasAttribute('title')) icon.removeAttribute('title');
        background: rgba(28, 28, 34, .95);
        color: #eaeaea;
        font-size: 13px;
        line-height: 1.25;
        text-align: center;
        max-width: 360px;
        box-shadow: 0 8px 24px rgba(0, 0, 0, .5), inset 0 0 0 1px rgba(255, 255, 255, .06);
        left: -9999px;
        top: -9999px;
        transform: none;
        opacity: 0;
        transition: opacity .12s ease;
        white-space: normal;
    }


    /* ===========================
                    const measureAndPos = (el) => {
      SKILL LEVEL STYLING
                        if (!el || tip.getAttribute('aria-hidden') === 'true') return;
      =========================== */
                        tip.style.left = '0px';
    #skills .desc-box .skill-title,
                        tip.style.top = '0px';
    #weapon .desc-box .skill-title {
                        const rect = el.getBoundingClientRect();
        margin: 0 0 4px;
                        const tr = tip.getBoundingClientRect();
    }
                        let left = Math.round(rect.left + (rect.width - tr.width) / 2);
                        left = Math.max(8, Math.min(left, window.innerWidth - tr.width - 8));
                        const coarse = (window.matchMedia && matchMedia('(pointer: coarse)').matches) || (window.innerWidth <= 600);
                        let top = coarse ? Math.round(rect.bottom + 10) : Math.round(rect.top - tr.height - 8);
                        if (top < 8) top = Math.round(rect.bottom + 10);
                        tip.style.left = left + 'px';
                        tip.style.top = top + 'px';
                    };
                    const show = (el, text) => {
                        tip.textContent = text || '';
                        tip.setAttribute('aria-hidden', 'false');
                        measureAndPos(el);
                        tip.style.opacity = '1';
                    };
                    const hide = () => {
                        tip.setAttribute('aria-hidden', 'true');
                        tip.style.opacity = '0';
                        tip.style.left = '-9999px';
                        tip.style.top = '-9999px';
                    };
                    icon.addEventListener('mouseenter', () => show(icon, label));
                    icon.addEventListener('mousemove', () => measureAndPos(icon));
                    icon.addEventListener('click', () => { window.__globalSkillTooltip.lockUntil.value = performance.now() + 240; measureAndPos(icon); });
                    icon.addEventListener('mouseleave', hide);
                });
            }


    #skills .desc-box .skill-level-line,
            // Do not pre-mark active here; activation handled on tab enter
    #weapon .desc-box .skill-level-line {
         })();
        margin: 0 0 10px;
         text-align: center;
        line-height: 1.15;
    }


    #skills .desc-box .skill-level-line .attr-label,
        // Debug logging
    #weapon .desc-box .skill-level-line .attr-label {
        setTimeout(() => {
        color: #f0c87b;
            Array.from(document.querySelectorAll('.skill-icon')).forEach(el => {
        font-weight: 800;
                console.log('icon', el.dataset.index, 'data-video=', el.dataset.video);
        letter-spacing: .2px;
            });
         font-size: .98rem;
            videosCache.forEach((v, idx) => {
     }
                const src = v.querySelector('source') ? v.querySelector('source').src : v.src;
</style>
                console.log('video element', idx, 'src=', src, 'readyState=', v.readyState);
                v.addEventListener('error', (ev) => {
                    console.error('VIDEO ERROR idx=', idx, 'src=', src, 'error=', v.error);
                });
                v.addEventListener('loadedmetadata', () => {
                    console.log('loadedmetadata idx=', idx, 'dimensions=', v.videoWidth, 'x', v.videoHeight);
                });
            });
         }, 600);
     })();
</script>

Edição das 19h48min de 17 de novembro de 2025

<script>

   (function () {
       // ===========================
       // Utility Functions
       // ===========================
       const $ = (s, root = document) => root.querySelector(s);
       const $$ = (s, root = document) => Array.from(root.querySelectorAll(s));
       const ensureRemoved = sel => { Array.from(document.querySelectorAll(sel)).forEach(n => n.remove()); };
       const onceFlag = (el, key) => { if (!el) return false; if (el.dataset[key]) return false; el.dataset[key] = '1'; return true; };
       const addOnce = (el, ev, fn) => {
           if (!el) return;
           const attr = `data-wired-${ev}`;
           if (el.hasAttribute(attr)) return;
           el.addEventListener(ev, fn);
           el.setAttribute(attr, '1');
       };
       // Extra helpers
       function filePathURL(fileName) {
           const f = encodeURIComponent((fileName || 'Nada.png').replace(/^Arquivo:|^File:/, ));
           const base = (window.mw && mw.util && typeof mw.util.wikiScript === 'function')
               ? mw.util.wikiScript()
               : (window.mw && window.mw.config ? (mw.config.get('wgScript') || '/index.php') : '/index.php');
           return `${base}?title=Especial:FilePath/${f}`;
       }
       function getLangKey() {
           const skillsRoot = document.getElementById('skills');
           const raw = (document.documentElement.lang || skillsRoot?.dataset.i18nDefault || 'pt').toLowerCase();
           return raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
       }
       function chooseDescFrom(obj) {
           const lang = getLangKey();
           const pack = obj.desc_i18n || { pt: obj.descPt, en: obj.descEn, es: obj.descEs, pl: obj.descPl };
           return (pack && (pack[lang] || pack.pt || pack.en || pack.es || pack.pl)) || (obj.desc || );
       }
       function renderSubAttributesFromObj(s, L) {

const chip = (label, val) => (val ? `

${label}${val}

` : );

           const pve = (s.powerpve || ).toString().trim();
           const pvp = (s.powerpvp || ).toString().trim();
           const en = (s.energy || ).toString().trim();
           const cd = (s.cooldown || ).toString().trim();
           const rows = [
               cd ? chip(L.cooldown, cd) : ,
               en ? chip((en.startsWith('-') ? L.energy_cost : L.energy_gain), en.replace(/^\+?/, )) : ,
               pve ? chip(L.power, pve) : ,
               pvp ? chip(L.power_pvp, pvp) : ,
           ].filter(Boolean);

return rows.length ? `

${rows.join()}

` : ;

       }
       // ===========================
       // DOM Setup & Initialization
       // ===========================
       const skillsTab = $('#skills');
       const skinsTab = $('#skins');
       const weaponTab = $('#weapon');
       // Clean up existing elements
       ensureRemoved('.top-rail');
       ensureRemoved('.content-card');
       ensureRemoved('.video-placeholder');
       Array.from(document.querySelectorAll('.card-skins-title, .card-skins .card-skins-title, .cardskins-title, .rail-title')).forEach(t => {
           if ((t.textContent || ).trim().toLowerCase().includes('skins')) {
               t.remove();
           }
       });
       // Setup skills tab structure
       if (skillsTab) {
           const iconBar = skillsTab.querySelector('.icon-bar');
           if (iconBar) {
               const rail = document.createElement('div');
               rail.className = 'top-rail skills';
               rail.appendChild(iconBar);
               skillsTab.prepend(rail);
           }
           const details = skillsTab.querySelector('.skills-details');
           const videoContainer = skillsTab.querySelector('.video-container');
           const card = document.createElement('div');
           card.className = 'content-card skills-grid';
           if (details) card.appendChild(details);
           if (videoContainer) card.appendChild(videoContainer);
           skillsTab.appendChild(card);
       }
       // Setup weapon tab structure (mirror skills)
       if (weaponTab) {
           const iconBarW = weaponTab.querySelector('.icon-bar');
           if (iconBarW) {
               const railW = document.createElement('div');
               railW.className = 'top-rail weapon';
               railW.appendChild(iconBarW);
               weaponTab.prepend(railW);
           }
           const detailsW = weaponTab.querySelector('.skills-details');
           const videoContainerW = weaponTab.querySelector('.video-container');
           const cardW = document.createElement('div');
           cardW.className = 'content-card skills-grid';
           if (detailsW) cardW.appendChild(detailsW);
           if (videoContainerW) cardW.appendChild(videoContainerW);
           weaponTab.appendChild(cardW);
       }
       // Setup skins tab structure
       if (skinsTab) {
           const wrapper = skinsTab.querySelector('.skins-carousel-wrapper');
           const rail = document.createElement('div');
           rail.className = 'top-rail skins';
           const title = document.createElement('div');
           title.className = 'rail-title';
           title.textContent = 'Skins & Spotlights';
           rail.appendChild(title);
           if (wrapper) {
               const card = document.createElement('div');
               card.className = 'content-card';
               card.appendChild(wrapper);
               skinsTab.prepend(rail);
               skinsTab.appendChild(card);
           } else {
               skinsTab.prepend(rail);
           }
       }
       // ===========================
       // Video Management
       // ===========================
       const iconsBar = $('#skills') ? $('.icon-bar', $('#skills')) : null;
       const iconItems = iconsBar ? Array.from(iconsBar.querySelectorAll('.skill-icon')) : [];
       const descBox = $('#skills') ? $('.desc-box', $('#skills')) : null;
       const videoBox = $('#skills') ? $('.video-container', $('#skills')) : null;
       const videosCache = new Map();
       const nestedVideoElByIcon = new WeakMap();
       const barStack = [];
       let initialBarSnapshot = null;
       let totalVideos = 0, loadedVideos = 0, autoplay = false;
       // Track last clicked skill/subskill for language changes
       window.__lastActiveSkillIcon = null;
       // Placeholder management
       let placeholder = videoBox ? videoBox.querySelector('.video-placeholder') : null;
       let placeholderCreatedOnLoad = false;
       let placeholderConsumed = false;
       if (!placeholder && videoBox) {
           placeholder = document.createElement('div');
           placeholder.className = 'video-placeholder';
           placeholder.innerHTML = '<img src="/images/d/d5/Icon_gla.png" alt="Carregando...">';
           videoBox.appendChild(placeholder);
           placeholderCreatedOnLoad = true;
       } else if (placeholder) {
           placeholderCreatedOnLoad = true;
       }
       if (!placeholder) placeholderConsumed = true;
       const removePlaceholderSmooth = () => {
           if (!placeholder) return;
           if (placeholder.classList.contains('fade-out')) return;
           placeholder.classList.add('fade-out');
           const onEnd = () => {
               try { placeholder.style.display = 'none'; } catch (e) { }
               placeholder.removeEventListener('transitionend', onEnd);
           };
           placeholder.addEventListener('transitionend', onEnd, { once: true });
           setTimeout(() => { try { placeholder.style.display = 'none'; } catch (e) { } }, 600);
       };
       const showPlaceholder = () => {
           if (!videoBox) return;
           if (!placeholder || !placeholderCreatedOnLoad) return;
           if (placeholderConsumed) return;
           placeholder.classList.remove('fade-out');
           placeholder.style.display = 'flex';
           void placeholder.offsetWidth;
       };
       // ===========================
       // Video Loading & Caching
       // ===========================
       if (iconItems.length && videoBox) {
           iconItems.forEach(el => {
               const src = (el.dataset.video || ).trim();
               const idx = el.dataset.index || ;
               if (!src || videosCache.has(idx)) return;
               totalVideos++;
               const v = document.createElement('video');
               v.className = 'skill-video';
               v.setAttribute('controls', );
               v.setAttribute('preload', 'auto');
               v.setAttribute('playsinline', );
               v.style.display = 'none';
               v.dataset.index = idx;
               v.style.width = '100%';
               v.style.maxWidth = '100%';
               v.style.height = 'auto';
               v.style.aspectRatio = '16/9';
               v.style.objectFit = 'cover';
               const source = document.createElement('source');
               source.src = src;
               source.type = 'video/webm';
               v.appendChild(source);
               v.addEventListener('canplay', () => {
                   loadedVideos++;
                   if (loadedVideos === 1) { try { v.pause(); v.currentTime = 0; } catch (e) { } }
                   const active = $('.skill-icon.active', iconsBar);
                   if (active && active.dataset.index === idx) {
                       if (!placeholderConsumed) {
                           setTimeout(() => { removePlaceholderSmooth(); placeholderConsumed = true; }, 180);
                       }
                   }
                   if (loadedVideos === totalVideos) autoplay = true;
               });
               v.addEventListener('error', () => {
                   loadedVideos++;
                   removePlaceholderSmooth();
                   if (loadedVideos === totalVideos) autoplay = true;
               });
               videoBox.appendChild(v);
               videosCache.set(idx, v);
           });
       }
       if (totalVideos === 0 && placeholder) {
           placeholder.style.display = 'none';
           placeholder.classList.add('fade-out');
       }
       // ===========================
       // Skill Bar Wiring (root and nested)
       // ===========================
       function wireTooltipsForNewIcons() {
           const tip = document.querySelector('.skill-tooltip');
           if (!tip) return;
           let lockUntil2 = 0;
           Array.from(document.querySelectorAll('.icon-bar .skill-icon')).forEach(icon => {
               if (icon.dataset.tipwired) return;
               icon.dataset.tipwired = '1';
               const label = icon.dataset.nome || icon.dataset.name || icon.title || ;
               if (label && !icon.hasAttribute('aria-label')) icon.setAttribute('aria-label', label);
               if (icon.hasAttribute('title')) icon.removeAttribute('title');
               const img = icon.querySelector('img');
               if (img) { const imgAlt = img.getAttribute('alt') || ; const imgTitle = img.getAttribute('title') || ; if (!label && (imgAlt || imgTitle)) icon.setAttribute('aria-label', imgAlt || imgTitle); img.setAttribute('alt', ); if (img.hasAttribute('title')) img.removeAttribute('title'); }
               const measureAndPos = (el) => {
                   if (!el || tip.getAttribute('aria-hidden') === 'true') return;
                   tip.style.left = '0px'; tip.style.top = '0px';
                   const rect = el.getBoundingClientRect(); const tr = tip.getBoundingClientRect();
                   let left = Math.round(rect.left + (rect.width - tr.width) / 2);
                   left = Math.max(8, Math.min(left, window.innerWidth - tr.width - 8));
                   const coarse = (window.matchMedia && matchMedia('(pointer: coarse)').matches) || (window.innerWidth <= 600);
                   let top = coarse ? Math.round(rect.bottom + 10) : Math.round(rect.top - tr.height - 8);
                   if (top < 8) top = Math.round(rect.bottom + 10);
                   tip.style.left = left + 'px'; tip.style.top = top + 'px';
               };
               const show = (el, text) => { tip.textContent = text || ; tip.setAttribute('aria-hidden', 'false'); measureAndPos(el); tip.style.opacity = '1'; };
               const hide = () => { tip.setAttribute('aria-hidden', 'true'); tip.style.opacity = '0'; tip.style.left = '-9999px'; tip.style.top = '-9999px'; };
               icon.addEventListener('mouseenter', () => show(icon, (icon.dataset.nome || icon.dataset.name || )));
               icon.addEventListener('mousemove', () => { if (performance.now() >= lockUntil2) measureAndPos(icon); });
               icon.addEventListener('click', () => { lockUntil2 = performance.now() + 240; measureAndPos(icon); });
               icon.addEventListener('mouseleave', hide);
           });
       }
       function showVideoForIcon(el) {
           // Hide all existing videos
           if (videoBox) {
               Array.from(videoBox.querySelectorAll('video.skill-video')).forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
           }
           if (window.__subskills) window.__subskills.hideAll?.(videoBox);
           const hasIdx = !!el.dataset.index;
           const hasVideo = !!(el.dataset.video && el.dataset.video.trim() !== );
           if (!videoBox || !hasVideo) {
               if (videoBox) { videoBox.style.display = 'none'; if (placeholder) { placeholder.style.display = 'none'; placeholder.classList.add('fade-out'); } }
               return;
           }
           if (hasIdx && videosCache.has(el.dataset.index)) {
               const v = videosCache.get(el.dataset.index);
               videoBox.style.display = 'block';
               v.style.display = 'block';
               try { v.currentTime = 0; } catch (e) { }
               const suppress = document.body.dataset.suppressSkillPlay === '1';
               if (!suppress) { if (autoplay) v.play().catch(() => { }); } else { try { v.pause(); } catch (e) { } }
               return;
           }
           // Nested or custom icon video
           let v = nestedVideoElByIcon.get(el);
           if (!v) {
               v = document.createElement('video');
               v.className = 'skill-video';
               v.setAttribute('controls', ); v.setAttribute('preload', 'auto'); v.setAttribute('playsinline', );
               v.style.display = 'none'; v.style.width = '100%'; v.style.height = 'auto'; v.style.aspectRatio = '16/9'; v.style.objectFit = 'cover';
               const src = document.createElement('source');
               src.src = el.dataset.video; src.type = 'video/webm';
               v.appendChild(src);
               videoBox.appendChild(v);
               nestedVideoElByIcon.set(el, v);
           }
           videoBox.style.display = 'block';
           v.style.display = 'block';
           try { v.currentTime = 0; } catch (e) { }
           const suppress = document.body.dataset.suppressSkillPlay === '1';
           if (!suppress) { if (autoplay) v.play().catch(() => { }); } else { try { v.pause(); } catch (e) { } }
       }
       function wireClicksForCurrentBar() {
           const currIcons = Array.from(iconsBar.querySelectorAll('.skill-icon'));
           currIcons.forEach(el => {
               if (el.dataset.wired) return;
               el.dataset.wired = '1';
               const label = el.dataset.nome || el.dataset.name || ;
               el.setAttribute('aria-label', label);
               if (el.hasAttribute('title')) el.removeAttribute('title');
               const img = el.querySelector('img'); if (img) { img.setAttribute('alt', ); if (img.hasAttribute('title')) img.removeAttribute('title'); }
               el.addEventListener('click', () => {
                   const skillsRoot = document.getElementById('skills');
                   const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
                   const L = i18nMap[getLangKey()] || i18nMap.pt || { cooldown: 'Recarga', energy_gain: 'Ganho de energia', energy_cost: 'Custo de energia', power: 'Poder', power_pvp: 'Poder PvP', level: 'Nível' };
                   const name = el.dataset.nome || el.dataset.name || ;
                   const level = (el.dataset.level || ).trim();
                   // Pick description from current language (respects language changes)
                   const lang = getLangKey();
                   const descPack = {
                       pt: el.dataset.descPt || ,
                       en: el.dataset.descEn || ,
                       es: el.dataset.descEs || ,
                       pl: el.dataset.descPl || 
                   };
                   const chosenDesc = descPack[lang] || descPack.pt || descPack.en || descPack.es || descPack.pl || el.dataset.desc || ;
                   const descHtml = chosenDesc.replace(/(.*?)/g, '$1');
                   const attrsHTML = el.dataset.atr ? renderAttributes(el.dataset.atr) : (el.dataset.subattrs ? renderSubAttributesFromObj(JSON.parse(el.dataset.subattrs), L) : );
                   if (descBox) {
                       descBox.innerHTML = `

${name}

${level ? `

${L.level} ${level}

` : }

       ${attrsHTML}
${descHtml}

`;

                   }
                   // Active state
                   currIcons.forEach(i => i.classList.remove('active')); el.classList.add('active'); if (!autoplay && loadedVideos > 0) autoplay = true;
                   // Track for language changes
                   window.__lastActiveSkillIcon = el;
                   // Video
                   showVideoForIcon(el);
                   // Nested skills UX: clicou na skill -> troca a barra se houver subs; se for 'back', volta
                   const subsRaw = el.dataset.subs || el.getAttribute('data-subs');
                   const isBack = el.dataset.back === 'true' || el.getAttribute('data-back') === 'true';
                   if (isBack && barStack.length) {
                       const prev = barStack.pop();
                       renderBarFromItems(prev.items);
                       const btn = document.querySelector('.skills-back-wrapper');
                       if (btn) btn.style.display = barStack.length ? 'block' : 'none';
                       return;
                   }
                   if (subsRaw && subsRaw.trim() !== ) {
                       try { const subs = JSON.parse(subsRaw); pushSubBarFrom(subs, el); } catch { /* no-op */ }
                   }
               });
           });
           // Removido: badges de +/− (abrir/voltar via clique direto no ícone)
           wireTooltipsForNewIcons();
       }
       function snapshotCurrentBarItemsFromDOM() {
           return Array.from(iconsBar.querySelectorAll('.skill-icon')).map(el => {
               const img = el.querySelector('img');
               const iconURL = img ? img.src : ;
               const subsRaw = el.dataset.subs || el.getAttribute('data-subs') || ;
               let subs = null; try { subs = subsRaw ? JSON.parse(subsRaw) : null; } catch { subs = null; }
               const subattrsRaw = el.dataset.subattrs || ;
               return {
                   name: el.dataset.nome || el.dataset.name || ,
                   level: el.dataset.level || ,
                   desc: el.dataset.desc || ,
                   descPt: el.dataset.descPt || ,
                   descEn: el.dataset.descEn || ,
                   descEs: el.dataset.descEs || ,
                   descPl: el.dataset.descPl || ,
                   attrs: el.dataset.atr || el.dataset.attrs || ,
                   video: el.dataset.video || ,
                   iconURL,
                   subs,
                   subattrsStr: subattrsRaw,
                   noback: el.dataset.noback || null
               };
           });
       }
       function ensureBackButton() {
           const rail = iconsBar.closest('.top-rail.skills'); if (!rail) return null;
           // Wrap rail in a dedicated container if not already wrapped
           let wrap = rail.parentElement;
           if (!wrap || !wrap.classList || !wrap.classList.contains('skills-rail-wrap')) {
               const parentNode = rail.parentNode;
               const newWrap = document.createElement('div');
               newWrap.className = 'skills-rail-wrap';
               // keep layout
               parentNode.insertBefore(newWrap, rail);
               newWrap.appendChild(rail);
               wrap = newWrap;
           }
           // Ensure back-wrapper exists as sibling layered behind
           let backWrap = wrap.querySelector('.skills-back-wrapper');
           if (!backWrap) {
               backWrap = document.createElement('div');
               backWrap.className = 'skills-back-wrapper';
               const btnInner = document.createElement('button');
               btnInner.className = 'skills-back';
               btnInner.type = 'button';
               btnInner.setAttribute('aria-label', 'Voltar');
               // Use SVG double chevron for a crisper, larger icon
               btnInner.innerHTML = '<svg class="back-chevron" width="100%" height="100%" viewBox="0 0 36 32" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" preserveAspectRatio="xMidYMid meet"><path d="M10 2L4 16L10 30" stroke="currentColor" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 2L14 16L20 30" stroke="currentColor" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M30 2L24 16L30 30" stroke="currentColor" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
               backWrap.appendChild(btnInner);
               wrap.insertBefore(backWrap, rail);
               btnInner.addEventListener('click', () => {
                   if (!barStack.length) return;
                   const prev = barStack.pop();
                   renderBarFromItems(prev.items);
                   backWrap.style.display = barStack.length ? 'block' : 'none';
                   if (!barStack.length) btnInner.classList.remove('peek');
               });
           }
           // Check if current level has noback flag
           const currentLevel = barStack[barStack.length - 1];
           const shouldHide = currentLevel && currentLevel.noback;
           backWrap.style.display = (barStack.length && !shouldHide) ? 'block' : 'none';
           const btnInner = backWrap.querySelector('.skills-back');
           return btnInner;
       }
       function renderBarFromItems(items) {
           iconsBar.innerHTML = ;
           items.forEach((it, idx) => {
               const node = document.createElement('div'); node.className = 'skill-icon';
               node.dataset.nome = it.name || ;
               if (it.level) node.dataset.level = it.level;
               if (it.desc) node.dataset.desc = it.desc;
               if (it.descPt) node.dataset.descPt = it.descPt;
               if (it.descEn) node.dataset.descEn = it.descEn;
               if (it.descEs) node.dataset.descEs = it.descEs;
               if (it.descPl) node.dataset.descPl = it.descPl;
               if (it.attrs) node.dataset.atr = it.attrs;
               if (it.video) node.dataset.video = it.video;
               if (it.subs) node.dataset.subs = JSON.stringify(it.subs);
               if (it.subattrsStr) node.dataset.subattrs = it.subattrsStr;
               if (it.noback) node.dataset.noback = it.noback;
               // mark nested icons (no dataset.index)
               if (!it.index) node.dataset.nested = '1';
               const img = document.createElement('img'); img.alt = ; img.src = it.iconURL || (it.icon ? filePathURL(it.icon) : ); node.appendChild(img);
               iconsBar.appendChild(node);
           });
           // Animação de entrada (igual quando entra em subskills)
           Array.from(iconsBar.children).forEach((c, i) => {
               c.style.opacity = '0'; c.style.transform = 'translateY(6px)';
               requestAnimationFrame(() => {
                   setTimeout(() => { c.style.transition = 'opacity .18s ease, transform .18s ease'; c.style.opacity = '1'; c.style.transform = 'translateY(0)'; }, i * 24);
               });
           });
           wireClicksForCurrentBar(); const b = ensureBackButton(); if (b) b.classList.add('peek');
       }
       function pushSubBarFrom(subs, parentIconEl) {
           // Check if parent has noback flag
           const hasNoBack = parentIconEl && (parentIconEl.dataset.noback === 'true' || parentIconEl.getAttribute('data-noback') === 'true');
           // Save current
           barStack.push({ items: snapshotCurrentBarItemsFromDOM(), noback: hasNoBack }); ensureBackButton();
           // Build next items from JSON
           const skillsRoot = document.getElementById('skills');
           const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
           const L = i18nMap[getLangKey()] || i18nMap.pt || { cooldown: 'Recarga', energy_gain: 'Ganho de energia', energy_cost: 'Custo de energia', power: 'Poder', power_pvp: 'Poder PvP', level: 'Nível' };
           const items = (subs || []).map(s => {
               const name = (s.name || s.n || ).trim();
               const desc = chooseDescFrom(s).replace(/(.*?)/g, '$1');
               const attrsHTML = renderSubAttributesFromObj(s, L);
               // store sub-attrs object JSON to re-render attributes later
               return {
                   name,
                   level: (s.level || ).toString().trim(),
                   desc,
                   descPt: (s.descPt || (s.desc_i18n && s.desc_i18n.pt) || ),
                   descEn: (s.descEn || (s.desc_i18n && s.desc_i18n.en) || ),
                   descEs: (s.descEs || (s.desc_i18n && s.desc_i18n.es) || ),
                   descPl: (s.descPl || (s.desc_i18n && s.desc_i18n.pl) || ),
                   // keep raw obj for attrs
                   attrs: ,
                   icon: (s.icon || 'Nada.png'),
                   iconURL: filePathURL(s.icon || 'Nada.png'),
                   video: s.video ? filePathURL(s.video) : ,
                   subs: Array.isArray(s.subs) ? s.subs : null,
                   subattrs: s,
                   back: s.back === true ? 'true' : null
               };
           });
           // Render and attach subattrs object via data-subattrs
           iconsBar.innerHTML = ;
           items.forEach((it, iIdx) => {
               const node = document.createElement('div'); node.className = 'skill-icon'; node.dataset.nested = '1';
               node.dataset.nome = it.name || ;
               if (it.level) node.dataset.level = it.level;
               if (it.desc) node.dataset.desc = it.desc;
               if (it.descPt) node.dataset.descPt = it.descPt;
               if (it.descEn) node.dataset.descEn = it.descEn;
               if (it.descEs) node.dataset.descEs = it.descEs;
               if (it.descPl) node.dataset.descPl = it.descPl;
               if (it.video) node.dataset.video = it.video;
               if (it.subs) node.dataset.subs = JSON.stringify(it.subs);
               if (it.subattrs) node.dataset.subattrs = JSON.stringify(it.subattrs);
               if (it.back) node.dataset.back = it.back;
               const img = document.createElement('img'); img.alt = ; img.src = it.iconURL; node.appendChild(img);
               iconsBar.appendChild(node);
           });
           // pequena animação de entrada
           Array.from(iconsBar.children).forEach((c, i) => {
               c.style.opacity = '0'; c.style.transform = 'translateY(6px)';
               requestAnimationFrame(() => {
                   setTimeout(() => { c.style.transition = 'opacity .18s ease, transform .18s ease'; c.style.opacity = '1'; c.style.transform = 'translateY(0)'; }, i * 24);
               });
           });
           wireClicksForCurrentBar(); const b2 = ensureBackButton(); if (b2) b2.classList.add('peek');
       }
       // Reage à troca de idioma (emitida pelo char translator)
       window.addEventListener('gla:langChanged', () => {
           const skillsRoot = document.getElementById('skills');
           const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
           const lang = getLangKey();
           // Atualiza dataset.desc dos ícones da barra atual (skills)
           Array.from(iconsBar.querySelectorAll('.skill-icon')).forEach(icon => {
               const pack = {
                   pt: icon.dataset.descPt || ,
                   en: icon.dataset.descEn || ,
                   es: icon.dataset.descEs || ,
                   pl: icon.dataset.descPl || 
               };
               const chosen = (pack[lang] || pack.pt || pack.en || pack.es || pack.pl || icon.dataset.desc || ).trim();
               if (chosen) icon.dataset.desc = chosen;
           });
           // Atualiza descrições salvas no stack (para futuras barras ao voltar)
           barStack.forEach(frame => {
               (frame.items || []).forEach(it => {
                   const pack = { pt: it.descPt, en: it.descEn, es: it.descEs, pl: it.descPl };
                   const chosen = (pack[lang] || pack.pt || pack.en || pack.es || pack.pl || it.desc || );
                   it.desc = chosen;
               });
           });
           // Atualiza dataset.desc dos ícones da weapon tab (se existir)
           const weaponTab = document.getElementById('weapon');
           if (weaponTab) {
               const weaponIconBar = weaponTab.querySelector('.icon-bar');
               if (weaponIconBar) {
                   Array.from(weaponIconBar.querySelectorAll('.skill-icon')).forEach(icon => {
                       const pack = {
                           pt: icon.dataset.descPt || ,
                           en: icon.dataset.descEn || ,
                           es: icon.dataset.descEs || ,
                           pl: icon.dataset.descPl || 
                       };
                       const chosen = (pack[lang] || pack.pt || pack.en || pack.es || pack.pl || icon.dataset.desc || ).trim();
                       if (chosen) icon.dataset.desc = chosen;
                   });
               }
           }
       });
       // Wire initial (root) bar and add + badges
       wireClicksForCurrentBar(); const b0 = ensureBackButton(); if (b0) { b0.classList.add('peek'); b0.style.alignSelf = 'stretch'; }
       // ===========================
       // Tooltip System
       // ===========================
       (function initSkillTooltip() {
           if (document.querySelector('.skill-tooltip')) return;
           const tip = document.createElement('div');
           tip.className = 'skill-tooltip';
           tip.setAttribute('role', 'tooltip');
           tip.setAttribute('aria-hidden', 'true');
           document.body.appendChild(tip);
           const lockUntilRef = { value: 0 };
           function measureAndPos(el) {
               if (!el || tip.getAttribute('aria-hidden') === 'true') return;
               tip.style.left = '0px';
               tip.style.top = '0px';
               const rect = el.getBoundingClientRect();
               const tr = tip.getBoundingClientRect();
               let left = Math.round(rect.left + (rect.width - tr.width) / 2);
               left = Math.max(8, Math.min(left, window.innerWidth - tr.width - 8));
               const coarse = (window.matchMedia && matchMedia('(pointer: coarse)').matches) || (window.innerWidth <= 600);
               let top = coarse ? Math.round(rect.bottom + 10) : Math.round(rect.top - tr.height - 8);
               if (top < 8) top = Math.round(rect.bottom + 10);
               tip.style.left = left + 'px';
               tip.style.top = top + 'px';
           }
           function show(el, text) {
               tip.textContent = text || ;
               tip.setAttribute('aria-hidden', 'false');
               measureAndPos(el);
               tip.style.opacity = '1';
           }
           function hide() {
               tip.setAttribute('aria-hidden', 'true');
               tip.style.opacity = '0';
               tip.style.left = '-9999px';
               tip.style.top = '-9999px';
           }
           // Export to global for subskills to use
           window.__globalSkillTooltip = {
               show,
               hide,
               measureAndPos,
               lockUntil: lockUntilRef
           };
           Array.from(document.querySelectorAll('.icon-bar .skill-icon')).forEach(icon => {
               if (icon.dataset.tipwired) return;
               icon.dataset.tipwired = '1';
               const label = icon.dataset.nome || icon.dataset.name || icon.title || ;
               if (label && !icon.hasAttribute('aria-label')) icon.setAttribute('aria-label', label);
               if (icon.hasAttribute('title')) icon.removeAttribute('title');
               const img = icon.querySelector('img');
               if (img) {
                   const imgAlt = img.getAttribute('alt') || ;
                   const imgTitle = img.getAttribute('title') || ;
                   if (!label && (imgAlt || imgTitle)) icon.setAttribute('aria-label', imgAlt || imgTitle);
                   img.setAttribute('alt', );
                   if (img.hasAttribute('title')) img.removeAttribute('title');
               }
               icon.addEventListener('mouseenter', () => show(icon, label));
               icon.addEventListener('mousemove', () => { if (performance.now() >= lockUntilRef.value) measureAndPos(icon); });
               icon.addEventListener('click', () => { lockUntilRef.value = performance.now() + 240; measureAndPos(icon); });
               icon.addEventListener('mouseleave', hide);
           });
           // Also wire subskill icons present at load (kept in sync when sub-rail opens)
           Array.from(document.querySelectorAll('.subskills-rail .subicon')).forEach(sub => {
               if (sub.dataset.tipwired) return;
               sub.dataset.tipwired = '1';
               const label = sub.getAttribute('title') || sub.getAttribute('aria-label') || ;
               if (label && !sub.hasAttribute('aria-label')) sub.setAttribute('aria-label', label);
               if (sub.hasAttribute('title')) sub.removeAttribute('title');
               sub.addEventListener('mouseenter', () => show(sub, label));
               sub.addEventListener('mousemove', () => { if (performance.now() >= lockUntilRef.value) measureAndPos(sub); });
               sub.addEventListener('click', () => { lockUntilRef.value = performance.now() + 240; measureAndPos(sub); });
               sub.addEventListener('mouseleave', hide);
           });
           window.addEventListener('scroll', () => {
               const visible = document.querySelector('.skill-tooltip[aria-hidden="false"]');
               if (!visible) return;
               const target = document.querySelector('.subskills-rail .subicon:hover')
                   || document.querySelector('.subskills-rail .subicon.active')
                   || document.querySelector('.icon-bar .skill-icon:hover')
                   || document.querySelector('.icon-bar .skill-icon.active');
               measureAndPos(target);
           }, true);
           window.addEventListener('resize', () => {
               const target = document.querySelector('.subskills-rail .subicon:hover')
                   || document.querySelector('.subskills-rail .subicon.active')
                   || document.querySelector('.icon-bar .skill-icon:hover')
                   || document.querySelector('.icon-bar .skill-icon.active');
               measureAndPos(target);
           });
       })();
       // ===========================
       // Tab System (com transição suave de altura)
       // ===========================
       (function initTabs() {
           const tabs = Array.from(document.querySelectorAll('.tab-btn'));
           if (!tabs.length) return;
           const contents = Array.from(document.querySelectorAll('.tab-content'));
           const characterBox = document.querySelector('.character-box');
           // Cria o wrapper UMA VEZ no início
           let wrapper = characterBox.querySelector('.tabs-height-wrapper');
           if (!wrapper) {
               wrapper = document.createElement('div');
               wrapper.className = 'tabs-height-wrapper';
               // Move os conteúdos das abas para dentro do wrapper
               contents.forEach(c => {
                   wrapper.appendChild(c);
               });
               // Encontra onde inserir o wrapper (após as tabs)
               const tabsElement = characterBox.querySelector('.character-tabs');
               if (tabsElement && tabsElement.nextSibling) {
                   characterBox.insertBefore(wrapper, tabsElement.nextSibling);
               } else {
                   characterBox.appendChild(wrapper);
               }
           }
           // Função para animar a altura suavemente (retorna Promise)
           // NOVA ESTRATÉGIA: aba já está visível mas invisível (opacity:0, visibility:hidden)
           async function smoothHeightTransition(fromTab, toTab) {
               if (!wrapper) return Promise.resolve();
               // Salva o scroll atual
               const scrollY = window.scrollY;
               // Mede a altura ATUAL
               const currentHeight = wrapper.getBoundingClientRect().height;
               // A aba toTab JÁ está display:block mas invisível
               // Aguarda ela renderizar COMPLETAMENTE na posição real
               await new Promise((resolve) => {
                   const videoContainers = toTab.querySelectorAll('.video-container');
                   const contentCard = toTab.querySelector('.content-card');
                   if (videoContainers.length === 0) {
                       // Sem vídeos, aguarda 3 frames
                       requestAnimationFrame(() => {
                           requestAnimationFrame(() => {
                               requestAnimationFrame(() => resolve());
                           });
                       });
                       return;
                   }
                   // COM vídeos: aguarda até que TUDO esteja renderizado
                   // Faz polling da altura até estabilizar
                   let lastHeight = 0;
                   let stableCount = 0;
                   const checksNeeded = 3; // Altura precisa ficar estável por 3 checks
                   let totalChecks = 0;
                   const maxChecks = 15; // Máximo 15 checks (750ms)
                   function checkStability() {
                       totalChecks++;
                       // Mede altura atual da aba
                       const currentTabHeight = toTab.scrollHeight;
                       // Verifica se estabilizou
                       if (Math.abs(currentTabHeight - lastHeight) < 5) {
                           stableCount++;
                       } else {
                           stableCount = 0; // Resetar se mudou
                       }
                       lastHeight = currentTabHeight;
                       // Se estável por N checks ou atingiu máximo, resolve
                       if (stableCount >= checksNeeded || totalChecks >= maxChecks) {
                           resolve();
                       } else {
                           // Continua checando a cada 50ms
                           setTimeout(checkStability, 50);
                       }
                   }
                   // Inicia checagem após um pequeno delay
                   setTimeout(checkStability, 50);
               });
               // AGORA mede a altura REAL (após tudo renderizado)
               const nextHeight = toTab.getBoundingClientRect().height;
               const finalHeight = Math.max(nextHeight, 100);
               // Se alturas são similares (< 30px diferença), não anima
               if (Math.abs(finalHeight - currentHeight) < 30) {
                   wrapper.style.height = ;
                   return Promise.resolve();
               }
               // Define altura inicial fixa
               wrapper.style.overflow = 'hidden';
               wrapper.style.height = currentHeight + 'px';
               // Força reflow
               wrapper.offsetHeight;
               // Define transição
               wrapper.style.transition = 'height 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
               // Anima para a altura FINAL (já medida corretamente)
               requestAnimationFrame(() => {
                   wrapper.style.height = finalHeight + 'px';
                   // Não altere a posição do scroll do usuário
               });
               // Limpa após transição
               return new Promise(resolve => {
                   setTimeout(() => {
                       wrapper.style.height = ;
                       wrapper.style.transition = ;
                       wrapper.style.overflow = ;
                       resolve();
                   }, 320);
               });
           }
           tabs.forEach(btn => {
               if (btn.dataset.wiredTab) return;
               btn.dataset.wiredTab = '1';
               btn.addEventListener('click', () => {
                   const target = btn.getAttribute('data-tab');
                   const currentActive = contents.find(c => c.classList.contains('active'));
                   const nextActive = contents.find(c => c.id === target);
                   if (currentActive === nextActive) return; // Já está na aba
                   // Previne scroll durante a transição
                   document.body.classList.add('transitioning-tabs');
                   // Desativa a aba atual (fade out)
                   if (currentActive) {
                       currentActive.style.opacity = '0';
                       currentActive.style.transform = 'translateY(-8px)';
                   }
                   // Delay para fade out completar
                   setTimeout(async () => {
                       // Remove display de TODAS as abas inativas
                       contents.forEach(c => {
                           if (c !== nextActive) {
                               c.style.display = 'none';
                               c.classList.remove('active');
                           }
                       });
                       // Ativa os botões
                       tabs.forEach(b => b.classList.toggle('active', b === btn));
                       // MOSTRA a nova aba INVISÍVEL na posição real
                       if (nextActive) {
                           nextActive.classList.add('active');
                           nextActive.style.display = 'block';
                           nextActive.style.opacity = '0';
                           nextActive.style.visibility = 'hidden';
                           // Força renderização completa ANTES de medir
                           nextActive.offsetHeight;
                           // Pré-carrega/ativa conteúdo padrão da aba (ex.: vídeo) ANTES da medição
                           // Assim a altura final já considera o player visível
                           try {
                               if (target === 'weapon' || target === 'skills') {
                                   const tabEl = document.getElementById(target);
                                   if (tabEl) {
                                       const activeIcon = tabEl.querySelector('.icon-bar .skill-icon.active');
                                       const firstIcon = tabEl.querySelector('.icon-bar .skill-icon');
                                       const toClick = activeIcon || firstIcon;
                                       if (toClick) {
                                           // Evita autoplay durante preparação
                                           const had = document.body.dataset.suppressSkillPlay;
                                           document.body.dataset.suppressSkillPlay = '1';
                                           toClick.click();
                                           // Mantém a flag até o fim da transição (limpamos mais abaixo)
                                           if (had) document.body.dataset.suppressSkillPlay = had;
                                       }
                                   }
                               }
                           } catch (e) { /* no-op */ }
                       }
                       // AGORA anima altura (com a aba já renderizada na posição correta)
                       if (currentActive && nextActive) {
                           await smoothHeightTransition(currentActive, nextActive);
                       }
                       // Após altura estar correta, MOSTRA a nova aba
                       if (nextActive) {
                           nextActive.style.visibility = ;
                           nextActive.style.transform = 'translateY(12px)';
                           // Anima entrada
                           requestAnimationFrame(() => {
                               nextActive.style.opacity = '1';
                               nextActive.style.transform = 'translateY(0)';
                               // Limpa estilos inline e classe de transição
                               setTimeout(() => {
                                   nextActive.style.opacity = ;
                                   nextActive.style.transform = ;
                                   document.body.classList.remove('transitioning-tabs');
                                   // Libera flag de autoplay suprimido (se aplicada acima)
                                   try { delete document.body.dataset.suppressSkillPlay; } catch { }
                               }, 300);
                           });
                       }
                   }, 120);
                   // Executa ações após a transição completa
                   setTimeout(() => {
                       syncDescHeight();
                       if (target === 'skins') {
                           // Pause all cached main skill videos
                           videosCache.forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
                           // Pause all nested/subskill videos
                           if (videoBox) {
                               videoBox.querySelectorAll('video.skill-video').forEach(v => {
                                   try { v.pause(); } catch (e) { }
                                   v.style.display = 'none';
                               });
                           }
                           // Hide subskill videos via API
                           if (window.__subskills) window.__subskills.hideAll?.(videoBox);
                           if (videoBox && placeholder) { placeholder.style.display = 'none'; placeholder.classList.add('fade-out'); }
                           // Pause weapon videos when entering skins
                           const weaponTabEl = document.getElementById('weapon');
                           if (weaponTabEl) {
                               const wvb = weaponTabEl.querySelector('.video-container');
                               if (wvb) wvb.querySelectorAll('video.skill-video').forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
                           }
                       } else if (target === 'weapon') {
                           // Initialize or refresh weapon tab
                           const weaponTab = document.getElementById('weapon');
                           if (weaponTab) {
                               const activeWeaponIcon = weaponTab.querySelector('.icon-bar .skill-icon.active');
                               const toClick = activeWeaponIcon || weaponTab.querySelector('.icon-bar .skill-icon');
                               if (toClick) toClick.click();
                               // Pause skills videos when entering weapon
                               videosCache.forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
                               if (videoBox) videoBox.querySelectorAll('video.skill-video').forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
                           }
                       } else {
                           const activeIcon = document.querySelector('.icon-bar .skill-icon.active');
                           if (activeIcon) activeIcon.click();
                           // Pause weapon videos when returning to skills
                           const weaponTabEl = document.getElementById('weapon');
                           if (weaponTabEl) {
                               const wvb = weaponTabEl.querySelector('.video-container');
                               if (wvb) wvb.querySelectorAll('video.skill-video').forEach(v => { try { v.pause(); } catch (e) { } v.style.display = 'none'; });
                           }
                       }
                   }, 450); // Após transição completar (120ms + 300ms + buffer)
               });
           });
       })();
       // ===========================
       // Skins Navigation
       // ===========================
       (function initSkinsArrows() {
           const carousel = $('.skins-carousel');
           const wrapper = $('.skins-carousel-wrapper');
           const left = $('.skins-arrow.left');
           const right = $('.skins-arrow.right');
           if (!carousel || !left || !right || !wrapper) return;
           if (wrapper.dataset.wired) return;
           wrapper.dataset.wired = '1';
           const scrollAmt = () => Math.round(carousel.clientWidth * 0.6);
           function setState() {
               const max = carousel.scrollWidth - carousel.clientWidth;
               const x = carousel.scrollLeft;
               const hasLeft = x > 5, hasRight = x < max - 5;
               left.style.display = hasLeft ? 'inline-block' : 'none';
               right.style.display = hasRight ? 'inline-block' : 'none';
               wrapper.classList.toggle('has-left', hasLeft);
               wrapper.classList.toggle('has-right', hasRight);
               carousel.style.justifyContent = (!hasLeft && !hasRight) ? 'center' : ;
           }
           function go(dir) {
               const max = carousel.scrollWidth - carousel.clientWidth;
               const next = dir < 0
                   ? Math.max(0, carousel.scrollLeft - scrollAmt())
                   : Math.min(max, carousel.scrollLeft + scrollAmt());
               carousel.scrollTo({ left: next, behavior: 'smooth' });
           }
           left.addEventListener('click', () => go(-1));
           right.addEventListener('click', () => go(1));
           carousel.addEventListener('scroll', setState);
           new ResizeObserver(setState).observe(carousel);
           setState();
       })();
       // ===========================
       // Utility Functions
       // ===========================
       function renderAttributes(str) {
           const skillsRoot = document.getElementById('skills');
           const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
           const langRaw = (document.documentElement.lang || skillsRoot?.dataset.i18nDefault || 'pt').toLowerCase();
           const langKey = i18nMap[langRaw] ? langRaw : (i18nMap[langRaw.split('-')[0]] ? langRaw.split('-')[0] : 'pt');
           const L = i18nMap[langKey] || i18nMap.pt || {
               cooldown: 'Recarga', energy_gain: 'Ganho de energia', energy_cost: 'Custo de energia',
               power: 'Poder', power_pvp: 'Poder PvP', level: 'Nível'
           };
           const vals = (str || ).split(',').map(v => v.trim());
           const pve = parseInt(vals[0], 10);
           const pvp = parseInt(vals[1], 10);
           const ene = parseInt(vals[2], 10);
           const cd = parseInt(vals[3], 10);
           const rows = [];
           if (!isNaN(cd)) rows.push([L.cooldown, cd]);
           if (!isNaN(ene) && ene !== 0) {
               const label = ene > 0 ? L.energy_gain : L.energy_cost;
               rows.push([label, Math.abs(ene)]);
           }
           if (!isNaN(pve)) rows.push([L.power, pve]);
           if (!isNaN(pvp)) rows.push([L.power_pvp, pvp]);
           if (!rows.length) return ;
           const html = rows.map(([label, value]) => `
     ${label}
     ${value}

`).join(); return `

${html}

`;

       }
       function syncDescHeight() {
           // no-op on purpose
       }
       // ===========================
       // Event Listeners & Initialization
       // ===========================
       window.addEventListener('resize', syncDescHeight);
       if (videoBox) new ResizeObserver(syncDescHeight).observe(videoBox);
       iconItems.forEach(el => {
           const wired = !!el.dataset._sync_wired;
           if (wired) return;
           el.dataset._sync_wired = '1';
           el.addEventListener('click', () => {
               Promise.resolve().then(syncDescHeight);
           });
       });
       if (iconsBar) addOnce(iconsBar, 'wheel', (e) => {
           if (e.deltaY) {
               e.preventDefault();
               iconsBar.scrollLeft += e.deltaY;
           }
       });
       // Initialize first skill
       if (iconItems.length) {
           const first = iconItems[0];
           if (first) {
               if (!first.classList.contains('active')) first.classList.add('active');
               setTimeout(() => first.click(), 0);
           }
       }
       // ===========================
       // Weapon Tab Setup (if exists)
       // ===========================
       (function initWeaponTab() {
           const weaponTab = document.getElementById('weapon');
           if (!weaponTab) return;
           const weaponIconBar = weaponTab.querySelector('.icon-bar');
           const weaponIconItems = weaponIconBar ? Array.from(weaponIconBar.querySelectorAll('.skill-icon')) : [];
           const weaponDescBox = weaponTab.querySelector('.desc-box');
           const weaponVideoBox = weaponTab.querySelector('.video-container');
           if (!weaponIconBar || !weaponIconItems.length) return;
           const weaponVideosCache = new Map();
           let weaponTotalVideos = 0, weaponLoadedVideos = 0, weaponAutoplay = false;
           // Load weapon videos
           weaponIconItems.forEach(el => {
               const src = (el.dataset.video || ).trim();
               const idx = el.dataset.index || ;
               if (!src || weaponVideosCache.has(idx)) return;
               weaponTotalVideos++;
               const v = document.createElement('video');
               v.className = 'skill-video';
               v.setAttribute('controls', );
               v.setAttribute('preload', 'auto');
               v.setAttribute('playsinline', );
               v.style.display = 'none';
               v.dataset.index = idx;
               v.style.width = '100%';
               v.style.maxWidth = '100%';
               v.style.height = 'auto';
               v.style.aspectRatio = '16/9';
               v.style.objectFit = 'cover';
               const source = document.createElement('source');
               source.src = src;
               source.type = 'video/webm';
               v.appendChild(source);
               v.addEventListener('canplay', () => {
                   weaponLoadedVideos++;
                   if (weaponLoadedVideos === weaponTotalVideos) weaponAutoplay = true;
               });
               v.addEventListener('error', () => {
                   weaponLoadedVideos++;
                   if (weaponLoadedVideos === weaponTotalVideos) weaponAutoplay = true;
               });
               weaponVideoBox.appendChild(v);
               weaponVideosCache.set(idx, v);
           });
           // Wire clicks for weapon icons
           weaponIconItems.forEach(el => {
               if (el.dataset.wired) return;
               el.dataset.wired = '1';
               const label = el.dataset.nome || el.dataset.name || ;
               el.setAttribute('aria-label', label);
               if (el.hasAttribute('title')) el.removeAttribute('title');
               const img = el.querySelector('img');
               if (img) {
                   img.setAttribute('alt', );
                   if (img.hasAttribute('title')) img.removeAttribute('title');
               }
               el.addEventListener('click', () => {
                   const skillsRoot = document.getElementById('skills');
                   const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
                   const lang = getLangKey();
                   const L = i18nMap[lang] || i18nMap.pt || {
                       cooldown: 'Recarga',
                       energy_gain: 'Ganho de energia',
                       energy_cost: 'Custo de energia',
                       power: 'Poder',
                       power_pvp: 'Poder PvP',
                       level: 'Nível'
                   };
                   const name = el.dataset.nome || el.dataset.name || ;
                   const level = (el.dataset.level || ).trim();
                   const descPack = {
                       pt: el.dataset.descPt || ,
                       en: el.dataset.descEn || ,
                       es: el.dataset.descEs || ,
                       pl: el.dataset.descPl || 
                   };
                   const chosenDesc = descPack[lang] || descPack.pt || descPack.en || descPack.es || descPack.pl || el.dataset.desc || ;
                   const descHtml = chosenDesc.replace(/(.*?)/g, '$1');
                   const attrsHTML = el.dataset.atr ? renderAttributes(el.dataset.atr) : ;
                   if (weaponDescBox) {
                       weaponDescBox.innerHTML = `

${name}

${level ? `

${L.level} ${level}

` : }

       ${attrsHTML}
${descHtml}

`;

                   }
                   weaponIconItems.forEach(i => i.classList.remove('active'));
                   el.classList.add('active');
                   if (!weaponAutoplay && weaponLoadedVideos > 0) weaponAutoplay = true;
                   window.__lastActiveSkillIcon = el;
                   // Show video
                   if (weaponVideoBox) {
                       Array.from(weaponVideoBox.querySelectorAll('video.skill-video')).forEach(v => {
                           try { v.pause(); } catch (e) { }
                           v.style.display = 'none';
                       });
                   }
                   const hasIdx = !!el.dataset.index;
                   const hasVideo = !!(el.dataset.video && el.dataset.video.trim() !== );
                   if (!weaponVideoBox || !hasVideo) {
                       if (weaponVideoBox) weaponVideoBox.style.display = 'none';
                       return;
                   }
                   if (hasIdx && weaponVideosCache.has(el.dataset.index)) {
                       const v = weaponVideosCache.get(el.dataset.index);
                       weaponVideoBox.style.display = 'block';
                       v.style.display = 'block';
                       try { v.currentTime = 0; } catch (e) { }
                       const suppress = document.body.dataset.suppressSkillPlay === '1';
                       if (!suppress && weaponAutoplay) v.play().catch(() => { });
                   }
               });
           });
           // Wire tooltips for weapon icons
           const tip = document.querySelector('.skill-tooltip');
           if (tip) {
               weaponIconItems.forEach(icon => {
                   if (icon.dataset.tipwired) return;
                   icon.dataset.tipwired = '1';
                   const label = icon.dataset.nome || icon.dataset.name || icon.title || ;
                   if (label && !icon.hasAttribute('aria-label')) icon.setAttribute('aria-label', label);
                   if (icon.hasAttribute('title')) icon.removeAttribute('title');
                   const measureAndPos = (el) => {
                       if (!el || tip.getAttribute('aria-hidden') === 'true') return;
                       tip.style.left = '0px';
                       tip.style.top = '0px';
                       const rect = el.getBoundingClientRect();
                       const tr = tip.getBoundingClientRect();
                       let left = Math.round(rect.left + (rect.width - tr.width) / 2);
                       left = Math.max(8, Math.min(left, window.innerWidth - tr.width - 8));
                       const coarse = (window.matchMedia && matchMedia('(pointer: coarse)').matches) || (window.innerWidth <= 600);
                       let top = coarse ? Math.round(rect.bottom + 10) : Math.round(rect.top - tr.height - 8);
                       if (top < 8) top = Math.round(rect.bottom + 10);
                       tip.style.left = left + 'px';
                       tip.style.top = top + 'px';
                   };
                   const show = (el, text) => {
                       tip.textContent = text || ;
                       tip.setAttribute('aria-hidden', 'false');
                       measureAndPos(el);
                       tip.style.opacity = '1';
                   };
                   const hide = () => {
                       tip.setAttribute('aria-hidden', 'true');
                       tip.style.opacity = '0';
                       tip.style.left = '-9999px';
                       tip.style.top = '-9999px';
                   };
                   icon.addEventListener('mouseenter', () => show(icon, label));
                   icon.addEventListener('mousemove', () => measureAndPos(icon));
                   icon.addEventListener('click', () => { window.__globalSkillTooltip.lockUntil.value = performance.now() + 240; measureAndPos(icon); });
                   icon.addEventListener('mouseleave', hide);
               });
           }
           // Do not pre-mark active here; activation handled on tab enter
       })();
       // Debug logging
       setTimeout(() => {
           Array.from(document.querySelectorAll('.skill-icon')).forEach(el => {
               console.log('icon', el.dataset.index, 'data-video=', el.dataset.video);
           });
           videosCache.forEach((v, idx) => {
               const src = v.querySelector('source') ? v.querySelector('source').src : v.src;
               console.log('video element', idx, 'src=', src, 'readyState=', v.readyState);
               v.addEventListener('error', (ev) => {
                   console.error('VIDEO ERROR idx=', idx, 'src=', src, 'error=', v.error);
               });
               v.addEventListener('loadedmetadata', () => {
                   console.log('loadedmetadata idx=', idx, 'dimensions=', v.videoWidth, 'x', v.videoHeight);
               });
           });
       }, 600);
   })();

</script>