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

De Wiki Gla
Ir para navegação Ir para pesquisar
m (wallpass)
m (wallpass certo (atualizei o codigo do .skill no .subskill :| ))
Linha 1: Linha 1:
<!-- MAIN SKILLS SYSTEM -->
<!-- SUBSKILLS SYSTEM -->
<script>
<script>
     (function () {
     (function () {
         const $ = (s, root = document) => root.querySelector(s);
         const SUBVIDEO_DEBUG = true;
        const $$ = (s, root = document) => Array.from(root.querySelectorAll(s));
         function logSubVideo(...args) {
        const ensureRemoved = sel => {
             if (!SUBVIDEO_DEBUG) return;
            Array.from(document.querySelectorAll(sel)).forEach(n => n.remove());
             console.log('[SubVideo]', ...args);
        };
        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');
        };
        const FLAG_ICON_FILES = {
            aggro: 'Enemyaggro-icon.png', bridge: 'Bridgemaker-icon.png', wall: 'Destroywall-icon.png', quickcast: 'Quickcast-icon.png', wallpass: 'Passthroughwall-icon.png'
        };
        const subBarTemplateCache = window.__skillSubBarTemplateCache || (window.__skillSubBarTemplateCache = new Map());
        const imagePreloadCache = window.__skillImagePreloadCache || (window.__skillImagePreloadCache = new Map());
        const videoPreloadCache = window.__skillVideoPreloadCache || (window.__skillVideoPreloadCache = new Set());
        const flagRowCache = window.__skillFlagRowCache || (window.__skillFlagRowCache = new Map());
        const flagIconURLCache = window.__skillFlagIconURLCache || (window.__skillFlagIconURLCache = new Map());
        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 slugify(s) {
            if (!s) return '';
            return String(s).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^\w\s-]/g, '').replace(/[\s:/\-]+/g, '-').replace(/^-+|-+$/g, '').replace(/-+/g, '-');
        } window.__skillSlugify = slugify;
        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();
            // Aceita tanto desc_i18n quanto desc para compatibilidade
            const pack = obj.desc_i18n || obj.desc || {
                pt: obj.descPt, en: obj.descEn, es: obj.descEs, pl: obj.descPl
            };
            return (pack && (pack[lang] || pack.pt || pack.en || pack.es || pack.pl)) || '';
        } 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.startsWith('-') ? en.replace(/^-/, '') : 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>` : '';
        } function getFlagIconURL(key) {
            if (!FLAG_ICON_FILES[key]) return '';
            if (!flagIconURLCache.has(key)) {
                flagIconURLCache.set(key, filePathURL(FLAG_ICON_FILES[key]));
            } return flagIconURLCache.get(key);
        } function renderFlagsRow(flags) {
            const arr = (flags || []).filter(Boolean);
            if (!arr.length) return '';
            const cacheKey = arr.join('|');
            if (flagRowCache.has(cacheKey)) {
                return flagRowCache.get(cacheKey);
            } const items = arr.map(k => {
                const url = getFlagIconURL(k);
                return url ? `<img class="skill-flag" data-flag="${k}" alt="" src="${url}">` : '';
            }).join('');
            const html = items ? `<div class="skill-flags" role="group" aria-label="Características">${items}</div>` : '';
            if (html) flagRowCache.set(cacheKey, html);
            return html;
        } function applyFlagTooltips(container) {
            const skillsRoot = document.getElementById('skills');
            if (!skillsRoot) return;
            let pack = {
            };
            try {
                pack = JSON.parse(skillsRoot.dataset.i18nFlags || '{}');
            } catch (e) {
            } const lang = getLangKey();
            const dict = pack[lang] || pack.pt || {
            };
            const flags = container.querySelectorAll('.skill-flags .skill-flag[data-flag]');
            const tooltip = window.__globalSkillTooltip;
             if (!tooltip) return;
             flags.forEach(el => {
                const key = el.getAttribute('data-flag');
                const tip = (dict && dict[key]) || '';
                if (!tip) return;
                if (el.dataset.flagTipWired) return;
                el.dataset.flagTipWired = '1';
                el.setAttribute('aria-label', tip);
                if (el.hasAttribute('title')) el.removeAttribute('title');
                el.addEventListener('mouseenter', () => {
                    const tipEl = document.querySelector('.skill-tooltip');
                    if (tipEl) tipEl.classList.add('flag-tooltip');
                    tooltip.show(el, tip);
                });
                el.addEventListener('mousemove', () => {
                    if (performance.now() >= tooltip.lockUntil.value) {
                        tooltip.measureAndPos(el);
                    }
                });
                el.addEventListener('click', () => {
                    tooltip.lockUntil.value = performance.now() + 240;
                    tooltip.measureAndPos(el);
                });
                el.addEventListener('mouseleave', () => {
                    const tipEl = document.querySelector('.skill-tooltip');
                    if (tipEl) tipEl.classList.remove('flag-tooltip');
                    tooltip.hide();
                });
            });
         }
         }


         // ====== Skill/Subskill inheritance helpers ======
         const api = (window.__subskills ||= {});
         const mainSkillsMeta = {
        // Cache global de elementos de vídeo para subskills - baseado em URL
            byIndex: new Map(),
         const subskillVideoElementCache = new Map(); // key: videoURL (string), value: HTMLVideoElement
            byName: new Map(),
        const imagePreloadCache = new Map();
            ready: false
         let subRail, subBar, spacer;
         };


         function normalizeFileURL(raw, fallback = '') {
         // Cache das skills principais (capturado na carga da página)
            if (!raw) return fallback;
        let cachedMainSkills = null;
            const val = String(raw).trim();
            if (!val) return fallback;
            if (/^(https?:)?\/\//i.test(val) || val.startsWith('data:') || val.includes('Especial:FilePath/')) {
                return val;
            } return filePathURL(val);
        }


         function extractFileNameFromURL(url) {
        // ===== HERANÇA DE ATRIBUTOS: busca dados das skills principais =====
             if (!url) return '';
         function getMainSkillsMap() {
             const match = String(url).match(/(?:FilePath\/)([^&?]+)/i);
             // Retorna cache se já foi construído E tem dados
            return match ? decodeURIComponent(match[1]) : '';
             if (cachedMainSkills && cachedMainSkills.byIndex.size > 0) {
        }
                return cachedMainSkills;
            }


        function parseAttrString(raw) {
             const maps = {
             const parts = (raw || '').split(',').map(v => v.trim());
                byName: new Map(),
            const safe = idx => {
                 byIndex: new Map()
                 const val = parts[idx] || '';
                return (val && val !== '-') ? val : '';
             };
             };
            return {
                powerpve: safe(0),
                powerpvp: safe(1),
                energy: safe(2),
                cooldown: safe(3)
            };
        }


        function hasText(value) {
            // Busca skills com data-index (skills principais, não subskills)
             return typeof value === 'string' ? value.trim() !== '' : value !== undefined && value !== null;
             const icons = document.querySelectorAll('.icon-bar .skill-icon[data-index][data-nome]');
        }
 
            icons.forEach(icon => {
                const name = (icon.dataset.nome || '').trim();
                if (!name) return;


        function pickFilled(current, fallback) {
                // Extrai atributos do data-atr (formato: "pve, pvp, energy, cd")
            if (current === 0 || current === '0') return current;
                const atrRaw = icon.dataset.atr || '';
            if (!hasText(current)) return fallback;
                const parts = atrRaw.split(',').map(x => (x || '').trim());
            return current;
                const powerpve = parts[0] && parts[0] !== '-' ? parts[0] : '';
        }
                const powerpvp = parts[1] && parts[1] !== '-' ? parts[1] : '';
                const energy = parts[2] && parts[2] !== '-' ? parts[2] : '';
                const cooldown = parts[3] && parts[3] !== '-' ? parts[3] : '';


        function buildMainSkillsMeta(nodes) {
                 // Nome original do arquivo de ícone (armazenado no dataset pelo widget de skills)
            if (mainSkillsMeta.ready) {
                return mainSkillsMeta;
            }
            (nodes || []).forEach(icon => {
                const index = (icon.dataset.index || '').trim();
                if (!index) return;
                const name = (icon.dataset.nome || icon.dataset.name || '').trim();
                 const attrs = parseAttrString(icon.dataset.atr || '');
                 let iconFile = (icon.dataset.iconFile || '').trim();
                 let iconFile = (icon.dataset.iconFile || '').trim();
                 if (!iconFile) {
                 if (!iconFile) {
Linha 178: Linha 51:
                     iconFile = iconMatch ? decodeURIComponent(iconMatch[1]) : '';
                     iconFile = iconMatch ? decodeURIComponent(iconMatch[1]) : '';
                 }
                 }
                // Nome original do arquivo de vídeo (caso exista)
                 let videoFile = (icon.dataset.videoFile || '').trim();
                 let videoFile = (icon.dataset.videoFile || '').trim();
                 if (!videoFile) {
                 if (!videoFile) {
                     videoFile = extractFileNameFromURL(icon.dataset.video || '');
                     const videoUrl = icon.dataset.video || '';
                    const videoMatch = videoUrl.match(/FilePath\/([^&?]+)/);
                    videoFile = videoMatch ? decodeURIComponent(videoMatch[1]) : '';
                 }
                 }
                 const meta = {
 
                     index,
                 const index = (icon.dataset.index || '').trim();
                    name,
 
                     icon: iconFile || 'Nada.png',
                const data = {
                     name: name, // Inclui o nome para herança completa
                     icon: iconFile,
                     level: icon.dataset.level || '',
                     level: icon.dataset.level || '',
                     video: videoFile || '',
                     video: videoFile,
                     powerpve: attrs.powerpve || '',
                     powerpve: powerpve,
                     powerpvp: attrs.powerpvp || '',
                     powerpvp: powerpvp,
                    energy: attrs.energy || '',
                     cooldown: cooldown,
                     cooldown: attrs.cooldown || '',
                     energy: energy
                     desc: icon.dataset.desc || '',
                    descPt: icon.dataset.descPt || '',
                    descEn: icon.dataset.descEn || '',
                    descEs: icon.dataset.descEs || '',
                    descPl: icon.dataset.descPl || ''
                 };
                 };
                 mainSkillsMeta.byIndex.set(index, meta);
 
                 mainSkillsMeta.byIndex.set(parseInt(index, 10), meta);
                // Mantém descrições caso precise como fallback extra
                 if (name) {
                if (icon.dataset.descPt) data.descPt = icon.dataset.descPt;
                     mainSkillsMeta.byName.set(name, meta);
                 if (icon.dataset.descEn) data.descEn = icon.dataset.descEn;
                if (icon.dataset.descEs) data.descEs = icon.dataset.descEs;
                if (icon.dataset.descPl) data.descPl = icon.dataset.descPl;
 
                 maps.byName.set(name, data);
                 if (index) {
                     // Guarda tanto como string quanto como número para compatibilidade
                    maps.byIndex.set(index, data);
                    maps.byIndex.set(parseInt(index, 10), data);
                 }
                 }
             });
             });
             mainSkillsMeta.ready = true;
 
             return mainSkillsMeta;
            // Cacheia para uso futuro (importante: as skills principais não mudam)
             cachedMainSkills = maps;
             return maps;
         }
         }


         function inheritSubskillFromMain(sub, meta) {
         // Aplica herança COMPLETA: se subskill só tem refM, busca TUDO da skill principal
            if (!sub || !meta) return sub;
        function applyInheritance(sub, mainSkills) {
            // Suporta refS (novo) e refM (legado)
            const refS = ((sub.refS || sub.S || sub.s || '') + '').trim();
            const refIndex = ((sub.refM || sub.M || sub.m || '') + '').trim();
             let name = (sub.name || sub.n || '').trim();
             let name = (sub.name || sub.n || '').trim();
            const refIndex = ((sub.refM || sub.m || sub.M || '') + '').trim();
             let main = null;
             let main = null;
 
             // Tenta como string
 
             if (refIndex && mainSkills.byIndex.has(refIndex)) {
             // Primeiro tenta por refS
                 main = mainSkills.byIndex.get(refIndex);
             if (refS) {
                 main = meta.byIndex.get(refS) || meta.byIndex.get(parseInt(refS, 10));
             }
             }
             // Depois por refM
             // Tenta como número
             if (!main && refIndex) {
             if (!main && refIndex) {
                 main = meta.byIndex.get(refIndex) || meta.byIndex.get(parseInt(refIndex, 10));
                 const numIndex = parseInt(refIndex, 10);
                if (!isNaN(numIndex) && mainSkills.byIndex.has(numIndex)) {
                    main = mainSkills.byIndex.get(numIndex);
                }
             }
             }
            // Por último pelo nome
             if (!main && name && mainSkills.byName.has(name)) {
             if (!main && name) {
                 main = mainSkills.byName.get(name);
                 main = meta.byName.get(name);
             }
             }
            // Se não tem main skill para herdar, retorna como está
             if (!main) {
             if (!main) {
                 return sub;
                 return sub;
             }
             }


             const hydrated = { ...sub };
             // Se não tem nome mas tem refM, herda o nome da skill principal
             if (!name && main.name) {
             if (!name && refIndex && main.name) {
                 name = main.name;
                 name = main.name;
             }
             }
            hydrated.name = name || hydrated.name || main.name || '';
            hydrated.icon = pickFilled(hydrated.icon, main.icon || 'Nada.png');
            hydrated.level = pickFilled(hydrated.level, main.level || '');
            hydrated.video = pickFilled(hydrated.video, main.video || '');
            hydrated.powerpve = pickFilled(hydrated.powerpve, main.powerpve || '');
            hydrated.powerpvp = pickFilled(hydrated.powerpvp, main.powerpvp || '');
            hydrated.energy = pickFilled(hydrated.energy, main.energy || '');
            hydrated.cooldown = pickFilled(hydrated.cooldown, main.cooldown || '');


             if (!hasText(hydrated.descPt) && hasText(main.descPt)) hydrated.descPt = main.descPt;
             return {
            if (!hasText(hydrated.descEn) && hasText(main.descEn)) hydrated.descEn = main.descEn;
                ...sub,
            if (!hasText(hydrated.descEs) && hasText(main.descEs)) hydrated.descEs = main.descEs;
                name: name || main.name || sub.name,
            if (!hasText(hydrated.descPl) && hasText(main.descPl)) hydrated.descPl = main.descPl;
                icon: (sub.icon && sub.icon !== 'Nada.png' && sub.icon !== '') ? sub.icon : (main.icon || 'Nada.png'),
             if (!hasText(hydrated.desc) && hasText(main.desc)) hydrated.desc = main.desc;
                level: sub.level || main.level,
                video: sub.video || main.video,
                powerpve: sub.powerpve || main.powerpve,
                powerpvp: sub.powerpvp || main.powerpvp,
                cooldown: sub.cooldown || main.cooldown,
                energy: sub.energy || main.energy,
                descPt: sub.descPt || (sub.desc_i18n?.pt) || main.descPt,
                descEn: sub.descEn || (sub.desc_i18n?.en) || main.descEn,
                descEs: sub.descEs || (sub.desc_i18n?.es) || main.descEs,
                descPl: sub.descPl || (sub.desc_i18n?.pl) || main.descPl
             };
        }


             if (!hydrated.desc_i18n && (hydrated.descPt || hydrated.descEn || hydrated.descEs || hydrated.descPl)) {
        function filePathURL(fileName) {
                hydrated.desc_i18n = {
             const f = encodeURIComponent((fileName || 'Nada.png').replace(/^Arquivo:|^File:/, ''));
                    pt: hydrated.descPt || '',
            const base = (window.mw && mw.util && typeof mw.util.wikiScript === 'function')
                    en: hydrated.descEn || '',
                ? mw.util.wikiScript()
                    es: hydrated.descEs || '',
                : (window.mw && mw.config ? (mw.config.get('wgScript') || '/index.php') : '/index.php');
                    pl: hydrated.descPl || ''
             return `${base}?title=Especial:FilePath/${f}`;
                };
             }
 
            return hydrated;
         }
         }


         function inheritSubskillTree(subs, meta) {
         function normalizeFileURL(raw, fallback = '') {
             if (!Array.isArray(subs)) return [];
             if (!raw) return fallback;
             return subs.map(sub => {
             const val = String(raw).trim();
                const hydrated = inheritSubskillFromMain(sub, meta);
            if (!val) return fallback;
                if (Array.isArray(hydrated.subs)) {
            if (/^(https?:)?\/\//i.test(val) || val.startsWith('data:') || val.includes('Especial:FilePath/')) {
                    hydrated.subs = inheritSubskillTree(hydrated.subs, meta);
                 return val;
                }
             }
                 return hydrated;
            return filePathURL(val);
             });
         }
         }


         function collectAssetsFromSubs(subs, iconsSet, videosSet, flagsSet) {
         function preloadImage(iconFile) {
             if (!Array.isArray(subs)) return;
             const url = filePathURL(iconFile || 'Nada.png');
            subs.forEach(sub => {
            if (imagePreloadCache.has(url)) {
                const iconURL = normalizeFileURL(sub.icon || 'Nada.png', filePathURL('Nada.png'));
                 return imagePreloadCache.get(url);
                if (iconURL && iconURL !== filePathURL('Nada.png')) iconsSet.add(iconURL);
             }
                // Vídeo normal
             const promise = new Promise((resolve, reject) => {
                if (sub.video && sub.video.trim() !== '' && sub.video !== 'Nada.png' && !sub.video.toLowerCase().includes('nada.png')) {
                    const videoURL = normalizeFileURL(sub.video);
                    if (videoURL) videosSet.add(videoURL);
                }
                // Vídeo de weapon
                if (sub.weapon && typeof sub.weapon === 'object' && sub.weapon.video && sub.weapon.video.trim() !== '' && sub.weapon.video !== 'Nada.png' && !sub.weapon.video.toLowerCase().includes('nada.png')) {
                    const weaponVideoURL = normalizeFileURL(sub.weapon.video);
                    if (weaponVideoURL) videosSet.add(weaponVideoURL);
                }
                if (Array.isArray(sub.flags)) {
                    sub.flags.forEach(flagKey => {
                        const url = getFlagIconURL(flagKey);
                        if (url) flagsSet.add(url);
                    });
                } if (Array.isArray(sub.subs)) {
                    collectAssetsFromSubs(sub.subs, iconsSet, videosSet, flagsSet);
                }
            });
        } function buildAssetManifest() {
            if (window.__skillAssetManifest && window.__skillAssetManifest.ready) {
                 return window.__skillAssetManifest;
            } const iconsSet = new Set();
            const videosSet = new Set();
            const flagsSet = new Set();
            iconItems.forEach(el => {
                const img = el.querySelector('img');
                if (img && img.src) {
                    iconsSet.add(img.src);
                } else if (el.dataset.iconFile) {
                    const iconURL = normalizeFileURL(el.dataset.iconFile);
                    if (iconURL) iconsSet.add(iconURL);
                }
                // Vídeo normal da skill
                const videoRaw = (el.dataset.videoFile || el.dataset.video || '').trim();
                if (videoRaw && videoRaw !== 'Nada.png' && !videoRaw.toLowerCase().includes('nada.png')) {
                    const videoURL = normalizeFileURL(videoRaw);
                    if (videoURL) videosSet.add(videoURL);
                }
                // Vídeo de weapon da skill
                if (el.dataset.weapon) {
                    try {
                        const weaponData = JSON.parse(el.dataset.weapon);
                        if (weaponData && weaponData.video && weaponData.video.trim() !== '' && weaponData.video !== 'Nada.png' && !weaponData.video.toLowerCase().includes('nada.png')) {
                            const weaponVideoURL = normalizeFileURL(weaponData.video);
                            if (weaponVideoURL) videosSet.add(weaponVideoURL);
                        }
                    } catch (e) {
                    }
                }
                if (el.dataset.flags) {
                    try {
                        const parsedFlags = JSON.parse(el.dataset.flags);
                        (parsedFlags || []).forEach(flagKey => {
                            const url = getFlagIconURL(flagKey);
                            if (url) flagsSet.add(url);
                        });
                    } catch (e) {
                    }
                } if (el.dataset.subs) {
                    try {
                        const subs = JSON.parse(el.dataset.subs);
                        collectAssetsFromSubs(subs, iconsSet, videosSet, flagsSet);
                    } catch (e) {
                    }
                }
             });
             Object.keys(FLAG_ICON_FILES).forEach(flagKey => {
                const url = getFlagIconURL(flagKey);
                if (url) flagsSet.add(url);
            });
            const manifest = {
                icons: iconsSet, videos: videosSet, flags: flagsSet, ready: true
            };
            window.__skillAssetManifest = manifest;
            return manifest;
        } function preloadImagesFromSet(set) {
            if (!set || !set.size) return;
            set.forEach(url => {
                if (!url || imagePreloadCache.has(url)) return;
                 const img = new Image();
                 const img = new Image();
                 img.decoding = 'async';
                 img.decoding = 'async';
                 img.loading = 'eager';
                 img.loading = 'eager';
                 img.referrerPolicy = 'same-origin';
                 img.referrerPolicy = 'same-origin';
                img.onload = () => resolve(url);
                img.onerror = () => resolve(url);
                 img.src = url;
                 img.src = url;
                imagePreloadCache.set(url, img);
             });
             });
        } function preloadVideosFromSet(set) {
             imagePreloadCache.set(url, promise);
            if (!set || !set.size) return;
             return promise;
             const head = document.head || document.getElementsByTagName('head')[0];
            set.forEach(url => {
                if (!url || videoPreloadCache.has(url)) return;
                const link = document.createElement('link');
                link.rel = 'preload';
                link.as = 'video';
                link.href = url;
                link.crossOrigin = 'anonymous';
                head.appendChild(link);
                videoPreloadCache.add(url);
            });
        } const subskillVideosCache = new Map();
        window.__subskillVideosCache = subskillVideosCache;
        let assetManifest = null;
        const skillsTab = $('#skills');
        const skinsTab = $('#skins');
        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();
            }
        });
        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);
        } 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);
            }
        } const iconsBar = $('#skills') ? $('.icon-bar', $('#skills')) : null;
        const skillsTopRail = iconsBar ? iconsBar.closest('.top-rail.skills') : null;
        const iconItems = iconsBar ? Array.from(iconsBar.querySelectorAll('.skill-icon')) : [];
        buildMainSkillsMeta(iconItems);
        // Verifica se há weapon em skills principais OU em subskills
        function checkHasAnyWeapon() {
            // Verifica skills principais
            if (iconItems.some(el => !!el.dataset.weapon)) {
                return true;
            }
            // Verifica subskills
            for (const el of iconItems) {
                const subsRaw = el.getAttribute('data-subs');
                if (!subsRaw) continue;
                try {
                    const subs = JSON.parse(subsRaw);
                    if (Array.isArray(subs) && subs.some(s => s && s.weapon)) {
                        return true;
                    }
                } catch (e) { }
            }
             return false;
         }
         }
        const hasWeaponSkillAvailable = checkHasAnyWeapon();
 
         let weaponToggleBtn = null;
         function getLabels() {
        if (!assetManifest) {
             const skillsRoot = document.getElementById('skills');
             assetManifest = buildAssetManifest();
             const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
             preloadImagesFromSet(assetManifest.icons);
             const raw = (document.documentElement.lang || skillsRoot?.dataset.i18nDefault || 'pt').toLowerCase();
            preloadImagesFromSet(assetManifest.flags);
            const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
             preloadVideosFromSet(assetManifest.videos);
            return i18nMap[lang] || i18nMap.pt || {
        } const descBox = $('#skills') ? $('.desc-box', $('#skills')) : null;
                cooldown: 'Recarga',
        const videoBox = $('#skills') ? $('.video-container', $('#skills')) : null;
                energy_gain: 'Ganho de energia',
        const videosCache = new Map();
                energy_cost: 'Custo de energia',
        const nestedVideoElByIcon = new WeakMap();
                power: 'Poder',
        const barStack = [];
                power_pvp: 'Poder PvP',
        window.__barStack = barStack;
                 level: 'Nível'
        let initialBarSnapshot = null;
             };
        let totalVideos = 0, loadedVideos = 0, autoplay = false;
        window.__lastActiveSkillIcon = null;
        let userHasInteracted = false;
        let globalWeaponEnabled = false;
        try {
            if (localStorage.getItem('glaWeaponEnabled') === '1') {
                 globalWeaponEnabled = true;
             }
        } catch (err) {
         }
         }
        const weaponStateListeners = new Set();
 
        let showWeaponPopupFn = null;
         // Verifica se o modo weapon está ativo
        let popupShouldOpen = false;
         function isWeaponModeOn() {
        function attachWeaponPopupFn(fn) {
            if (typeof fn !== 'function') return;
            showWeaponPopupFn = fn;
            if (popupShouldOpen) {
                popupShouldOpen = false;
                try {
                    showWeaponPopupFn();
                } catch (err) {
                }
            }
        }
         attachWeaponPopupFn(window.__glaWeaponShowPopup);
         function requestWeaponPopupDisplay() {
             try {
             try {
                 if (localStorage.getItem('glaWeaponPopupDismissed') === '1') return;
                 return localStorage.getItem('glaWeaponEnabled') === '1';
             } catch (err) {
             } catch (e) {
            }
                 return false;
            if (typeof showWeaponPopupFn === 'function') {
                showWeaponPopupFn();
                 return;
             }
             }
            popupShouldOpen = true;
         }
         }
         function onWeaponStateChange(fn) {
 
             if (typeof fn !== 'function') return;
        // Retorna os atributos corretos (weapon ou normal)
             weaponStateListeners.add(fn);
         function getEffectiveAttrs(s) {
        }
             const weaponOn = isWeaponModeOn();
        function syncWeaponButtonState(enabled) {
             if (weaponOn && s.weapon) {
            if (!weaponToggleBtn || !weaponToggleBtn.isConnected) return;
                return {
            // Usa .weapon-active em vez de .active para não conflitar com skills
                    powerpve: s.weapon.powerpve || s.powerpve,
            weaponToggleBtn.classList.toggle('weapon-active', !!enabled);
                    powerpvp: s.weapon.powerpvp || s.powerpvp,
            weaponToggleBtn.classList.remove('active'); // Garante que .active nunca seja aplicado
                    energy: s.weapon.energy || s.energy,
            weaponToggleBtn.setAttribute('aria-pressed', enabled ? 'true' : 'false');
                    cooldown: s.weapon.cooldown || s.cooldown
            weaponToggleBtn.setAttribute('aria-label', enabled ? 'Desativar Arma Especial' : 'Ativar Arma Especial');
                };
        }
        function syncWeaponRailState(enabled) {
            if (skillsTopRail) {
                skillsTopRail.classList.toggle('weapon-mode-on', !!enabled);
             }
             }
            return {
                powerpve: s.powerpve,
                powerpvp: s.powerpvp,
                energy: s.energy,
                cooldown: s.cooldown
            };
         }
         }
        function notifyWeaponStateListeners(enabled) {
            weaponStateListeners.forEach(listener => {
                try {
                    listener(enabled);
                } catch (err) {
                }
            });
        }
        let pendingWeaponState = null;
        window.addEventListener('weapon:ready', (ev) => {
            if (ev && ev.detail && ev.detail.showPopup) {
                attachWeaponPopupFn(ev.detail.showPopup);
            }
            if (pendingWeaponState === null) return;
            if (typeof window.__applyWeaponState === 'function') {
                const target = pendingWeaponState;
                pendingWeaponState = null;
                window.__applyWeaponState(target);
            }
        });
        window.__setGlobalWeaponEnabled = (enabled) => {
            globalWeaponEnabled = enabled;
            notifyWeaponStateListeners(enabled);
        };
        function requestWeaponState(targetState) {
            if (typeof window.__applyWeaponState === 'function') {
                pendingWeaponState = null;
                window.__applyWeaponState(targetState);
                return;
            }
            pendingWeaponState = targetState;
        }
        onWeaponStateChange(syncWeaponButtonState);
        function reapplyWeaponClassesToBar() {
            if (!globalWeaponEnabled) return;
            // SISTEMA UNIFICADO: Aplica em skills E subskills
            iconsBar.querySelectorAll('.skill-icon[data-weapon], .subicon[data-weapon]').forEach(el => {
                if (!el.classList.contains('has-weapon-available')) {
                    el.classList.add('has-weapon-available');
                }
                if (!el.querySelector('.weapon-indicator')) {
                    const ind = document.createElement('div');
                    ind.className = 'weapon-indicator';
                    el.appendChild(ind);
                }
            });
        }
        function setupWeaponBarToggle(shouldShow) {
            if (!shouldShow || !iconsBar) return;
            if (iconsBar.querySelector('.weapon-bar-toggle')) return;


            // PRIORIDADE 1: Busca weaponicon global do Character (data-weaponicon no .character-box)
        // Retorna a descrição correta (weapon ou normal)
             let weaponIcon = null;
        // Aceita tanto desc_i18n quanto desc para compatibilidade
             let weaponName = 'Arma Especial';
        function getEffectiveDesc(s) {
             const weaponOn = isWeaponModeOn();
             const raw = (document.documentElement.lang || 'pt').toLowerCase();
            const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');


             const characterBox = document.querySelector('.character-box');
             // Para weapon: aceita tanto desc_i18n quanto desc
             if (characterBox && characterBox.dataset.weaponicon) {
             if (weaponOn && s.weapon) {
                 const globalIcon = characterBox.dataset.weaponicon.trim();
                 const wDesc = s.weapon.desc_i18n || s.weapon.desc;
                 if (globalIcon && globalIcon !== '' && globalIcon !== 'Nada.png') {
                 if (wDesc) {
                     weaponIcon = globalIcon;
                     return wDesc[lang] || wDesc.pt || wDesc.en || '';
                 }
                 }
             }
             }


             // PRIORIDADE 2: Se não encontrou no character, busca em skills principais (data-weaponicon)
             // Para descrição base: aceita tanto desc_i18n quanto desc
             if (!weaponIcon) {
             const base = s.desc_i18n || s.desc;
                const firstWithWeaponIcon = document.querySelector('.skill-icon[data-weaponicon]');
            if (base) {
                if (firstWithWeaponIcon && firstWithWeaponIcon.dataset.weaponicon && firstWithWeaponIcon.dataset.weaponicon !== 'Nada.png') {
                return base[lang] || base.pt || base.en || '';
                    weaponIcon = firstWithWeaponIcon.dataset.weaponicon;
                    // Tenta pegar o nome da arma também
                    try {
                        const weaponData = JSON.parse(firstWithWeaponIcon.getAttribute('data-weapon') || '{}');
                        if (weaponData.name) {
                            weaponName = weaponData.name;
                        }
                    } catch (e) { }
                }
             }
             }


             // PRIORIDADE 3: Se não encontrou, busca weaponicon em subskills (dentro do JSON de data-subs)
             // Fallback para campos individuais (compatibilidade)
             if (!weaponIcon) {
             const descI18n = {
                 const skillsWithSubs = document.querySelectorAll('.skill-icon[data-subs]');
                 pt: s.descPt || '',
                 for (const el of skillsWithSubs) {
                en: s.descEn || '',
                    try {
                 es: s.descEs || '',
                        const subs = JSON.parse(el.getAttribute('data-subs') || '[]');
                pl: s.descPl || ''
                        for (const s of subs) {
            };
                            // Busca weaponicon diretamente no objeto da subskill
            return descI18n[lang] || descI18n.pt || '';
                            if (s && s.weaponicon && s.weaponicon.trim() !== '' && s.weaponicon !== 'Nada.png') {
        }
                                weaponIcon = s.weaponicon;
 
                                if (s.weapon && s.weapon.name) weaponName = s.weapon.name;
        // Retorna o vídeo correto (weapon ou normal)
                                break;
        function getEffectiveVideo(s) {
                            }
            const weaponOn = isWeaponModeOn();
                        }
            if (weaponOn && s.weapon && s.weapon.video && s.weapon.video.trim() !== '') {
                        if (weaponIcon) break;
                return s.weapon.video;
                    } catch (e) { }
                }
             }
             }
            return s.video || '';
        }


            const btn = document.createElement('button');
        // Função única para obtenção de vídeo de subskill - baseada em URL
             btn.type = 'button';
        function getOrCreateSubskillVideo(videoURL) {
            btn.className = 'skill-icon weapon-bar-toggle';
             if (!videoURL || videoURL.trim() === '') return null;
            btn.dataset.weaponToggle = '1';
            btn.dataset.nome = weaponName;
            btn.setAttribute('aria-pressed', 'false');
            btn.setAttribute('aria-label', weaponName);


             // Se tem weaponicon global do character, deixa o Character.WeaponToggle.html aplicar o ícone
             const normalizedURL = normalizeFileURL(videoURL);
            // Caso contrário, usa o ícone encontrado em skills/subskills ou SVG de fallback
             if (!normalizedURL || normalizedURL.trim() === '') return null;
            if (weaponIcon && characterBox && characterBox.dataset.weaponicon) {
                // Ícone global do character - deixa vazio para o Character.WeaponToggle.html aplicar
                btn.innerHTML = '';
            } else if (weaponIcon) {
                // Ícone de skill/subskill - aplica diretamente
                const imgSrc = weaponIcon.startsWith('http') ? weaponIcon : `/images/${weaponIcon}`;
                btn.innerHTML = `<img src="${imgSrc}" alt="${weaponName}" class="weapon-toggle-icon" onerror="this.style.display='none';this.nextElementSibling.style.display='block';" /><svg style="display:none" width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path fill="currentColor" d="M19.14 12.94c.04-.31.06-.63.06-.94s-.02-.63-.06-.94l2.03-1.58a.5.5 0 00.12-.62l-1.92-3.32a.5.5 0 00-.61-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.37-2.48a.55.55 0 00-.55-.5h-3.82a.55.55 0 00-.55.5l-.37 2.48c-.59.24-1.12.56-1.62.94l-2.39-.96a.5.5 0 00-.61.22L3.13 8.5a.5.5 0 00.12.62l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58a.5.5 0 00-.12.62l1.92 3.32a.5.5 0 00.61.22l2.39-.96c.5.38 1.03.7 1.62.94l.37 2.48a.55.55 0 00.55.5h3.82a.55.55 0 00.55-.5l.37-2.48c.59-.24 1.12-.56 1.62-.94l2.39.96a.5.5 0 00.61-.22l1.92-3.32a.5.5 0 00-.12-.62zM12 15.6a3.6 3.6 0 110-7.2 3.6 3.6 0 010 7.2z"></path></svg>`;
             } else {
                // SVG de fallback quando não há weaponicon
                btn.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path fill="currentColor" d="M19.14 12.94c.04-.31.06-.63.06-.94s-.02-.63-.06-.94l2.03-1.58a.5.5 0 00.12-.62l-1.92-3.32a.5.5 0 00-.61-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.37-2.48a.55.55 0 00-.55-.5h-3.82a.55.55 0 00-.55.5l-.37 2.48c-.59.24-1.12.56-1.62.94l-2.39-.96a.5.5 0 00-.61.22L3.13 8.5a.5.5 0 00.12.62l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58a.5.5 0 00-.12.62l1.92 3.32a.5.5 0 00.61.22l2.39-.96c.5.38 1.03.7 1.62.94l.37 2.48a.55.55 0 00.55.5h3.82a.55.55 0 00.55-.5l.37-2.48c.59-.24 1.12-.56 1.62-.94l2.39.96a.5.5 0 00.61-.22l1.92-3.32a.5.5 0 00-.12-.62zM12 15.6a3.6 3.6 0 110-7.2 3.6 3.6 0 010 7.2z"></path></svg>';
            }
            iconsBar.appendChild(btn);
            weaponToggleBtn = btn;
            syncWeaponButtonState(globalWeaponEnabled);
            btn.addEventListener('click', () => {
                const nextState = !globalWeaponEnabled;
                if (nextState) {
                    requestWeaponPopupDisplay();
                }
                requestWeaponState(nextState);
            });
            reapplyWeaponClassesToBar();
        }
        onWeaponStateChange(syncWeaponRailState);
        syncWeaponRailState(globalWeaponEnabled);
        // Toggle agora é criado pelo Character.WeaponToggle.html abaixo dos botões de idioma
        // setupWeaponBarToggle(hasWeaponSkillAvailable);
        (function injectWeaponStyles() {
            if (document.getElementById('weapon-toggle-styles')) return;
            const style = document.createElement('style');
            style.id = 'weapon-toggle-styles';
            style.textContent = `
                /* Animação da borda nos ícones */
                @keyframes weapon-icon-border-scan {
                    0% { background-position: 0% 0%; }
                    100% { background-position: 400% 0%; }
                }
                @keyframes weapon-icon-pulse {
                    0%, 100% {
                        opacity: 0.8;
                        box-shadow: 0 0 8px 2px rgba(255, 80, 80, 0.45), 0 0 16px 4px rgba(255, 80, 80, 0.2);
                    }
                    50% {
                        opacity: 1;
                        box-shadow: 0 0 12px 4px rgba(255, 80, 80, 0.6), 0 0 24px 8px rgba(255, 80, 80, 0.3);
                    }
                }
                @keyframes weapon-icon-pulse-active {
                    0%, 100% {
                        opacity: 0.85;
                        box-shadow: 0 0 10px 4px rgba(255, 87, 34, 0.55), 0 0 22px 8px rgba(255, 87, 34, 0.3), 0 0 0 2px rgba(255, 87, 34, 0.35);
                    }
                    50% {
                        opacity: 1;
                        box-shadow: 0 0 16px 6px rgba(255, 87, 34, 0.7), 0 0 32px 12px rgba(255, 87, 34, 0.4), 0 0 0 3px rgba(255, 87, 34, 0.5);
                    }
                }
                /* Skills com arma disponível - borda vermelha quando inativa */
                .character-box .top-rail.skills .icon-bar .skill-icon.has-weapon-available:not(.weapon-bar-toggle):not(.active)::after {
                    box-shadow: inset 0 0 0 var(--icon-ring-w) rgba(220, 70, 70, 0.85) !important;
                }
                /* Skill com arma ATIVA - laranja/coral vibrante + brilho forte */
                .character-box .top-rail.skills .icon-bar .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::after {
                    box-shadow: inset 0 0 0 var(--icon-ring-w) #FF7043 !important;
                }
                .character-box .top-rail.skills .icon-bar .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::before {
                    box-shadow: 0 0 12px 4px rgba(255, 112, 67, 0.6), 0 0 24px 8px rgba(255, 87, 34, 0.35), 0 0 0 3px rgba(255, 112, 67, 0.4) !important;
                    opacity: 1 !important;
                }
                /* Modo arma ON - efeito animado nos ícones */
                .character-box .top-rail.skills.weapon-mode-on .icon-bar .skill-icon.has-weapon-available:not(.weapon-bar-toggle) {
                    position: relative;
                }
                .character-box .top-rail.skills.weapon-mode-on .icon-bar .skill-icon.has-weapon-available:not(.weapon-bar-toggle)::after {
                    box-shadow: none !important;
                    background: linear-gradient(90deg, rgba(255, 80, 80, 0.9) 0%, rgba(255, 120, 60, 1) 25%, rgba(255, 80, 80, 0.9) 50%, rgba(255, 120, 60, 1) 75%, rgba(255, 80, 80, 0.9) 100%) !important;
                    background-size: 400% 100% !important;
                    animation: weapon-icon-border-scan 4s linear infinite !important;
                    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) !important;
                    mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) !important;
                    -webkit-mask-composite: xor !important;
                    mask-composite: exclude !important;
                    padding: var(--icon-ring-w) !important;
                }
                .character-box .top-rail.skills.weapon-mode-on .icon-bar .skill-icon.has-weapon-available:not(.weapon-bar-toggle)::before {
                    box-shadow: 0 0 10px 3px rgba(255, 80, 80, 0.5), 0 0 20px 6px rgba(255, 80, 80, 0.25) !important;
                    opacity: 1 !important;
                    animation: weapon-icon-pulse 3s ease-in-out infinite !important;
                }
                /* Skill ativa com arma - mais intenso */
                .character-box .top-rail.skills.weapon-mode-on .icon-bar .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::after {
                    background: linear-gradient(90deg, rgba(255, 87, 34, 1) 0%, rgba(255, 140, 60, 1) 25%, rgba(255, 87, 34, 1) 50%, rgba(255, 140, 60, 1) 75%, rgba(255, 87, 34, 1) 100%) !important;
                }
                .character-box .top-rail.skills.weapon-mode-on .icon-bar .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::before {
                    box-shadow: 0 0 14px 5px rgba(255, 87, 34, 0.65), 0 0 28px 10px rgba(255, 87, 34, 0.35), 0 0 0 3px rgba(255, 87, 34, 0.45) !important;
                    animation: weapon-icon-pulse-active 2.5s ease-in-out infinite !important;
                }
                .skill-icon .weapon-indicator {
                    display: none;
                }
                /* Variáveis de cor vermelha para skill ativa com arma equipada */
                .skill-icon.weapon-equipped {
                    --icon-active: #FF6B6B;
                    --icon-active-ring: rgba(255, 100, 100, 0.6);
                    --icon-active-glow: rgba(255, 90, 90, 0.45);
                }
                /* Badge de arma no canto inferior direito */
                .skill-icon .weapon-badge {
                    position: absolute;
                    bottom: 3px;
                    right: 3px;
                    width: 16px;
                    height: 16px;
                    background: var(--weapon-badge-url) center/contain no-repeat;
                    filter: drop-shadow(0 1px 2px rgba(0,0,0,0.6));
                    pointer-events: none;
                    z-index: 10;
                    border-radius: 3px;
                    display: none;
                }
                .skill-icon.weapon-equipped .weapon-badge {
                    display: block;
                }
            `.replace(/\s+/g, ' ').trim();
            document.head.appendChild(style);
        })();
        function applyWeaponBadge(el, weaponData, equipped) {
            // Encontrar ou criar o badge
            let badge = el.querySelector('.weapon-badge');
            if (!badge) {
                badge = document.createElement('div');
                badge.className = 'weapon-badge';
                el.appendChild(badge);
            }


             if (equipped && weaponData) {
            // Verifica se já existe no cache
                el.classList.add('weapon-equipped');
             if (subskillVideoElementCache.has(normalizedURL)) {
                 el.style.setProperty('--weapon-badge-url', `url('${filePathURL(weaponData.icon || 'Nada.png')}')`);
                 logSubVideo('getOrCreateSubskillVideo: cache hit', { videoURL: normalizedURL });
            } else {
                 return subskillVideoElementCache.get(normalizedURL);
                el.classList.remove('weapon-equipped');
                 el.style.removeProperty('--weapon-badge-url');
             }
             }
        } function getWeaponKey(el) {
            return (el.dataset.index || '') + ':' + (el.dataset.nome || el.dataset.name || '');
        } function isWeaponModeOn() {
            try {
                return localStorage.getItem('glaWeaponEnabled') === '1';
            } catch (e) {
                return false;
            }
        } function getWeaponDataForIcon(iconEl) {
            if (!iconEl || !iconEl.dataset.weapon) return null;
            try {
                return JSON.parse(iconEl.dataset.weapon);
            } catch (e) {
                return null;
            }
        } function getEffectiveSkillVideoFromIcon(iconEl) {
            const weaponOn = globalWeaponEnabled;
            const weaponData = getWeaponDataForIcon(iconEl);
            const baseVideoFile = (iconEl.dataset.videoFile || '').trim();
            const baseVideoURL = (iconEl.dataset.video || '').trim();


             console.log('[Skills DEBUG]', {
             // Cria novo elemento de vídeo
                skillName: iconEl.dataset.nome || iconEl.dataset.name,
            logSubVideo('getOrCreateSubskillVideo: creating new video', { videoURL: normalizedURL });
                weaponOn,
                hasWeaponData: !!weaponData,
                weaponData: weaponData,
                baseVideoFile,
                baseVideoURL
            });


            if (weaponOn && weaponData) {
                console.log('[Skills] checking weapon video', {
                    skillName: iconEl.dataset.nome || iconEl.dataset.name,
                    hasVideo: !!(weaponData.video),
                    videoValue: weaponData.video,
                    videoTrimmed: weaponData.video ? weaponData.video.trim() : ''
                });
                if (weaponData.video && weaponData.video.trim() !== '') {
                    console.log('[Skills] video escolhido (weapon)', iconEl.dataset.nome || iconEl.dataset.name, weaponData.video);
                    return weaponData.video.trim();
                }
            }
            const result = baseVideoFile || baseVideoURL || '';
            console.log('[Skills] video escolhido (base)', iconEl.dataset.nome || iconEl.dataset.name, result);
            return result;
        } function createVideoElement(videoURL, extraAttrs = {
        }) {
             const v = document.createElement('video');
             const v = document.createElement('video');
             v.className = 'skill-video';
             v.className = 'skill-video';
            v.dataset.sub = '1';
             v.setAttribute('controls', '');
             v.setAttribute('controls', '');
             v.setAttribute('preload', 'auto');
             v.setAttribute('preload', 'auto');
             v.setAttribute('playsinline', '');
             v.setAttribute('playsinline', '');
            v.muted = true; // Permite autoload em mais navegadores
             v.style.display = 'none';
             v.style.display = 'none';
             v.style.width = '100%';
             v.style.width = '100%';
Linha 820: Linha 288:
             v.style.aspectRatio = '16/9';
             v.style.aspectRatio = '16/9';
             v.style.objectFit = 'cover';
             v.style.objectFit = 'cover';
            Object.keys(extraAttrs).forEach(k => {
 
                v.dataset[k] = extraAttrs[k];
            });
             // Detectar formato do vídeo pela extensão
             // Detectar formato do vídeo pela extensão
             const ext = (videoURL.split('.').pop() || '').toLowerCase().split('?')[0];
             const ext = (normalizedURL.split('.').pop() || '').toLowerCase().split('?')[0];
             const mimeTypes = {
             const mimeTypes = {
                 'mp4': 'video/mp4',
                 'mp4': 'video/mp4',
Linha 836: Linha 302:


             const src = document.createElement('source');
             const src = document.createElement('source');
             src.src = videoURL;
             src.src = normalizedURL;
             src.type = mimeType;
             src.type = mimeType;
             v.appendChild(src);
             v.appendChild(src);
Linha 843: Linha 309:
             v.setAttribute('webkit-playsinline', '');
             v.setAttribute('webkit-playsinline', '');
             v.setAttribute('x-webkit-airplay', 'allow');
             v.setAttribute('x-webkit-airplay', 'allow');
            // Adiciona ao cache
            subskillVideoElementCache.set(normalizedURL, v);
            // Adiciona ao DOM imediatamente e força carregamento
            const videoBox = document.querySelector('.video-container');
            if (videoBox && !v.isConnected) {
                videoBox.appendChild(v);
                // Força carregamento imediato
                v.load();
            }


             return v;
             return v;
        } function precreateSubskillVideos() {
            if (!videoBox) return;
            console.log('[Skills] precreateSubskillVideos: iniciando pré-criação de TODOS os vídeos de subskills');
            let createdCount = 0;
            iconItems.forEach(parentIcon => {
                const subsRaw = parentIcon.dataset.subs || parentIcon.getAttribute('data-subs');
                if (!subsRaw) return;
                try {
                    const subs = JSON.parse(subsRaw);
                    if (!Array.isArray(subs)) return;
                    const parentIdx = parentIcon.dataset.index || '';
                    subs.forEach(s => {
                        const subName = (s.name || s.n || '').trim();
                        // Vídeo normal da subskill
                        if (s.video && s.video.trim() !== '' && s.video !== 'Nada.png' && !s.video.toLowerCase().includes('nada.png')) {
                            const key = `sub:${parentIdx}:${subName}`;
                            if (subskillVideosCache.has(key)) return;
                            const videoURL = normalizeFileURL(s.video);
                            if (videoURL && videoURL.trim() !== '') {
                                const v = createVideoElement(videoURL, {
                                    sub: '1', parentIndex: parentIdx, subName: subName, cacheKey: key
                                });
                                v.setAttribute('preload', 'auto');
                                v.muted = true; // Permite autoload em mais navegadores
                                videoBox.appendChild(v);
                                subskillVideosCache.set(key, v);
                                createdCount++;
                                // Força carregamento imediato
                                v.load();
                            }
                        }
                        // Vídeo de weapon da subskill
                        if (s.weapon && typeof s.weapon === 'object' && s.weapon.video && s.weapon.video.trim() !== '' && s.weapon.video !== 'Nada.png' && !s.weapon.video.toLowerCase().includes('nada.png')) {
                            const weaponKey = `sub:${parentIdx}:${subName}:weapon`;
                            if (subskillVideosCache.has(weaponKey)) return;
                            const weaponVideoURL = normalizeFileURL(s.weapon.video);
                            if (weaponVideoURL && weaponVideoURL.trim() !== '') {
                                const v = createVideoElement(weaponVideoURL, {
                                    sub: '1', parentIndex: parentIdx, subName: subName, weapon: '1', cacheKey: weaponKey
                                });
                                v.setAttribute('preload', 'auto');
                                v.muted = true; // Permite autoload em mais navegadores
                                videoBox.appendChild(v);
                                subskillVideosCache.set(weaponKey, v);
                                createdCount++;
                                // Força carregamento imediato
                                v.load();
                            }
                        }
                    });
                } catch (e) {
                }
            });
            console.log('[Skills] precreateSubskillVideos: criados', createdCount, 'vídeos. Cache size:', subskillVideosCache.size);
         }
         }


         // Função para forçar carregamento de todos os vídeos
         function renderSubAttrs(s, L) {
        function forceLoadAllVideos() {
            const chip = (label, val) => (val ? `<div class="attr-row"><span class="attr-label">${label}</span><span class="attr-value">${val}</span></div>` : '');
             if (!videoBox) return;
            const pve = (s.powerpve || '').toString().trim();
             const allVideos = videoBox.querySelectorAll('video[data-sub="1"]');
             const pvp = (s.powerpvp || '').toString().trim();
             console.log('[Skills] forceLoadAllVideos: forçando load de', allVideos.length, 'vídeos');
             const en = (s.energy || '').toString().trim();
             allVideos.forEach(v => {
            const cd = (s.cooldown || '').toString().trim();
                if (v.readyState < 2) {
            const rows = [
                    v.load();
                cd ? chip(L.cooldown, cd) : '',
                }
                en ? chip((en.startsWith('-') ? L.energy_cost : L.energy_gain), en.startsWith('-') ? en.replace(/^-/, '') : 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>` : '';
        }
 
        function renderFlagsRow(flags) {
             const map = {
                aggro: 'Enemyaggro-icon.png',
                bridge: 'Bridgemaker-icon.png',
                wall: 'Destroywall-icon.png',
                quickcast: 'Quickcast-icon.png',
                wallpass: 'Passthroughwall-icon.png'
            };
            const arr = (flags || []).filter(Boolean);
            if (!arr.length) return '';
             const items = arr.map(k => `<img class="skill-flag" data-flag="${k}" alt="" src="${filePathURL(map[k])}">`).join('');
             return `<div class="skill-flags" role="group" aria-label="Características">${items}</div>`;
         }
         }


         // Pré-cria vídeos de subskills IMEDIATAMENTE no carregamento
         function applyFlagTooltips(container) {
        precreateSubskillVideos();
             const skillsRoot = document.getElementById('skills');
        // Força carregamento novamente após curto delay para garantir
             if (!skillsRoot) return;
        setTimeout(() => {
            let pack = {};
             precreateSubskillVideos();
            try { pack = JSON.parse(skillsRoot.dataset.i18nFlags || '{}'); } catch (e) { }
             forceLoadAllVideos();
            const raw = (document.documentElement.lang || 'pt').toLowerCase();
        }, 100);
            const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
        // E mais uma vez após um delay maior para garantir que tudo esteja pronto
            const dict = pack[lang] || pack.pt || {};
        setTimeout(forceLoadAllVideos, 500);
            const flags = container.querySelectorAll('.skill-flags .skill-flag[data-flag]');
        if (iconItems.length && videoBox) {
            const tooltip = window.__globalSkillTooltip;
            iconItems.forEach(el => {
            if (!tooltip) return;
                const idx = el.dataset.index || '';
 
                // Vídeo normal
            flags.forEach(el => {
                const src = (el.dataset.videoFile || el.dataset.video || '').trim();
                const key = el.getAttribute('data-flag');
                if (src && src !== 'Nada.png' && !src.toLowerCase().includes('nada.png') && !videosCache.has(idx)) {
                const tip = (dict && dict[key]) || '';
                    totalVideos++;
                if (!tip) return;
                    const videoURL = normalizeFileURL(src);
                if (el.dataset.flagTipWired) return;
                    if (videoURL) {
                el.dataset.flagTipWired = '1';
                        const v = createVideoElement(videoURL, {
                el.setAttribute('aria-label', tip);
                            index: idx
                if (el.hasAttribute('title')) el.removeAttribute('title');
                        });
 
                        v.setAttribute('preload', 'auto');
                el.addEventListener('mouseenter', () => {
                        v.style.maxWidth = '100%';
                    const tipEl = document.querySelector('.skill-tooltip');
                        v.addEventListener('canplaythrough', () => {
                    if (tipEl) tipEl.classList.add('flag-tooltip');
                            loadedVideos++;
                    tooltip.show(el, tip);
                            if (!userHasInteracted && loadedVideos === 1) {
                });
                                try {
                el.addEventListener('mousemove', () => {
                                    v.pause();
                    if (performance.now() >= tooltip.lockUntil.value) {
                                    v.currentTime = 0;
                         tooltip.measureAndPos(el);
                                } catch (e) {
                                }
                            } if (loadedVideos === totalVideos) autoplay = true;
                        }, {
                            once: true
                        });
                        v.addEventListener('error', () => {
                            loadedVideos++;
                            if (loadedVideos === totalVideos) autoplay = true;
                        }, {
                            once: true
                        });
                        videoBox.appendChild(v);
                        videosCache.set(idx, v);
                        // Força carregamento
                         v.load();
                     }
                     }
                }
                // Vídeo de weapon
                if (el.dataset.weapon) {
                    try {
                        const weaponData = JSON.parse(el.dataset.weapon);
                        if (weaponData && weaponData.video && weaponData.video.trim() !== '' && weaponData.video !== 'Nada.png' && !weaponData.video.toLowerCase().includes('nada.png')) {
                            const weaponKey = `weapon:${idx}:${(el.dataset.nome || el.dataset.name || '').trim()}`;
                            if (!videosCache.has(weaponKey)) {
                                totalVideos++;
                                const weaponVideoURL = normalizeFileURL(weaponData.video);
                                if (weaponVideoURL) {
                                    const v = createVideoElement(weaponVideoURL, {
                                        index: idx, weapon: '1'
                                    });
                                    v.setAttribute('preload', 'auto');
                                    v.style.maxWidth = '100%';
                                    v.addEventListener('canplaythrough', () => {
                                        loadedVideos++;
                                        if (loadedVideos === totalVideos) autoplay = true;
                                    }, {
                                        once: true
                                    });
                                    v.addEventListener('error', () => {
                                        loadedVideos++;
                                        if (loadedVideos === totalVideos) autoplay = true;
                                    }, {
                                        once: true
                                    });
                                    videoBox.appendChild(v);
                                    videosCache.set(weaponKey, v);
                                    // Força carregamento
                                    v.load();
                                }
                            }
                        }
                    } catch (e) {
                    }
                }
            });
        } 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.weaponToggle === '1' || icon.classList.contains('weapon-bar-toggle')) return;
                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', () => {
                 el.addEventListener('click', () => {
                     lockUntil2 = performance.now() + 240;
                     tooltip.lockUntil.value = performance.now() + 240;
                     measureAndPos(icon);
                     tooltip.measureAndPos(el);
                });
                el.addEventListener('mouseleave', () => {
                    const tipEl = document.querySelector('.skill-tooltip');
                    if (tipEl) tipEl.classList.remove('flag-tooltip');
                    tooltip.hide();
                 });
                 });
                icon.addEventListener('mouseleave', hide);
             });
             });
         } function showVideoForIcon(el) {
         }
             userHasInteracted = true;
 
             if (!videoBox) return;
        function ensureRail(iconsBar) {
             const effectiveVideo = getEffectiveSkillVideoFromIcon(el);
             const rail = iconsBar.closest('.top-rail');
             if (!effectiveVideo || effectiveVideo.trim() === '') {
             if (!rail) {
                 videoBox.style.display = 'none';
                return null;
                 return;
             }
 
            if (!subRail) {
                subRail = document.createElement('div');
                subRail.className = 'subskills-rail collapsed hidden';
                rail.appendChild(subRail);
            }
 
             if (!subBar) {
                subBar = document.createElement('div');
                 subBar.className = 'subicon-bar';
                 subRail.appendChild(subBar);
             }
             }
            const videoURL = normalizeFileURL(effectiveVideo);
 
             if (!videoURL || videoURL.trim() === '') {
             if (!spacer) {
                 videoBox.style.display = 'none';
                spacer = document.createElement('div');
                 return;
                 spacer.className = 'subskills-spacer';
                 rail.parentNode.insertBefore(spacer, rail.nextSibling);
             }
             }
             Array.from(videoBox.querySelectorAll('video.skill-video')).forEach(v => {
 
                try {
            return rail;
                    v.pause();
        }
                } catch (e) {
 
                }
        // Função para mostrar vídeo de subskill usando cache baseado em URL
        function showSubVideo(videoURL, videoBox) {
             logSubVideo('showSubVideo called', { videoURL, videoBoxExists: !!videoBox });
 
            if (!videoBox) return;
 
            // Ocultar todos os vídeos existentes no container
            videoBox.querySelectorAll('video.skill-video[data-sub="1"]').forEach(v => {
                 v.style.display = 'none';
                 v.style.display = 'none';
             });
             });
            if (window.__subskills) window.__subskills.hideAll?.(videoBox);
            const hasIdx = !!el.dataset.index;
            const weaponOn = globalWeaponEnabled;
            const weaponData = getWeaponDataForIcon(el);
            const isWeaponVideo = weaponOn && weaponData && weaponData.video && weaponData.video.trim() !== '';


             console.log('[Skills] showVideoForIcon chamado', {
             // Obter vídeo via getOrCreateSubskillVideo (única forma de obter vídeos)
                skillName: el.dataset.nome || el.dataset.name,
             const video = getOrCreateSubskillVideo(videoURL);
                weaponOn,
 
                isWeaponVideo,
             if (!video) {
                effectiveVideo: getEffectiveSkillVideoFromIcon(el)
                 logSubVideo('no video found for URL, hiding box', { videoURL });
            });
                 videoBox.style.display = 'none';
             const videoKey = isWeaponVideo ? `weapon:${getWeaponKey(el)}` : (el.dataset.index || '');
             if (hasIdx && !isWeaponVideo && 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) {
                    v.play().catch(() => {
                    });
                } else {
                    try {
                        v.pause();
                    } catch (e) {
                    }
                }
                 return;
                 return;
             }
             }
             let v = null;
 
             if (isWeaponVideo) {
             // Se o vídeo não estiver conectado ao DOM, anexá-lo ao container
                v = videoBox.querySelector(`video[data-weapon-key="${videoKey}"]`);
             if (!video.isConnected || video.parentNode !== videoBox) {
            } else {
                 logSubVideo('video not in DOM, appending', {
                 v = nestedVideoElByIcon.get(el);
                    videoURL,
            }
                     isConnected: video.isConnected,
            if (!v) {
                     parentNode: video.parentNode
                v = createVideoElement(videoURL, isWeaponVideo ? {
                 });
                     weaponKey: videoKey
                 if (video.parentNode) {
                } : {});
                     video.parentNode.removeChild(video);
                if (isWeaponVideo) {
                    videoBox.appendChild(v);
                } else {
                     videoBox.appendChild(v);
                    nestedVideoElByIcon.set(el, v);
                 }
            } else {
                // Verifica se o source já tem a URL correta (compara apenas o pathname/filename)
                const src = v.querySelector('source');
                 if (src) {
                     const currentURL = src.src || '';
                    const currentFilename = currentURL.split('/').pop().split('?')[0];
                    const targetFilename = videoURL.split('/').pop().split('?')[0];
                    // Só recarrega se o arquivo for realmente diferente
                    if (currentFilename !== targetFilename) {
                        src.src = videoURL;
                        v.load();
                    }
                 }
                 }
                videoBox.appendChild(video);
             }
             }
            logSubVideo('showing video', {
                videoURL,
                readyState: video.readyState,
                paused: video.paused,
                currentTime: video.currentTime
            });
            // Exibir o vídeo
             videoBox.style.display = 'block';
             videoBox.style.display = 'block';
             v.style.display = 'block';
             video.style.display = 'block';
 
            // Reinicia o vídeo para feedback visual imediato
             try {
             try {
                 v.currentTime = 0;
                 video.currentTime = 0;
             } catch (e) {
             } catch (e) { }
            }
 
             const suppress = document.body.dataset.suppressSkillPlay === '1';
             // Tenta dar play se o vídeo já está pronto
             if (!suppress) {
             if (video.readyState >= 2) {
                 v.play().catch(() => {
                 video.play().then(() => {
                    logSubVideo('play() resolved', { videoURL, currentTime: video.currentTime, readyState: video.readyState });
                }).catch(err => {
                    logSubVideo('play() rejected', { videoURL, error: err });
                 });
                 });
             } else {
             } else {
                 try {
                 // Se não está pronto ainda, espera o evento canplay
                     v.pause();
                logSubVideo('video not ready, waiting for canplay', { videoURL, readyState: video.readyState });
                 } catch (e) {
                const onCanPlay = () => {
                 }
                     video.removeEventListener('canplay', onCanPlay);
                    video.play().catch(() => { });
                 };
                video.addEventListener('canplay', onCanPlay, { once: true });
            }
        }
 
        // Funções antigas removidas - usar getOrCreateSubskillVideo() em vez disso
        // REMOVIDO: ensureSubVideoCached
        // REMOVIDO: ensureSubVideoInCache
 
        api.refreshCurrentSubSafe = function () {
            const btn = document.querySelector('.subskills-rail .subicon.active');
            if (!btn) return false;
            const had = document.body.dataset.suppressSkillPlay;
            document.body.dataset.suppressSkillPlay = '1';
            try {
                 btn.dispatchEvent(new Event('click', { bubbles: true }));
            } finally {
                if (had) document.body.dataset.suppressSkillPlay = had;
                else delete document.body.dataset.suppressSkillPlay;
             }
             }
         } function activateSkill(el, options = {
            return true;
         }) {
         };
            const {
 
                openSubs = true
         // Função auxiliar para aplicar classes de weapon nas subskills renderizadas
            } = options;
        const applyWeaponClassesToSubskills = () => {
             const tip = document.querySelector('.skill-tooltip');
             const weaponOn = isWeaponModeOn();
             if (tip) {
             // Busca TODAS as subskills com weapon
                tip.setAttribute('aria-hidden', 'true');
             let weaponSubs = document.querySelectorAll('.subicon[data-weapon]');
                tip.style.opacity = '0';
 
                tip.style.left = '-9999px';
             weaponSubs.forEach(el => {
                tip.style.top = '-9999px';
                if (weaponOn) {
             } const skillsRoot = document.getElementById('skills');
                     el.classList.add('has-weapon-available');
            const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {
                     if (el.classList.contains('active')) {
            };
                        el.classList.add('weapon-equipped');
            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();
            let weaponData = null;
            if (el.dataset.weapon) {
                try {
                     weaponData = JSON.parse(el.dataset.weapon);
                } catch (e) {
                     weaponData = null;
                }
            } const hasWeapon = !!weaponData;
            const weaponEquipped = hasWeapon && globalWeaponEnabled;
            const lang = getLangKey();
            const baseDescPack = {
                pt: el.dataset.descPt || '', en: el.dataset.descEn || '', es: el.dataset.descEs || '', pl: el.dataset.descPl || ''
            };
            const baseDesc = baseDescPack[lang] || baseDescPack.pt || baseDescPack.en || baseDescPack.es || baseDescPack.pl || el.dataset.desc || '';
            // Aceita tanto desc_i18n quanto desc para compatibilidade
            let weaponDescPack = {};
            if (weaponData) {
                if (weaponData.desc_i18n) {
                     weaponDescPack = weaponData.desc_i18n;
                } else if (weaponData.desc) {
                    weaponDescPack = weaponData.desc;
                 } else {
                 } else {
                     weaponDescPack = {
                     el.classList.remove('has-weapon-available');
                        pt: weaponData.descPt || '', en: weaponData.descEn || '', es: weaponData.descEs || '', pl: weaponData.descPl || ''
                    el.classList.remove('weapon-equipped');
                    };
                 }
                 }
            });
        };
        api.renderBarFrom = function (el, { iconsBar, descBox, videoBox }) {
            logSubVideo('api.renderBarFrom called', {
                hasEl: !!el,
                hasIconsBar: !!iconsBar,
                hasDescBox: !!descBox,
                hasVideoBox: !!videoBox,
                parentIndex: el?.dataset?.index || 'unknown'
            });
            const rail = ensureRail(iconsBar);
            if (!rail) {
                logSubVideo('api.renderBarFrom: no rail found, returning');
                return;
             }
             }
            const weaponDesc = weaponDescPack[lang] || weaponDescPack.pt || weaponDescPack.en || weaponDescPack.es || weaponDescPack.pl || '';
 
             const chosenDesc = (weaponEquipped && weaponDesc) ? weaponDesc : baseDesc;
             const rawSubs = el.getAttribute('data-subs') || '';
            const descHtml = chosenDesc.replace(/'''(.*?)'''/g, '<b>$1</b>');
             const rawOrder = el.getAttribute('data-suborder') || '';
            let attrsHTML = '';
            const parentIdx = el.dataset.index || '';
             if (weaponEquipped && weaponData) {
 
                // Usa valores do weapon, mas só se existirem (não herda da skill base)
            logSubVideo('api.renderBarFrom: parsing subs', {
                const wPve = (weaponData.powerpve !== undefined && weaponData.powerpve !== null && weaponData.powerpve !== '') ? weaponData.powerpve.toString().trim() : '';
                 parentIdx,
                const wPvp = (weaponData.powerpvp !== undefined && weaponData.powerpvp !== null && weaponData.powerpvp !== '') ? weaponData.powerpvp.toString().trim() : '';
                 hasRawSubs: !!rawSubs.trim(),
                const wEnergy = (weaponData.energy !== undefined && weaponData.energy !== null && weaponData.energy !== '') ? weaponData.energy.toString().trim() : '';
                 rawSubsLength: rawSubs.length
                const wCd = (weaponData.cooldown !== undefined && weaponData.cooldown !== null && weaponData.cooldown !== '') ? weaponData.cooldown.toString().trim() : '';
             });
                const weaponAttrs = [wPve, wPvp, wEnergy, wCd].join(',');
 
                console.log('[Skills] weaponAttrs string:', weaponAttrs, { wPve, wPvp, wEnergy, wCd, weaponData });
             if (!rawSubs.trim()) {
                 attrsHTML = renderAttributes(weaponAttrs);
                 if (subRail) subRail.classList.add('collapsed');
            } else {
                if (subRail) subRail.classList.add('hidden');
                 attrsHTML = el.dataset.atr ? renderAttributes(el.dataset.atr) : (el.dataset.subattrs ? renderSubAttributesFromObj(JSON.parse(el.dataset.subattrs), L) : '');
                if (subBar) subBar.innerHTML = '';
            } let flagsHTML = '';
                 if (spacer) spacer.style.height = '0px';
            if (el.dataset.flags) {
                 try {
                    const flags = JSON.parse(el.dataset.flags);
                    flagsHTML = renderFlagsRow(flags);
                } catch (e) {
                }
             } if (descBox) {
                descBox.innerHTML = `<div class="skill-title"><h3>${name}</h3></div>${level ? `<div class="skill-level-line"><span class="attr-label">${L.level} ${level}</span></div>` : ''}${attrsHTML}<div class="desc">${descHtml}</div>`;
            } if (hasWeapon) {
                applyWeaponBadge(el, weaponData, weaponEquipped);
             } if (videoBox) {
                const oldFlags = videoBox.querySelector('.skill-flags');
                if (oldFlags) oldFlags.remove();
                 if (flagsHTML) {
                    videoBox.insertAdjacentHTML('beforeend', flagsHTML);
                    applyFlagTooltips(videoBox);
                }
            } const currIcons = Array.from(iconsBar.querySelectorAll('.skill-icon'));
            currIcons.forEach(i => i.classList.remove('active'));
            el.classList.add('active');
            if (!autoplay && loadedVideos > 0) autoplay = true;
            window.__lastActiveSkillIcon = el;
            // Lógica de vídeo: usa função centralizada que já considera weapon
            showVideoForIcon(el);
            const subsRaw = el.dataset.subs || el.getAttribute('data-subs');
            const isBack = el.dataset.back === 'true' || el.getAttribute('data-back') === 'true' || el.dataset.back === 'yes' || el.getAttribute('data-back') === 'yes' || el.dataset.back === '1' || el.getAttribute('data-back') === '1';
            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;
                 return;
            } if (openSubs && subsRaw && subsRaw.trim() !== '') {
                if (barStack.length && barStack[barStack.length - 1].parentIcon === el) return;
                try {
                    const subs = JSON.parse(subsRaw);
                    pushSubBarFrom(subs, el);
                } catch {
                }
             }
             }
        } function wireClicksForCurrentBar() {
 
             const currIcons = Array.from(iconsBar.querySelectorAll('.skill-icon'));
             let subs;
             currIcons.forEach(el => {
            try { subs = JSON.parse(rawSubs); } catch { subs = []; }
                 if (el.dataset.weaponToggle === '1' || el.classList.contains('weapon-bar-toggle')) return;
 
                 if (el.dataset.wired) return;
            // NORMALIZADOR: Converte weaponPacked para weapon se necessário
                el.dataset.wired = '1';
             subs = subs.map(sub => {
                const label = el.dataset.nome || el.dataset.name || '';
                 // Se tem weaponPacked mas não tem weapon, tenta parsear
                el.setAttribute('aria-label', label);
                 if (sub.weaponPacked && !sub.weapon && typeof sub.weaponPacked === 'string' && sub.weaponPacked.trim() !== '') {
                if (el.hasAttribute('title')) el.removeAttribute('title');
                    const parts = sub.weaponPacked.split('~');
                const img = el.querySelector('img');
                     if (parts.length >= 2) {
                if (img) {
                         sub.weapon = {
                    img.setAttribute('alt', '');
                            icon: parts[0] || 'Nada.png',
                     if (img.hasAttribute('title')) img.removeAttribute('title');
                            powerpve: parts[1] || null,
                } el.addEventListener('click', () => {
                            powerpvp: parts[2] || null,
                    activateSkill(el, {
                            cooldown: parts[3] || null,
                         openSubs: true
                            video: parts[4] || '',
                    });
                            energy: parts[5] || null
                });
                        };
            });
                        // Remove valores vazios
            wireTooltipsForNewIcons();
                        Object.keys(sub.weapon).forEach(k => {
        } function animateIconsBarEntrance() {
                            if (sub.weapon[k] === '' || sub.weapon[k] === null) {
            Array.from(iconsBar.children).forEach((c, i) => {
                                delete sub.weapon[k];
                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);
                });
            });
        } function snapshotCurrentBarItemsFromDOM() {
            return Array.from(iconsBar.querySelectorAll('.skill-icon')).filter(el => el.dataset.weaponToggle !== '1').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 || '';
                let flags = null;
                if (el.dataset.flags) {
                    try {
                        flags = JSON.parse(el.dataset.flags);
                    } catch (e) {
                    }
                } let weapon = null;
                if (el.dataset.weapon) {
                    try {
                        weapon = JSON.parse(el.dataset.weapon);
                    } catch (e) {
                     }
                     }
                } return {
                    name: el.dataset.nome || el.dataset.name || '', index: el.dataset.index || '', 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, flags: flags, weapon: weapon
                };
            });
        } function ensureBackButton() {
            const rail = iconsBar.closest('.top-rail.skills');
            if (!rail) return null;
            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';
                parentNode.insertBefore(newWrap, rail);
                newWrap.appendChild(rail);
                wrap = newWrap;
            } 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');
                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';
                    wrap.classList.toggle('has-sub-bar', barStack.length > 0);
                    if (!barStack.length) btnInner.classList.remove('peek');
                });
            } backWrap.style.display = barStack.length ? 'block' : 'none';
            wrap.classList.toggle('has-sub-bar', barStack.length > 0);
            const btnInner = backWrap.querySelector('.skills-back');
            return btnInner;
        } function renderBarFromItems(items) {
            const tip = document.querySelector('.skill-tooltip');
            if (tip) {
                tip.setAttribute('aria-hidden', 'true');
                tip.style.opacity = '0';
                tip.style.left = '-9999px';
                tip.style.top = '-9999px';
            } iconsBar.innerHTML = '';
            items.forEach((it, idx) => {
                const node = document.createElement('div');
                node.className = 'skill-icon';
                node.dataset.nome = it.name || '';
                if (it.index) node.dataset.index = it.index;
                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.flags) node.dataset.flags = JSON.stringify(it.flags);
                if (it.weapon) node.dataset.weapon = JSON.stringify(it.weapon);
                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);
            });
            animateIconsBarEntrance();
            wireClicksForCurrentBar();
            // Remove qualquer toggle antigo que possa aparecer
            const oldToggle = iconsBar.querySelector('.weapon-bar-toggle');
            if (oldToggle) oldToggle.remove();
            const b = ensureBackButton();
            if (b) b.classList.add('peek');
        } function pushSubBarFrom(subs, parentIconEl) {
            const tip = document.querySelector('.skill-tooltip');
            if (tip) {
                tip.setAttribute('aria-hidden', 'true');
                tip.style.opacity = '0';
                tip.style.left = '-9999px';
                tip.style.top = '-9999px';
            } const parentNameSnapshot = parentIconEl ? (parentIconEl.dataset.nome || parentIconEl.dataset.name || '') : '';
            const parentIndexSnapshot = parentIconEl ? (parentIconEl.dataset.index || '') : '';
            barStack.push({
                items: snapshotCurrentBarItemsFromDOM(), parentIcon: parentIconEl, parentName: parentNameSnapshot, parentIndex: parentIndexSnapshot
            });
            ensureBackButton();
            const langKey = getLangKey();
            let cacheKey = null;
            if (parentIconEl) {
                cacheKey = parentIconEl.dataset.subCacheKey || null;
                if (!cacheKey) {
                    if (parentIconEl.dataset.index) {
                        cacheKey = `idx:${parentIconEl.dataset.index}`;
                    } else {
                        const slug = slugify(parentIconEl.dataset.nome || parentIconEl.dataset.name || '');
                        if (slug) cacheKey = `slug:${slug}`;
                    } if (cacheKey) parentIconEl.dataset.subCacheKey = cacheKey;
                 }
                 }
            } if (cacheKey) {
                 // Garante que weapon seja objeto válido
                const cached = subBarTemplateCache.get(cacheKey);
                if (sub.weapon && typeof sub.weapon === 'string') {
                 if (cached && cached.lang === langKey) {
                    // Tenta parsear como JSON primeiro
                    iconsBar.innerHTML = '';
                    const clone = cached.template.cloneNode(true);
                    iconsBar.appendChild(clone);
                    animateIconsBarEntrance();
                    wireClicksForCurrentBar();
                    // Remove qualquer toggle antigo que possa aparecer
                    const oldToggle2 = iconsBar.querySelector('.weapon-bar-toggle');
                    if (oldToggle2) oldToggle2.remove();
                    const cachedBtn = ensureBackButton();
                    if (cachedBtn) cachedBtn.classList.add('peek');
                    return;
                }
            } 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 hydratedSubs = inheritSubskillTree(subs, mainSkillsMeta);
            const items = (hydratedSubs || []).filter(s => {
                // Filtra só se não tem nada útil
                const hasName = (s.name || s.n || '').trim() !== '';
                const hasIcon = (s.icon || '').trim() !== '' && s.icon !== 'Nada.png';
                const hasRef = (s.refS || s.refM || '').toString().trim() !== '';
                return hasName || hasIcon || hasRef;
            }).map(s => {
                const name = (s.name || s.n || '').trim();
                const desc = chooseDescFrom(s).replace(/'''(.*?)'''/g, '<b>$1</b>');
                const attrsHTML = renderSubAttributesFromObj(s, L);
                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) || ''), 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, flags: Array.isArray(s.flags) ? s.flags : null, back: (s.back === true || s.back === 'true' || s.back === 'yes' || s.back === '1') ? 'true' : null, weapon: s.weapon || null
                };
            });
            const fragment = document.createDocumentFragment();
            items.forEach((it, iIdx) => {
                const node = document.createElement('div');
                node.className = 'skill-icon';
                node.dataset.nested = '1';
                node.dataset.nome = it.name || '';
                node.dataset.parentIndex = parentIndexSnapshot;
                node.dataset.subName = it.name || '';
                const subSlug = slugify(it.name || '');
                if (subSlug) node.dataset.slug = subSlug;
                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.flags) node.dataset.flags = JSON.stringify(it.flags);
                if (it.back) node.dataset.back = it.back;
                if (it.weapon) {
                     try {
                     try {
                         node.dataset.weapon = JSON.stringify(it.weapon);
                         sub.weapon = JSON.parse(sub.weapon);
                     } catch (e) {
                     } catch {
                        console.error('[Skills] Erro ao serializar weapon de subskill', it.name, e);
                        // Se falhar, tenta formato ~
                        const parts = sub.weapon.split('~');
                        if (parts.length >= 2) {
                            sub.weapon = {
                                icon: parts[0] || 'Nada.png',
                                powerpve: parts[1] || null,
                                powerpvp: parts[2] || null,
                                cooldown: parts[3] || null,
                                video: parts[4] || '',
                                energy: parts[5] || null
                            };
                            Object.keys(sub.weapon).forEach(k => {
                                if (sub.weapon[k] === '' || sub.weapon[k] === null) {
                                    delete sub.weapon[k];
                                }
                            });
                        } else {
                            sub.weapon = null;
                        }
                     }
                     }
                 }
                 }
                 const img = document.createElement('img');
                 return sub;
                img.alt = '';
                img.src = it.iconURL;
                node.appendChild(img);
                fragment.appendChild(node);
             });
             });
             const templateClone = fragment.cloneNode(true);
 
            iconsBar.innerHTML = '';
             if (!Array.isArray(subs) || subs.length === 0) {
            iconsBar.appendChild(fragment);
                subRail.classList.add('collapsed');
            animateIconsBarEntrance();
                subRail.classList.add('hidden');
            wireClicksForCurrentBar();
                subBar.innerHTML = '';
            // Remove qualquer toggle antigo que possa aparecer
                if (spacer) spacer.style.height = '0px';
            const oldToggle3 = iconsBar.querySelector('.weapon-bar-toggle');
                 return;
            if (oldToggle3) oldToggle3.remove();
            const b2 = ensureBackButton();
            if (b2) b2.classList.add('peek');
            if (cacheKey) {
                 subBarTemplateCache.set(cacheKey, {
                    template: templateClone, lang: langKey
                });
             }
             }
        } window.addEventListener('gla:langChanged', () => {
 
             subBarTemplateCache.clear();
             // Busca mapa das skills principais para herança
             const skillsRoot = document.getElementById('skills');
             const mainSkills = getMainSkillsMap();
             const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {
 
             };
            // Aplica herança ANTES de processar
             const lang = getLangKey();
             subs = subs.map(sub => applyInheritance(sub, mainSkills));
            Array.from(iconsBar.querySelectorAll('.skill-icon')).forEach(icon => {
 
                const pack = {
             // Remove subskills que ficaram sem nome após herança
                    pt: icon.dataset.descPt || '', en: icon.dataset.descEn || '', es: icon.dataset.descEs || '', pl: icon.dataset.descPl || ''
             subs = subs.filter(s => (s.name || s.n || '').trim() !== '');
                };
 
                const chosen = (pack[lang] || pack.pt || pack.en || pack.es || pack.pl || icon.dataset.desc || '').trim();
            subRail.classList.add('hidden');
                if (chosen) icon.dataset.desc = chosen;
            subBar.innerHTML = '';
            });
 
            barStack.forEach(frame => {
            // Usa a ordem natural das subskills após herança
                 (frame.items || []).forEach(it => {
            let order = subs.map(s => s.name || s.n || '');
                     const pack = {
            if (rawOrder.trim()) {
                        pt: it.descPt, en: it.descEn, es: it.descEs, pl: it.descPl
                 try {
                    };
                     const preferred = JSON.parse(rawOrder);
                    const chosen = (pack[lang] || pack.pt || pack.en || pack.es || pack.pl || it.desc || '');
                    if (Array.isArray(preferred) && preferred.length) {
                    it.desc = chosen;
                        const byName = new Map(subs.map(s => [(s.name || s.n || ''), s]));
                });
                        order = preferred.filter(n => byName.has(n));
            });
                    }
            if (descBox) {
                 } catch { }
                applyFlagTooltips(descBox);
            } const activeIcon = window.__lastActiveSkillIcon;
            if (activeIcon && activeIcon.dataset.weapon) {
                 activateSkill(activeIcon, {
                    openSubs: false
                });
             }
             }
        });
 
        wireClicksForCurrentBar();
             // Pré-carrega TODOS os ícones ANTES de renderizar
        const b0 = ensureBackButton();
             const iconPreloadPromises = [];
        if (b0) {
             order.forEach(nm => {
             b0.classList.add('peek');
                 const s = subs.find(x => (x.name || x.n || '') === nm);
            b0.style.alignSelf = 'stretch';
                 if (s && s.icon && s.icon.trim() !== '' && s.icon !== 'Nada.png') {
        } (function initSkillTooltip() {
                     iconPreloadPromises.push(preloadImage(s.icon));
            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';
            } window.__globalSkillTooltip = {
                show, hide, measureAndPos, lockUntil: lockUntilRef
            };
            Array.from(document.querySelectorAll('.icon-bar .skill-icon')).forEach(icon => {
                if (icon.dataset.weaponToggle === '1' || icon.classList.contains('weapon-bar-toggle')) return;
                 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);
             });
             });
             Array.from(document.querySelectorAll('.subskills-rail .subicon')).forEach(sub => {
 
                 if (sub.dataset.tipwired) return;
             // Pré-cria vídeos usando getOrCreateSubskillVideo (baseado em URL)
                 sub.dataset.tipwired = '1';
            // Isso garante que todos os vídeos estejam no cache antes de renderizar
                const label = sub.getAttribute('title') || sub.getAttribute('aria-label') || '';
            order.forEach(nm => {
                if (label && !sub.hasAttribute('aria-label')) sub.setAttribute('aria-label', label);
                 const s = subs.find(x => (x.name || x.n || '') === nm);
                if (sub.hasAttribute('title')) sub.removeAttribute('title');
                 if (s) {
                sub.addEventListener('mouseenter', () => show(sub, label));
                    // Pré-cria vídeo normal se existir
                sub.addEventListener('mousemove', () => {
                    if (s.video && s.video.trim() !== '' && s.video !== 'Nada.png' && !s.video.toLowerCase().includes('nada.png')) {
                    if (performance.now() >= lockUntilRef.value) measureAndPos(sub);
                        const video = getOrCreateSubskillVideo(s.video);
                });
                        if (video && video.readyState < 2) {
                sub.addEventListener('click', () => {
                            video.load();
                    lockUntilRef.value = performance.now() + 240;
                        }
                     measureAndPos(sub);
                    }
                 });
                    // Pré-cria vídeo de weapon se existir
                sub.addEventListener('mouseleave', hide);
                    if (s.weapon && typeof s.weapon === 'object' && s.weapon.video && s.weapon.video.trim() !== '' && s.weapon.video !== 'Nada.png' && !s.weapon.video.toLowerCase().includes('nada.png')) {
                        const weaponVideo = getOrCreateSubskillVideo(s.weapon.video);
                        if (weaponVideo && weaponVideo.readyState < 2) {
                            weaponVideo.load();
                        }
                     }
                 }
             });
             });
             window.addEventListener('scroll', () => {
 
                const visible = document.querySelector('.skill-tooltip[aria-hidden="false"]');
             // Função para renderizar a barra de subskills
                if (!visible) return;
            const renderSubskillsBar = () => {
                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');
                 order.forEach(nm => {
                 measureAndPos(target);
                    const s = subs.find(x => (x.name || x.n || '') === nm);
            }, true);
                     if (!s) {
            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);
            });
        })();
        (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');
            let wrapper = characterBox.querySelector('.tabs-height-wrapper');
            if (!wrapper) {
                wrapper = document.createElement('div');
                wrapper.className = 'tabs-height-wrapper';
                contents.forEach(c => {
                    wrapper.appendChild(c);
                });
                const tabsElement = characterBox.querySelector('.character-tabs');
                if (tabsElement && tabsElement.nextSibling) {
                     characterBox.insertBefore(wrapper, tabsElement.nextSibling);
                } else {
                    characterBox.appendChild(wrapper);
                }
            } async function smoothHeightTransition(fromTab, toTab) {
                if (!wrapper) return Promise.resolve();
                const scrollY = window.scrollY;
                const currentHeight = wrapper.getBoundingClientRect().height;
                await new Promise((resolve) => {
                    const videoContainers = toTab.querySelectorAll('.video-container');
                    const contentCard = toTab.querySelector('.content-card');
                    if (videoContainers.length === 0) {
                        requestAnimationFrame(() => {
                            requestAnimationFrame(() => {
                                requestAnimationFrame(() => resolve());
                            });
                        });
                         return;
                         return;
                     } let lastHeight = 0;
                     }
                     let stableCount = 0;
 
                     const checksNeeded = 3;
                    const item = document.createElement('div');
                     let totalChecks = 0;
                    item.className = 'subicon';
                     const maxChecks = 15;
                     item.title = s.name || nm;
                     function checkStability() {
 
                         totalChecks++;
                     const slugify = window.__skillSlugify || ((str) => (str || '').toLowerCase().replace(/[^\w]+/g, '-').replace(/^-+|-+$/g, ''));
                         const currentTabHeight = toTab.scrollHeight;
                     item.dataset.slug = slugify(s.name || nm);
                        if (Math.abs(currentTabHeight - lastHeight) < 5) {
 
                            stableCount++;
                    logSubVideo('creating subicon element', {
                         } else {
                        subName: (s.name || s.n || '').trim(),
                             stableCount = 0;
                        parentIdx,
                        } lastHeight = currentTabHeight;
                        className: item.className
                        if (stableCount >= checksNeeded || totalChecks >= maxChecks) {
                    });
                            resolve();
 
                        } else {
                     const img = document.createElement('img');
                             setTimeout(checkStability, 50);
                    img.alt = '';
                    img.src = filePathURL(s.icon || 'Nada.png');
                     item.appendChild(img);
 
                    // Verifica weapon de forma mais robusta
                    const hasWeapon = s.weapon && (
                         (typeof s.weapon === 'object' && Object.keys(s.weapon).length > 0) ||
                         (typeof s.weapon === 'string' && s.weapon.trim() !== '')
                    );
 
                    const subName = (s.name || s.n || '').trim();
 
                    if (hasWeapon) {
                        // Normaliza weapon se for string
                        let weaponObj = s.weapon;
                         if (typeof weaponObj === 'string') {
                             try {
                                weaponObj = JSON.parse(weaponObj);
                            } catch {
                                // Se falhar, tenta formato ~
                                const parts = weaponObj.split('~');
                                if (parts.length >= 2) {
                                    weaponObj = {
                                        icon: parts[0] || 'Nada.png',
                                        powerpve: parts[1] || null,
                                        powerpvp: parts[2] || null,
                                        cooldown: parts[3] || null,
                                        video: parts[4] || '',
                                        energy: parts[5] || null
                                    };
                                    Object.keys(weaponObj).forEach(k => {
                                        if (weaponObj[k] === '' || weaponObj[k] === null) {
                                            delete weaponObj[k];
                                        }
                                    });
                                } else {
                                    weaponObj = null;
                                }
                             }
                         }
                         }
                    } setTimeout(checkStability, 50);
 
                });
                        if (weaponObj && typeof weaponObj === 'object' && Object.keys(weaponObj).length > 0) {
                const nextHeight = toTab.getBoundingClientRect().height;
                            try {
                const finalHeight = Math.max(nextHeight, 100);
                                item.dataset.weapon = JSON.stringify(weaponObj);
                if (Math.abs(finalHeight - currentHeight) < 30) {
                            } catch (e) {
                    wrapper.style.height = '';
                                console.error('[Subskills] Erro ao serializar weapon de subskill', subName, e);
                    return Promise.resolve();
                            }
                } wrapper.style.overflow = 'hidden';
 
                wrapper.style.height = currentHeight + 'px';
                            // Aplica classe inicial se o toggle já está ativo
                wrapper.offsetHeight;
                            if (isWeaponModeOn()) {
                wrapper.style.transition = 'height 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
                                 item.classList.add('has-weapon-available');
                requestAnimationFrame(() => {
                    wrapper.style.height = finalHeight + 'px';
                });
                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;
                    document.body.classList.add('transitioning-tabs');
                    if (currentActive) {
                        currentActive.style.opacity = '0';
                        currentActive.style.transform = 'translateY(-8px)';
                    } setTimeout(async () => {
                        contents.forEach(c => {
                            if (c !== nextActive) {
                                 c.style.display = 'none';
                                c.classList.remove('active');
                             }
                             }
                        }
                    }
                    // CORREÇÃO 4: Evento de clique otimizado para não recarregar vídeos
                    logSubVideo('attaching click handler to subicon', {
                        subName: (s.name || s.n || '').trim(),
                        parentIdx,
                        itemClassName: item.className
                    });
                    item.addEventListener('click', () => {
                        const L = getLabels();
                        const subName = (s.name || s.n || '').trim();
                        logSubVideo('subicon click HANDLER EXECUTED', {
                            subName,
                            parentIdx,
                            weaponMode: isWeaponModeOn(),
                            hasWeaponDataAttr: !!item.dataset.weapon,
                            rawWeaponDataset: item.dataset.weapon || null
                         });
                         });
                         tabs.forEach(b => b.classList.toggle('active', b === btn));
 
                         if (nextActive) {
                         // Lê weapon diretamente do atributo data-weapon
                            nextActive.classList.add('active');
                        let subWeaponData = null;
                            nextActive.style.display = 'block';
                         if (item.dataset.weapon) {
                            nextActive.style.opacity = '0';
                            nextActive.style.visibility = 'hidden';
                            nextActive.offsetHeight;
                             try {
                             try {
                                 if (target === 'skills') {
                                 subWeaponData = JSON.parse(item.dataset.weapon);
                                    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) {
                                            const had = document.body.dataset.suppressSkillPlay;
                                            document.body.dataset.suppressSkillPlay = '1';
                                            toClick.click();
                                            if (had) document.body.dataset.suppressSkillPlay = had;
                                        }
                                    }
                                }
                             } catch (e) {
                             } catch (e) {
                                subWeaponData = null;
                             }
                             }
                        } if (currentActive && nextActive) {
                            await smoothHeightTransition(currentActive, nextActive);
                        } if (nextActive) {
                            nextActive.style.visibility = '';
                            nextActive.style.transform = 'translateY(12px)';
                            requestAnimationFrame(() => {
                                nextActive.style.opacity = '1';
                                nextActive.style.transform = 'translateY(0)';
                                setTimeout(() => {
                                    nextActive.style.opacity = '';
                                    nextActive.style.transform = '';
                                    document.body.classList.remove('transitioning-tabs');
                                    try {
                                        delete document.body.dataset.suppressSkillPlay;
                                    } catch {
                                    }
                                }, 300);
                            });
                         }
                         }
                     }, 120);
                        const hasSubWeapon = !!subWeaponData;
 
                        const weaponOn = isWeaponModeOn();
                        const weaponEquipped = weaponOn && hasSubWeapon && subWeaponData;
 
                        logSubVideo('weapon status for subclick', {
                            subName,
                            weaponEquipped,
                            hasSubWeaponData: !!subWeaponData
                        });
 
                        // Determina descrição (weapon ou normal)
                        const raw = (document.documentElement.lang || 'pt').toLowerCase();
                        const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
 
                        let chosen = '';
                        if (weaponEquipped && subWeaponData) {
                            // Descrição do weapon
                            const weaponDescPack = subWeaponData.desc_i18n || subWeaponData.desc || {};
                            chosen = weaponDescPack[lang] || weaponDescPack.pt || weaponDescPack.en || '';
                        } else {
                            // Descrição normal
                            const base = s.desc_i18n || s.desc || {};
                            chosen = base[lang] || base.pt || base.en || (s.descPt || '');
                        }
 
                        // Determina atributos (weapon ou normal)
                        let attrsObj = {
                            powerpve: s.powerpve,
                            powerpvp: s.powerpvp,
                            energy: s.energy,
                            cooldown: s.cooldown
                        };
                        if (weaponEquipped && subWeaponData) {
                            attrsObj = {
                                powerpve: subWeaponData.powerpve || s.powerpve,
                                powerpvp: subWeaponData.powerpvp || s.powerpvp,
                                energy: subWeaponData.energy || s.energy,
                                cooldown: subWeaponData.cooldown || s.cooldown
                            };
                        }
 
                        // Level (weapon ou normal)
                        const level = (weaponEquipped && subWeaponData && subWeaponData.level)
                            ? subWeaponData.level.toString().trim()
                            : (s.level || '').toString().trim();
 
                        let flagsHTML = '';
                        if (Array.isArray(s.flags) && s.flags.length > 0) {
                            flagsHTML = renderFlagsRow(s.flags);
                        }
 
                        if (descBox) {
                            descBox.innerHTML = `<div class="skill-title"><h3>${s.name || nm}</h3></div>${level ? `<div class="skill-level-line"><span class="attr-label">${L.level} ${level}</span></div>` : ''}${renderSubAttrs(attrsObj, L)}<div class="desc">${chosen.replace(/'''(.*?)'''/g, '<b>$1</b>')}</div>`;
                        }
 
                        if (videoBox) {
                            const oldFlags = videoBox.querySelector('.skill-flags');
                            if (oldFlags) oldFlags.remove();
                            if (flagsHTML) {
                                videoBox.insertAdjacentHTML('beforeend', flagsHTML);
                                applyFlagTooltips(videoBox);
                            }
                        }
 
                        // Vídeo (weapon ou normal) - usa cache já criado
                        const effectiveVideo = getEffectiveVideo(s);
 
                        logSubVideo('effectiveVideo for sub', {
                            subName,
                            parentIdx,
                            effectiveVideo,
                            weaponEquipped,
                            rawBaseVideo: s.video,
                            rawWeaponVideo: subWeaponData?.video
                        });
 
                        if (!effectiveVideo || effectiveVideo.trim() === '' || effectiveVideo === 'Nada.png' || effectiveVideo.toLowerCase().includes('nada.png')) {
                            if (videoBox) videoBox.style.display = 'none';
                        } else {
                            logSubVideo('calling showSubVideo from click handler', { effectiveVideo });
 
                            // Usa getOrCreateSubskillVideo internamente - nunca recria se já existe
                            showSubVideo(effectiveVideo, videoBox);
                        }
 
                        Array.from(subBar.children).forEach(c => {
                            c.classList.remove('active');
                            c.classList.remove('weapon-equipped');
                        });
                        item.classList.add('active');
 
                        // Aplica weapon-equipped se tem weapon e está ativo
                        if (weaponEquipped) {
                            item.classList.add('weapon-equipped');
                        }
                        window.__lastActiveSkillIcon = item;
                    });
 
                     if (window.__globalSkillTooltip) {
                        const { show, hide, measureAndPos, lockUntil } = window.__globalSkillTooltip;
                        const label = item.title || '';
                        item.setAttribute('aria-label', label);
                        if (item.hasAttribute('title')) item.removeAttribute('title');
                        item.addEventListener('mouseenter', () => show(item, label));
                        item.addEventListener('mousemove', () => { if (performance.now() >= lockUntil.value) measureAndPos(item); });
                        item.addEventListener('click', () => { lockUntil.value = performance.now() + 240; measureAndPos(item); });
                        item.addEventListener('mouseleave', hide);
                    }
 
                    subBar.appendChild(item);
                    logSubVideo('subicon appended to subBar', {
                        subName: (s.name || s.n || '').trim(),
                        parentIdx,
                        subBarChildrenCount: subBar.children.length
                    });
                });
 
                // Aplica classes de weapon nas subskills recém-renderizadas
                applyWeaponClassesToSubskills();
 
                // Dispara evento para notificar que subskills estão prontas
                window.dispatchEvent(new CustomEvent('gla:subskills:ready', { detail: { count: order.length } }));
 
                // Remove listener anterior se existir (evita duplicação)
                if (subBar._weaponToggleListener) {
                    window.removeEventListener('gla:weaponToggled', subBar._weaponToggleListener);
                }
 
                // Cria novo listener que opera no subBar atual
                subBar._weaponToggleListener = (e) => {
                    const enabled = e.detail?.enabled ?? false;
 
                    // Aplica classes usando a função auxiliar
                    applyWeaponClassesToSubskills();
 
                    // Atualiza a subskill ativa se houver
                     setTimeout(() => {
                     setTimeout(() => {
                         syncDescHeight();
                         const activeSub = subBar.querySelector('.subicon[data-weapon].active')
                        if (target === 'skins') {
                             || subBar.querySelector('.subicon.active');
                            videosCache.forEach(v => {
                        if (activeSub) {
                                try {
                             activeSub.dispatchEvent(new Event('click', { bubbles: true }));
                                    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';
                                });
                             } if (window.__subskills) window.__subskills.hideAll?.(videoBox);
                            if (videoBox && placeholder) {
                                placeholder.style.display = 'none';
                                placeholder.classList.add('fade-out');
                            }
                         } else {
                         } else {
                             const activeIcon = document.querySelector('.icon-bar .skill-icon.active');
                             api.refreshCurrentSubSafe();
                            if (activeIcon) activeIcon.click();
                         }
                         }
                     }, 450);
                     }, 50);
                };
 
                window.addEventListener('gla:weaponToggled', subBar._weaponToggleListener);
 
                requestAnimationFrame(() => {
                    subRail.classList.remove('collapsed');
                    subRail.classList.remove('hidden');
                    const h = subRail.offsetHeight || 48;
                    if (spacer) spacer.style.height = h + 'px';
                 });
                 });
            }, 10);
        };
        api.hideAll = function (videoBox) {
            const videos = videoBox?.querySelectorAll('.skill-video[data-sub="1"]') || [];
            logSubVideo('api.hideAll called', { videoBoxExists: !!videoBox, videoCount: videos.length });
            videos.forEach(v => {
                try { v.pause(); } catch { }
                v.style.display = 'none';
             });
             });
         })();
         };
         (function initSkinsArrows() {
 
             const carousel = $('.skins-carousel');
        window.renderSubskillsBarFrom = function (el, ctx) { api.renderBarFrom(el, ctx); };
             const wrapper = $('.skins-carousel-wrapper');
 
             const left = $('.skins-arrow.left');
         api.preloadAllSubskillImages = function () {
             const right = $('.skins-arrow.right');
             const allSkillIcons = document.querySelectorAll('.icon-bar .skill-icon[data-subs]');
            if (!carousel || !left || !right || !wrapper) return;
             const preloadPromises = [];
            if (wrapper.dataset.wired) return;
             let totalImages = 0;
            wrapper.dataset.wired = '1';
 
            const scrollAmt = () => Math.round(carousel.clientWidth * 0.6);
             allSkillIcons.forEach(icon => {
            function setState() {
                try {
                const max = carousel.scrollWidth - carousel.clientWidth;
                    const subsRaw = icon.getAttribute('data-subs');
                const x = carousel.scrollLeft;
                    if (!subsRaw) return;
                const hasLeft = x > 5, hasRight = x < max - 5;
                    const subs = JSON.parse(subsRaw);
                left.style.display = hasLeft ? 'inline-block' : 'none';
                    if (!Array.isArray(subs)) return;
                right.style.display = hasRight ? 'inline-block' : 'none';
 
                 wrapper.classList.toggle('has-left', hasLeft);
                    subs.forEach(s => {
                wrapper.classList.toggle('has-right', hasRight);
                        if (s && s.icon && s.icon.trim() !== '' && s.icon !== 'Nada.png') {
                 carousel.style.justifyContent = (!hasLeft && !hasRight) ? 'center' : '';
                            preloadPromises.push(preloadImage(s.icon));
             } function go(dir) {
                            totalImages++;
                 const max = carousel.scrollWidth - carousel.clientWidth;
                        }
                 const next = dir < 0 ? Math.max(0, carousel.scrollLeft - scrollAmt()) : Math.min(max, carousel.scrollLeft + scrollAmt());
                        if (s && Array.isArray(s.subs)) {
                carousel.scrollTo({
                            s.subs.forEach(nested => {
                    left: next, behavior: 'smooth'
                                if (nested && nested.icon && nested.icon.trim() !== '' && nested.icon !== 'Nada.png') {
                                    preloadPromises.push(preloadImage(nested.icon));
                                    totalImages++;
                                }
                            });
                        }
                    });
                 } catch (e) {
                    console.error('[Subskills] preloadAllSubskillImages error:', e);
                 }
            });
 
             if (totalImages > 0) {
                 console.log('[Subskills] preloadAllSubskillImages: pré-carregando', totalImages, 'ícones');
                 return Promise.all(preloadPromises).then(() => {
                    console.log('[Subskills] preloadAllSubskillImages: todos os ícones carregados');
                 });
                 });
            } left.addEventListener('click', () => go(-1));
            right.addEventListener('click', () => go(1));
            carousel.addEventListener('scroll', setState);
            new ResizeObserver(setState).observe(carousel);
            setState();
        })();
        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());
            // Processa valores, tratando strings vazias como NaN
            // IMPORTANTE: ordem fixa é [powerpve, powerpvp, energy, cooldown]
            const pve = (vals[0] && vals[0] !== '') ? parseFloat(vals[0]) : NaN;
            const pvp = (vals[1] && vals[1] !== '') ? parseFloat(vals[1]) : NaN;
            const ene = (vals[2] && vals[2] !== '') ? parseFloat(vals[2]) : NaN;
            const cd = (vals[3] && vals[3] !== '') ? parseFloat(vals[3]) : NaN;
            // Debug: log se houver problema na ordem
            if (str && str.includes(',') && !isNaN(cd) && !isNaN(pvp) && cd === pvp) {
                console.warn('[Skills] renderAttributes: possível problema na ordem dos atributos', { str, vals, pve, pvp, ene, cd });
             }
             }
             const rows = [];
             return Promise.resolve();
            // Ordem de exibição: cooldown, energy, power, power_pvp
        };
            if (!isNaN(cd)) rows.push([L.cooldown, cd]);
 
             if (!isNaN(ene) && ene !== 0) {
        // Função para pré-carregar vídeos de subskills usando getOrCreateSubskillVideo
                 const label = ene > 0 ? L.energy_gain : L.energy_cost;
        api.preloadAllSubskillVideos = function () {
                 rows.push([label, Math.abs(ene)]);
            const videoBox = document.querySelector('.video-container');
             if (!videoBox) {
                 logSubVideo('preloadAllSubskillVideos: videoBox not found, skipping');
                 return;
             }
             }
             if (!isNaN(pve)) rows.push([L.power, pve]);
 
            if (!isNaN(pvp)) rows.push([L.power_pvp, pvp]);
            const allSkillIcons = document.querySelectorAll('.icon-bar .skill-icon[data-subs]');
            // Debug: log se houver valores suspeitos
            let createdCount = 0;
            if (str && str.includes(',')) {
 
                 console.log('[Skills] renderAttributes processed', {
             logSubVideo('preloadAllSubskillVideos: starting', { iconCount: allSkillIcons.length });
                     str,
 
                    vals: vals.slice(0, 4),
            allSkillIcons.forEach(icon => {
                    parsed: { pve, pvp, ene, cd },
                try {
                    rows: rows.map(r => r[0])
                    const subsRaw = icon.getAttribute('data-subs');
                    if (!subsRaw) return;
                    const subs = JSON.parse(subsRaw);
                    if (!Array.isArray(subs)) return;
 
                    subs.forEach(s => {
                        // Pré-cria vídeo normal usando getOrCreateSubskillVideo
                        if (s.video && s.video.trim() !== '' && s.video !== 'Nada.png' && !s.video.toLowerCase().includes('nada.png')) {
                            const video = getOrCreateSubskillVideo(s.video);
                            if (video) {
                                if (!video.isConnected) {
                                    videoBox.appendChild(video);
                                    createdCount++;
                                }
                                // FORÇA carregamento imediato
                                if (video.readyState < 2) {
                                    video.muted = true; // Permite autoload em mais navegadores
                                    video.load();
                                }
                            }
                        }
 
                        // Pré-cria vídeo de weapon usando getOrCreateSubskillVideo
                        if (s.weapon && typeof s.weapon === 'object' && s.weapon.video && s.weapon.video.trim() !== '' && s.weapon.video !== 'Nada.png' && !s.weapon.video.toLowerCase().includes('nada.png')) {
                            const weaponVideo = getOrCreateSubskillVideo(s.weapon.video);
                            if (weaponVideo) {
                                if (!weaponVideo.isConnected) {
                                    videoBox.appendChild(weaponVideo);
                                    createdCount++;
                                }
                                // FORÇA carregamento imediato
                                if (weaponVideo.readyState < 2) {
                                    weaponVideo.muted = true; // Permite autoload em mais navegadores
                                    weaponVideo.load();
                                }
                            }
                        }
                    });
                 } catch (e) {
                    console.error('[Subskills] preloadAllSubskillVideos error:', e);
                }
            });
 
            logSubVideo('preloadAllSubskillVideos: completed', { createdCount, cacheSize: subskillVideoElementCache.size });
 
            // Força carregamento novamente após um delay para garantir
            setTimeout(() => {
                subskillVideoElementCache.forEach(video => {
                     if (video.readyState < 2) {
                        video.muted = true;
                        video.load();
                    }
                });
            }, 100);
        };
 
        // Função syncVideoCaches removida - não é mais necessária com cache baseado em URL
 
        // Inicialização
        function init() {
            logSubVideo('init: starting initialization');
 
            // Constrói cache das skills principais
            getMainSkillsMap();
 
            // Pré-carrega imagens das subskills IMEDIATAMENTE
            api.preloadAllSubskillImages();
 
            // Pré-carrega vídeos das subskills usando getOrCreateSubskillVideo IMEDIATAMENTE
            api.preloadAllSubskillVideos();
 
            // Força carregamento novamente após delays para garantir
            setTimeout(() => {
                api.preloadAllSubskillVideos();
            }, 100);
 
            setTimeout(() => {
                // Força load em todos os vídeos que ainda não carregaram
                subskillVideoElementCache.forEach(video => {
                    if (video.readyState < 2) {
                        video.muted = true;
                        video.load();
                    }
                 });
                 });
             }
             }, 500);
             if (!rows.length) return '';
 
             const html = rows.map(([label, value]) => `<div class="attr-row"><span class="attr-label">${label}</span><span class="attr-value">${value}</span></div>`).join('');
             // Escuta mudanças no localStorage
             return `<div class="attr-list">${html}</div>`;
             window.addEventListener('storage', (e) => {
        } function syncDescHeight() {
                if (e.key === 'glaWeaponEnabled') {
        } window.addEventListener('resize', syncDescHeight);
                    setTimeout(() => api.refreshCurrentSubSafe(), 50);
        if (videoBox) new ResizeObserver(syncDescHeight).observe(videoBox);
                }
        iconItems.forEach(el => {
             });
            const wired = !!el.dataset._sync_wired;
 
            if (wired) return;
            // LISTENER GLOBAL: Escuta evento de toggle
            el.dataset._sync_wired = '1';
            window.addEventListener('gla:weaponToggled', (e) => {
            el.addEventListener('click', () => {
                const enabled = e.detail?.enabled ?? false;
                Promise.resolve().then(syncDescHeight);
 
                // Tenta aplicar classes imediatamente
                applyWeaponClassesToSubskills();
 
                // Atualiza a subskill ativa se houver
                setTimeout(() => {
                    const activeSub = document.querySelector('.subicon[data-weapon].active')
                        || document.querySelector('.subicon.active');
                    if (activeSub) {
                        activeSub.dispatchEvent(new Event('click', { bubbles: true }));
                    } else {
                        api.refreshCurrentSubSafe();
                    }
                }, 50);
             });
             });
        });
 
        if (iconsBar) addOnce(iconsBar, 'wheel', (e) => {
            // LISTENER: Escuta quando subskills estão prontas
            if (e.deltaY) {
            window.addEventListener('gla:subskills:ready', (e) => {
                 e.preventDefault();
                 // Aplica classes de weapon se o toggle estiver ativo
                 iconsBar.scrollLeft += e.deltaY;
                 applyWeaponClassesToSubskills();
            }
        });
        wireClicksForCurrentBar();
        if (iconItems.length) {
            const first = iconItems[0];
            if (first) {
                activateSkill(first, {
                    openSubs: false
                });
            }
        } setTimeout(() => {
            Array.from(document.querySelectorAll('.skill-icon')).forEach(el => {
             });
             });
            videosCache.forEach((v, idx) => {
        }
                const src = v.querySelector('source') ? v.querySelector('source').src : v.src;
 
                v.addEventListener('error', (ev) => {
        if (document.readyState === 'loading') {
                 });
            document.addEventListener('DOMContentLoaded', () => {
                v.addEventListener('loadedmetadata', () => {
                 setTimeout(init, 100);
                });
             });
             });
         }, 600);
         } else {
            setTimeout(init, 100);
        }
     })();
     })();
</script>
</script>
<style>
    .subicon-bar {
        display: flex;
        gap: 10px;
        padding: 6px 6px;
        overflow-x: auto;
        /* Firefox */
        scrollbar-width: thin;
        scrollbar-color: #ababab transparent;
    }
    .subicon-bar::-webkit-scrollbar {
        height: 6px;
    }
    .subicon-bar::-webkit-scrollbar-thumb {
        background: #151515;
        border-radius: 3px;
    }
    .subicon {
        width: var(--icon-size, 42px);
        height: var(--icon-size, 42px);
        border-radius: var(--icon-radius, 10px);
        overflow: hidden;
        position: relative;
        flex: 0 0 auto;
        cursor: pointer;
        isolation: isolate;
    }
    .subicon img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
        border-radius: inherit;
    }
    .subicon::after {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: inherit;
        box-shadow: inset 0 0 0 var(--icon-ring-w, 2px) var(--icon-idle, #cfcfcf);
        pointer-events: none;
        z-index: 2;
        transition: box-shadow .12s ease;
    }
    .subicon:hover::after {
        box-shadow: inset 0 0 0 var(--icon-ring-w, 2px) #e6e6e6;
    }
    .subicon.active::after {
        box-shadow: inset 0 0 0 var(--icon-ring-w, 2px) var(--icon-active, #FFD95A);
    }
    .subicon.active::before {
        content: "";
        position: absolute;
        inset: -4px;
        border-radius: calc(var(--icon-radius, 10px) + 4px);
        pointer-events: none;
        z-index: 1;
        opacity: 1;
        box-shadow: 0 0 12px 3px var(--icon-active-glow, rgba(255, 217, 90, .30)),
            0 0 0 calc(var(--icon-ring-w, 2px) * 2) var(--icon-active-ring, rgba(255, 217, 90, .50));
    }
    .top-rail.skills {
        position: relative;
        display: flex;
        flex-direction: column;
        align-items: center;
        overflow: visible;
    }
    .top-rail.skills .icon-bar {
        margin-bottom: 0;
        position: relative;
        z-index: 2;
    }
    .subskills-rail {
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
        top: calc(100% - 1px);
        z-index: 3;
        display: inline-flex;
        justify-content: center;
        align-items: center;
        width: auto;
        max-width: 100%;
        padding: 3px 5px;
        background: rgba(0, 0, 0, .38);
        border: 1px solid rgba(255, 255, 255, .10);
        border-top: 1px solid rgba(255, 255, 255, .08);
        border-radius: 0 0 10px 10px;
        box-shadow: 0 3px 9px rgba(0, 0, 0, .22);
        -webkit-backdrop-filter: blur(2px);
        backdrop-filter: blur(2px);
        overflow: hidden;
        transition: opacity .14s ease, transform .14s ease;
        opacity: 1;
    }
    .subskills-rail::before {
        content: "";
        position: absolute;
        top: -6px;
        left: 0;
        right: 0;
        height: 6px;
        background: linear-gradient(to bottom, rgba(0, 0, 0, .20), rgba(0, 0, 0, 0));
        pointer-events: none;
    }
    .subskills-rail.collapsed {
        opacity: 0;
        pointer-events: none;
        transform: translate(-50%, -6px);
    }
    .subskills-rail.hidden {
        visibility: hidden;
    }
    .subskills-spacer {
        height: 0;
        transition: height .2s ease;
    }
    .subskills-rail .subicon-bar {
        display: inline-flex;
        align-items: center;
        gap: 0;
        overflow-x: auto;
        /* Firefox */
        scrollbar-width: thin;
        scrollbar-color: #ababab transparent;
    }
    .subskills-rail .subicon-bar::-webkit-scrollbar {
        height: 6px;
    }
    .subskills-rail .subicon-bar::-webkit-scrollbar-thumb {
        background: #151515;
        border-radius: 3px;
    }
    .subskills-rail .subicon {
        width: 42px;
        height: 42px;
        border-radius: 6px;
        position: relative;
        overflow: hidden;
        flex: 0 0 auto;
        cursor: pointer;
        isolation: isolate;
        -webkit-backface-visibility: hidden;
        backface-visibility: hidden;
        transform: translateZ(0);
    }
    .subskills-rail .subicon+.subicon {
        margin-left: 4px;
    }
    .subskills-rail .subicon img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
        border-radius: inherit;
    }
    .subskills-rail .subicon::after {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: inherit;
        box-shadow: inset 0 0 0 2px var(--icon-idle, #cfcfcf);
        pointer-events: none;
        z-index: 2;
        transition: box-shadow .12s ease;
    }
    .subskills-rail .subicon:hover::after {
        box-shadow: inset 0 0 0 2px #e6e6e6;
    }
    .subskills-rail .subicon.active::after {
        box-shadow: inset 0 0 0 2px var(--icon-active, #FFD95A);
    }
    .video-container .skill-video {
        width: 100%;
        height: auto;
        aspect-ratio: 16 / 9;
        object-fit: cover;
        background: #000;
        border-radius: 10px;
    }
    @media (max-width: 900px) {
        .subskills-rail {
            position: static;
            transform: none;
            margin-top: -2px;
            border-top: 0;
            border-radius: 0 0 10px 10px;
        }
        .subskills-spacer {
            height: 0 !important;
        }
    }
    .skills-rail-wrap {
        position: relative;
        display: block;
        width: max-content;
        margin: 0 auto;
    }
    /* Subskills com arma disponível - borda vermelha quando inativa */
    .character-box .top-rail.skills .subicon.has-weapon-available:not(.active)::after {
        box-shadow: inset 0 0 0 2px rgba(220, 70, 70, 0.85) !important;
    }
    /* Subskill com arma ATIVA - laranja/coral vibrante + brilho forte */
    .character-box .top-rail.skills .subicon.has-weapon-available.active::after {
        box-shadow: inset 0 0 0 2px #FF7043 !important;
    }
    .character-box .top-rail.skills .subicon.has-weapon-available.active::before {
        box-shadow:
            0 0 12px 4px rgba(255, 112, 67, 0.6),
            0 0 24px 8px rgba(255, 87, 34, 0.35),
            0 0 0 3px rgba(255, 112, 67, 0.4) !important;
        opacity: 1 !important;
    }
    /* Modo arma ON - efeito animado nos subicons */
    .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available {
        position: relative;
    }
    .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available::after {
        box-shadow: none !important;
        background: linear-gradient(90deg,
                rgba(255, 80, 80, 0.9) 0%,
                rgba(255, 120, 60, 1) 25%,
                rgba(255, 80, 80, 0.9) 50%,
                rgba(255, 120, 60, 1) 75%,
                rgba(255, 80, 80, 0.9) 100%) !important;
        background-size: 400% 100% !important;
        animation: weapon-subicon-border-scan 4s linear infinite !important;
        -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) !important;
        mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) !important;
        -webkit-mask-composite: xor !important;
        mask-composite: exclude !important;
        padding: 2px !important;
    }
    .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available::before {
        content: "";
        position: absolute;
        inset: -4px;
        border-radius: calc(6px + 4px);
        pointer-events: none;
        z-index: 1;
        box-shadow:
            0 0 10px 3px rgba(255, 80, 80, 0.5),
            0 0 20px 6px rgba(255, 80, 80, 0.25) !important;
        opacity: 1 !important;
        animation: weapon-subicon-pulse 3s ease-in-out infinite !important;
    }
    /* Subskill ativa com arma - mais intenso */
    .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available.active::after {
        background: linear-gradient(90deg,
                rgba(255, 87, 34, 1) 0%,
                rgba(255, 140, 60, 1) 25%,
                rgba(255, 87, 34, 1) 50%,
                rgba(255, 140, 60, 1) 75%,
                rgba(255, 87, 34, 1) 100%) !important;
    }
    .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available.active::before {
        box-shadow:
            0 0 14px 5px rgba(255, 87, 34, 0.65),
            0 0 28px 10px rgba(255, 87, 34, 0.35),
            0 0 0 3px rgba(255, 87, 34, 0.45) !important;
        animation: weapon-subicon-pulse-active 2.5s ease-in-out infinite !important;
    }
    @keyframes weapon-subicon-border-scan {
        0% {
            background-position: 0% 0%;
        }
        100% {
            background-position: 400% 0%;
        }
    }
    @keyframes weapon-subicon-pulse {
        0%,
        100% {
            opacity: 0.8;
            box-shadow: 0 0 8px 2px rgba(255, 80, 80, 0.45), 0 0 16px 4px rgba(255, 80, 80, 0.2);
        }
        50% {
            opacity: 1;
            box-shadow: 0 0 12px 4px rgba(255, 80, 80, 0.6), 0 0 24px 8px rgba(255, 80, 80, 0.3);
        }
    }
    @keyframes weapon-subicon-pulse-active {
        0%,
        100% {
            opacity: 0.85;
            box-shadow: 0 0 10px 4px rgba(255, 87, 34, 0.55), 0 0 22px 8px rgba(255, 87, 34, 0.3), 0 0 0 2px rgba(255, 87, 34, 0.35);
        }
        50% {
            opacity: 1;
            box-shadow: 0 0 16px 6px rgba(255, 87, 34, 0.7), 0 0 32px 12px rgba(255, 87, 34, 0.4), 0 0 0 3px rgba(255, 87, 34, 0.5);
        }
    }
</style>

Edição das 00h28min de 23 de dezembro de 2025

<script>

   (function () {
       const SUBVIDEO_DEBUG = true;
       function logSubVideo(...args) {
           if (!SUBVIDEO_DEBUG) return;
           console.log('[SubVideo]', ...args);
       }
       const api = (window.__subskills ||= {});
       // Cache global de elementos de vídeo para subskills - baseado em URL
       const subskillVideoElementCache = new Map(); // key: videoURL (string), value: HTMLVideoElement
       const imagePreloadCache = new Map();
       let subRail, subBar, spacer;
       // Cache das skills principais (capturado na carga da página)
       let cachedMainSkills = null;
       // ===== HERANÇA DE ATRIBUTOS: busca dados das skills principais =====
       function getMainSkillsMap() {
           // Retorna cache se já foi construído E tem dados
           if (cachedMainSkills && cachedMainSkills.byIndex.size > 0) {
               return cachedMainSkills;
           }
           const maps = {
               byName: new Map(),
               byIndex: new Map()
           };
           // Busca skills com data-index (skills principais, não subskills)
           const icons = document.querySelectorAll('.icon-bar .skill-icon[data-index][data-nome]');
           icons.forEach(icon => {
               const name = (icon.dataset.nome || ).trim();
               if (!name) return;
               // Extrai atributos do data-atr (formato: "pve, pvp, energy, cd")
               const atrRaw = icon.dataset.atr || ;
               const parts = atrRaw.split(',').map(x => (x || ).trim());
               const powerpve = parts[0] && parts[0] !== '-' ? parts[0] : ;
               const powerpvp = parts[1] && parts[1] !== '-' ? parts[1] : ;
               const energy = parts[2] && parts[2] !== '-' ? parts[2] : ;
               const cooldown = parts[3] && parts[3] !== '-' ? parts[3] : ;
               // Nome original do arquivo de ícone (armazenado no dataset pelo widget de skills)
               let iconFile = (icon.dataset.iconFile || ).trim();
               if (!iconFile) {
                   const imgSrc = icon.querySelector('img')?.src || ;
                   const iconMatch = imgSrc.match(/(?:FilePath|images)\/([^\/?]+)$/);
                   iconFile = iconMatch ? decodeURIComponent(iconMatch[1]) : ;
               }
               // Nome original do arquivo de vídeo (caso exista)
               let videoFile = (icon.dataset.videoFile || ).trim();
               if (!videoFile) {
                   const videoUrl = icon.dataset.video || ;
                   const videoMatch = videoUrl.match(/FilePath\/([^&?]+)/);
                   videoFile = videoMatch ? decodeURIComponent(videoMatch[1]) : ;
               }
               const index = (icon.dataset.index || ).trim();
               const data = {
                   name: name,  // Inclui o nome para herança completa
                   icon: iconFile,
                   level: icon.dataset.level || ,
                   video: videoFile,
                   powerpve: powerpve,
                   powerpvp: powerpvp,
                   cooldown: cooldown,
                   energy: energy
               };
               // Mantém descrições caso precise como fallback extra
               if (icon.dataset.descPt) data.descPt = icon.dataset.descPt;
               if (icon.dataset.descEn) data.descEn = icon.dataset.descEn;
               if (icon.dataset.descEs) data.descEs = icon.dataset.descEs;
               if (icon.dataset.descPl) data.descPl = icon.dataset.descPl;
               maps.byName.set(name, data);
               if (index) {
                   // Guarda tanto como string quanto como número para compatibilidade
                   maps.byIndex.set(index, data);
                   maps.byIndex.set(parseInt(index, 10), data);
               }
           });
           // Cacheia para uso futuro (importante: as skills principais não mudam)
           cachedMainSkills = maps;
           return maps;
       }
       // Aplica herança COMPLETA: se subskill só tem refM, busca TUDO da skill principal
       function applyInheritance(sub, mainSkills) {
           let name = (sub.name || sub.n || ).trim();
           const refIndex = ((sub.refM || sub.m || sub.M || ) + ).trim();
           let main = null;
           // Tenta como string
           if (refIndex && mainSkills.byIndex.has(refIndex)) {
               main = mainSkills.byIndex.get(refIndex);
           }
           // Tenta como número
           if (!main && refIndex) {
               const numIndex = parseInt(refIndex, 10);
               if (!isNaN(numIndex) && mainSkills.byIndex.has(numIndex)) {
                   main = mainSkills.byIndex.get(numIndex);
               }
           }
           if (!main && name && mainSkills.byName.has(name)) {
               main = mainSkills.byName.get(name);
           }
           // Se não tem main skill para herdar, retorna como está
           if (!main) {
               return sub;
           }
           // Se não tem nome mas tem refM, herda o nome da skill principal
           if (!name && refIndex && main.name) {
               name = main.name;
           }
           return {
               ...sub,
               name: name || main.name || sub.name,
               icon: (sub.icon && sub.icon !== 'Nada.png' && sub.icon !== ) ? sub.icon : (main.icon || 'Nada.png'),
               level: sub.level || main.level,
               video: sub.video || main.video,
               powerpve: sub.powerpve || main.powerpve,
               powerpvp: sub.powerpvp || main.powerpvp,
               cooldown: sub.cooldown || main.cooldown,
               energy: sub.energy || main.energy,
               descPt: sub.descPt || (sub.desc_i18n?.pt) || main.descPt,
               descEn: sub.descEn || (sub.desc_i18n?.en) || main.descEn,
               descEs: sub.descEs || (sub.desc_i18n?.es) || main.descEs,
               descPl: sub.descPl || (sub.desc_i18n?.pl) || main.descPl
           };
       }
       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 && mw.config ? (mw.config.get('wgScript') || '/index.php') : '/index.php');
           return `${base}?title=Especial:FilePath/${f}`;
       }
       function normalizeFileURL(raw, fallback = ) {
           if (!raw) return fallback;
           const val = String(raw).trim();
           if (!val) return fallback;
           if (/^(https?:)?\/\//i.test(val) || val.startsWith('data:') || val.includes('Especial:FilePath/')) {
               return val;
           }
           return filePathURL(val);
       }
       function preloadImage(iconFile) {
           const url = filePathURL(iconFile || 'Nada.png');
           if (imagePreloadCache.has(url)) {
               return imagePreloadCache.get(url);
           }
           const promise = new Promise((resolve, reject) => {
               const img = new Image();
               img.decoding = 'async';
               img.loading = 'eager';
               img.referrerPolicy = 'same-origin';
               img.onload = () => resolve(url);
               img.onerror = () => resolve(url);
               img.src = url;
           });
           imagePreloadCache.set(url, promise);
           return promise;
       }
       function getLabels() {
           const skillsRoot = document.getElementById('skills');
           const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
           const raw = (document.documentElement.lang || skillsRoot?.dataset.i18nDefault || 'pt').toLowerCase();
           const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
           return 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'
           };
       }
       // Verifica se o modo weapon está ativo
       function isWeaponModeOn() {
           try {
               return localStorage.getItem('glaWeaponEnabled') === '1';
           } catch (e) {
               return false;
           }
       }
       // Retorna os atributos corretos (weapon ou normal)
       function getEffectiveAttrs(s) {
           const weaponOn = isWeaponModeOn();
           if (weaponOn && s.weapon) {
               return {
                   powerpve: s.weapon.powerpve || s.powerpve,
                   powerpvp: s.weapon.powerpvp || s.powerpvp,
                   energy: s.weapon.energy || s.energy,
                   cooldown: s.weapon.cooldown || s.cooldown
               };
           }
           return {
               powerpve: s.powerpve,
               powerpvp: s.powerpvp,
               energy: s.energy,
               cooldown: s.cooldown
           };
       }
       // Retorna a descrição correta (weapon ou normal)
       // Aceita tanto desc_i18n quanto desc para compatibilidade
       function getEffectiveDesc(s) {
           const weaponOn = isWeaponModeOn();
           const raw = (document.documentElement.lang || 'pt').toLowerCase();
           const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
           // Para weapon: aceita tanto desc_i18n quanto desc
           if (weaponOn && s.weapon) {
               const wDesc = s.weapon.desc_i18n || s.weapon.desc;
               if (wDesc) {
                   return wDesc[lang] || wDesc.pt || wDesc.en || ;
               }
           }
           // Para descrição base: aceita tanto desc_i18n quanto desc
           const base = s.desc_i18n || s.desc;
           if (base) {
               return base[lang] || base.pt || base.en || ;
           }
           // Fallback para campos individuais (compatibilidade)
           const descI18n = {
               pt: s.descPt || ,
               en: s.descEn || ,
               es: s.descEs || ,
               pl: s.descPl || 
           };
           return descI18n[lang] || descI18n.pt || ;
       }
       // Retorna o vídeo correto (weapon ou normal)
       function getEffectiveVideo(s) {
           const weaponOn = isWeaponModeOn();
           if (weaponOn && s.weapon && s.weapon.video && s.weapon.video.trim() !== ) {
               return s.weapon.video;
           }
           return s.video || ;
       }
       // Função única para obtenção de vídeo de subskill - baseada em URL
       function getOrCreateSubskillVideo(videoURL) {
           if (!videoURL || videoURL.trim() === ) return null;
           const normalizedURL = normalizeFileURL(videoURL);
           if (!normalizedURL || normalizedURL.trim() === ) return null;
           // Verifica se já existe no cache
           if (subskillVideoElementCache.has(normalizedURL)) {
               logSubVideo('getOrCreateSubskillVideo: cache hit', { videoURL: normalizedURL });
               return subskillVideoElementCache.get(normalizedURL);
           }
           // Cria novo elemento de vídeo
           logSubVideo('getOrCreateSubskillVideo: creating new video', { videoURL: normalizedURL });
           const v = document.createElement('video');
           v.className = 'skill-video';
           v.dataset.sub = '1';
           v.setAttribute('controls', );
           v.setAttribute('preload', 'auto');
           v.setAttribute('playsinline', );
           v.muted = true; // Permite autoload em mais navegadores
           v.style.display = 'none';
           v.style.width = '100%';
           v.style.height = 'auto';
           v.style.aspectRatio = '16/9';
           v.style.objectFit = 'cover';
           // Detectar formato do vídeo pela extensão
           const ext = (normalizedURL.split('.').pop() || ).toLowerCase().split('?')[0];
           const mimeTypes = {
               'mp4': 'video/mp4',
               'm4v': 'video/mp4',
               'webm': 'video/webm',
               'ogv': 'video/ogg',
               'ogg': 'video/ogg',
               'mov': 'video/quicktime'
           };
           const mimeType = mimeTypes[ext] || 'video/mp4';
           const src = document.createElement('source');
           src.src = normalizedURL;
           src.type = mimeType;
           v.appendChild(src);
           // Fallback para Safari/iOS mais antigos
           v.setAttribute('webkit-playsinline', );
           v.setAttribute('x-webkit-airplay', 'allow');
           // Adiciona ao cache
           subskillVideoElementCache.set(normalizedURL, v);
           // Adiciona ao DOM imediatamente e força carregamento
           const videoBox = document.querySelector('.video-container');
           if (videoBox && !v.isConnected) {
               videoBox.appendChild(v);
               // Força carregamento imediato
               v.load();
           }
           return v;
       }
       function renderSubAttrs(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.startsWith('-') ? en.replace(/^-/, ) : en.replace(/^\+?/, )) : ,
               pve ? chip(L.power, pve) : ,
               pvp ? chip(L.power_pvp, pvp) : ,
           ].filter(Boolean);

return rows.length ? `

${rows.join()}

` : ;

       }
       function renderFlagsRow(flags) {
           const map = {
               aggro: 'Enemyaggro-icon.png',
               bridge: 'Bridgemaker-icon.png',
               wall: 'Destroywall-icon.png',
               quickcast: 'Quickcast-icon.png',
               wallpass: 'Passthroughwall-icon.png'
           };
           const arr = (flags || []).filter(Boolean);
           if (!arr.length) return ;
           const items = arr.map(k => `<img class="skill-flag" data-flag="${k}" alt="" src="${filePathURL(map[k])}">`).join();

return `

${items}

`;

       }
       function applyFlagTooltips(container) {
           const skillsRoot = document.getElementById('skills');
           if (!skillsRoot) return;
           let pack = {};
           try { pack = JSON.parse(skillsRoot.dataset.i18nFlags || '{}'); } catch (e) { }
           const raw = (document.documentElement.lang || 'pt').toLowerCase();
           const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
           const dict = pack[lang] || pack.pt || {};
           const flags = container.querySelectorAll('.skill-flags .skill-flag[data-flag]');
           const tooltip = window.__globalSkillTooltip;
           if (!tooltip) return;
           flags.forEach(el => {
               const key = el.getAttribute('data-flag');
               const tip = (dict && dict[key]) || ;
               if (!tip) return;
               if (el.dataset.flagTipWired) return;
               el.dataset.flagTipWired = '1';
               el.setAttribute('aria-label', tip);
               if (el.hasAttribute('title')) el.removeAttribute('title');
               el.addEventListener('mouseenter', () => {
                   const tipEl = document.querySelector('.skill-tooltip');
                   if (tipEl) tipEl.classList.add('flag-tooltip');
                   tooltip.show(el, tip);
               });
               el.addEventListener('mousemove', () => {
                   if (performance.now() >= tooltip.lockUntil.value) {
                       tooltip.measureAndPos(el);
                   }
               });
               el.addEventListener('click', () => {
                   tooltip.lockUntil.value = performance.now() + 240;
                   tooltip.measureAndPos(el);
               });
               el.addEventListener('mouseleave', () => {
                   const tipEl = document.querySelector('.skill-tooltip');
                   if (tipEl) tipEl.classList.remove('flag-tooltip');
                   tooltip.hide();
               });
           });
       }
       function ensureRail(iconsBar) {
           const rail = iconsBar.closest('.top-rail');
           if (!rail) {
               return null;
           }
           if (!subRail) {
               subRail = document.createElement('div');
               subRail.className = 'subskills-rail collapsed hidden';
               rail.appendChild(subRail);
           }
           if (!subBar) {
               subBar = document.createElement('div');
               subBar.className = 'subicon-bar';
               subRail.appendChild(subBar);
           }
           if (!spacer) {
               spacer = document.createElement('div');
               spacer.className = 'subskills-spacer';
               rail.parentNode.insertBefore(spacer, rail.nextSibling);
           }
           return rail;
       }
       // Função para mostrar vídeo de subskill usando cache baseado em URL
       function showSubVideo(videoURL, videoBox) {
           logSubVideo('showSubVideo called', { videoURL, videoBoxExists: !!videoBox });
           if (!videoBox) return;
           // Ocultar todos os vídeos existentes no container
           videoBox.querySelectorAll('video.skill-video[data-sub="1"]').forEach(v => {
               v.style.display = 'none';
           });
           // Obter vídeo via getOrCreateSubskillVideo (única forma de obter vídeos)
           const video = getOrCreateSubskillVideo(videoURL);
           if (!video) {
               logSubVideo('no video found for URL, hiding box', { videoURL });
               videoBox.style.display = 'none';
               return;
           }
           // Se o vídeo não estiver conectado ao DOM, anexá-lo ao container
           if (!video.isConnected || video.parentNode !== videoBox) {
               logSubVideo('video not in DOM, appending', {
                   videoURL,
                   isConnected: video.isConnected,
                   parentNode: video.parentNode
               });
               if (video.parentNode) {
                   video.parentNode.removeChild(video);
               }
               videoBox.appendChild(video);
           }
           logSubVideo('showing video', {
               videoURL,
               readyState: video.readyState,
               paused: video.paused,
               currentTime: video.currentTime
           });
           // Exibir o vídeo
           videoBox.style.display = 'block';
           video.style.display = 'block';
           // Reinicia o vídeo para feedback visual imediato
           try {
               video.currentTime = 0;
           } catch (e) { }
           // Tenta dar play se o vídeo já está pronto
           if (video.readyState >= 2) {
               video.play().then(() => {
                   logSubVideo('play() resolved', { videoURL, currentTime: video.currentTime, readyState: video.readyState });
               }).catch(err => {
                   logSubVideo('play() rejected', { videoURL, error: err });
               });
           } else {
               // Se não está pronto ainda, espera o evento canplay
               logSubVideo('video not ready, waiting for canplay', { videoURL, readyState: video.readyState });
               const onCanPlay = () => {
                   video.removeEventListener('canplay', onCanPlay);
                   video.play().catch(() => { });
               };
               video.addEventListener('canplay', onCanPlay, { once: true });
           }
       }
       // Funções antigas removidas - usar getOrCreateSubskillVideo() em vez disso
       // REMOVIDO: ensureSubVideoCached
       // REMOVIDO: ensureSubVideoInCache
       api.refreshCurrentSubSafe = function () {
           const btn = document.querySelector('.subskills-rail .subicon.active');
           if (!btn) return false;
           const had = document.body.dataset.suppressSkillPlay;
           document.body.dataset.suppressSkillPlay = '1';
           try {
               btn.dispatchEvent(new Event('click', { bubbles: true }));
           } finally {
               if (had) document.body.dataset.suppressSkillPlay = had;
               else delete document.body.dataset.suppressSkillPlay;
           }
           return true;
       };
       // Função auxiliar para aplicar classes de weapon nas subskills renderizadas
       const applyWeaponClassesToSubskills = () => {
           const weaponOn = isWeaponModeOn();
           // Busca TODAS as subskills com weapon
           let weaponSubs = document.querySelectorAll('.subicon[data-weapon]');
           weaponSubs.forEach(el => {
               if (weaponOn) {
                   el.classList.add('has-weapon-available');
                   if (el.classList.contains('active')) {
                       el.classList.add('weapon-equipped');
                   }
               } else {
                   el.classList.remove('has-weapon-available');
                   el.classList.remove('weapon-equipped');
               }
           });
       };
       api.renderBarFrom = function (el, { iconsBar, descBox, videoBox }) {
           logSubVideo('api.renderBarFrom called', {
               hasEl: !!el,
               hasIconsBar: !!iconsBar,
               hasDescBox: !!descBox,
               hasVideoBox: !!videoBox,
               parentIndex: el?.dataset?.index || 'unknown'
           });
           const rail = ensureRail(iconsBar);
           if (!rail) {
               logSubVideo('api.renderBarFrom: no rail found, returning');
               return;
           }
           const rawSubs = el.getAttribute('data-subs') || ;
           const rawOrder = el.getAttribute('data-suborder') || ;
           const parentIdx = el.dataset.index || ;
           logSubVideo('api.renderBarFrom: parsing subs', {
               parentIdx,
               hasRawSubs: !!rawSubs.trim(),
               rawSubsLength: rawSubs.length
           });
           if (!rawSubs.trim()) {
               if (subRail) subRail.classList.add('collapsed');
               if (subRail) subRail.classList.add('hidden');
               if (subBar) subBar.innerHTML = ;
               if (spacer) spacer.style.height = '0px';
               return;
           }
           let subs;
           try { subs = JSON.parse(rawSubs); } catch { subs = []; }
           // NORMALIZADOR: Converte weaponPacked para weapon se necessário
           subs = subs.map(sub => {
               // Se tem weaponPacked mas não tem weapon, tenta parsear
               if (sub.weaponPacked && !sub.weapon && typeof sub.weaponPacked === 'string' && sub.weaponPacked.trim() !== ) {
                   const parts = sub.weaponPacked.split('~');
                   if (parts.length >= 2) {
                       sub.weapon = {
                           icon: parts[0] || 'Nada.png',
                           powerpve: parts[1] || null,
                           powerpvp: parts[2] || null,
                           cooldown: parts[3] || null,
                           video: parts[4] || ,
                           energy: parts[5] || null
                       };
                       // Remove valores vazios
                       Object.keys(sub.weapon).forEach(k => {
                           if (sub.weapon[k] ===  || sub.weapon[k] === null) {
                               delete sub.weapon[k];
                           }
                       });
                   }
               }
               // Garante que weapon seja objeto válido
               if (sub.weapon && typeof sub.weapon === 'string') {
                   // Tenta parsear como JSON primeiro
                   try {
                       sub.weapon = JSON.parse(sub.weapon);
                   } catch {
                       // Se falhar, tenta formato ~
                       const parts = sub.weapon.split('~');
                       if (parts.length >= 2) {
                           sub.weapon = {
                               icon: parts[0] || 'Nada.png',
                               powerpve: parts[1] || null,
                               powerpvp: parts[2] || null,
                               cooldown: parts[3] || null,
                               video: parts[4] || ,
                               energy: parts[5] || null
                           };
                           Object.keys(sub.weapon).forEach(k => {
                               if (sub.weapon[k] ===  || sub.weapon[k] === null) {
                                   delete sub.weapon[k];
                               }
                           });
                       } else {
                           sub.weapon = null;
                       }
                   }
               }
               return sub;
           });
           if (!Array.isArray(subs) || subs.length === 0) {
               subRail.classList.add('collapsed');
               subRail.classList.add('hidden');
               subBar.innerHTML = ;
               if (spacer) spacer.style.height = '0px';
               return;
           }
           // Busca mapa das skills principais para herança
           const mainSkills = getMainSkillsMap();
           // Aplica herança ANTES de processar
           subs = subs.map(sub => applyInheritance(sub, mainSkills));
           // Remove subskills que ficaram sem nome após herança
           subs = subs.filter(s => (s.name || s.n || ).trim() !== );
           subRail.classList.add('hidden');
           subBar.innerHTML = ;
           // Usa a ordem natural das subskills após herança
           let order = subs.map(s => s.name || s.n || );
           if (rawOrder.trim()) {
               try {
                   const preferred = JSON.parse(rawOrder);
                   if (Array.isArray(preferred) && preferred.length) {
                       const byName = new Map(subs.map(s => [(s.name || s.n || ), s]));
                       order = preferred.filter(n => byName.has(n));
                   }
               } catch { }
           }
           // Pré-carrega TODOS os ícones ANTES de renderizar
           const iconPreloadPromises = [];
           order.forEach(nm => {
               const s = subs.find(x => (x.name || x.n || ) === nm);
               if (s && s.icon && s.icon.trim() !==  && s.icon !== 'Nada.png') {
                   iconPreloadPromises.push(preloadImage(s.icon));
               }
           });
           // Pré-cria vídeos usando getOrCreateSubskillVideo (baseado em URL)
           // Isso garante que todos os vídeos estejam no cache antes de renderizar
           order.forEach(nm => {
               const s = subs.find(x => (x.name || x.n || ) === nm);
               if (s) {
                   // Pré-cria vídeo normal se existir
                   if (s.video && s.video.trim() !==  && s.video !== 'Nada.png' && !s.video.toLowerCase().includes('nada.png')) {
                       const video = getOrCreateSubskillVideo(s.video);
                       if (video && video.readyState < 2) {
                           video.load();
                       }
                   }
                   // Pré-cria vídeo de weapon se existir
                   if (s.weapon && typeof s.weapon === 'object' && s.weapon.video && s.weapon.video.trim() !==  && s.weapon.video !== 'Nada.png' && !s.weapon.video.toLowerCase().includes('nada.png')) {
                       const weaponVideo = getOrCreateSubskillVideo(s.weapon.video);
                       if (weaponVideo && weaponVideo.readyState < 2) {
                           weaponVideo.load();
                       }
                   }
               }
           });
           // Função para renderizar a barra de subskills
           const renderSubskillsBar = () => {
               order.forEach(nm => {
                   const s = subs.find(x => (x.name || x.n || ) === nm);
                   if (!s) {
                       return;
                   }
                   const item = document.createElement('div');
                   item.className = 'subicon';
                   item.title = s.name || nm;
                   const slugify = window.__skillSlugify || ((str) => (str || ).toLowerCase().replace(/[^\w]+/g, '-').replace(/^-+|-+$/g, ));
                   item.dataset.slug = slugify(s.name || nm);
                   logSubVideo('creating subicon element', {
                       subName: (s.name || s.n || ).trim(),
                       parentIdx,
                       className: item.className
                   });
                   const img = document.createElement('img');
                   img.alt = ;
                   img.src = filePathURL(s.icon || 'Nada.png');
                   item.appendChild(img);
                   // Verifica weapon de forma mais robusta
                   const hasWeapon = s.weapon && (
                       (typeof s.weapon === 'object' && Object.keys(s.weapon).length > 0) ||
                       (typeof s.weapon === 'string' && s.weapon.trim() !== )
                   );
                   const subName = (s.name || s.n || ).trim();
                   if (hasWeapon) {
                       // Normaliza weapon se for string
                       let weaponObj = s.weapon;
                       if (typeof weaponObj === 'string') {
                           try {
                               weaponObj = JSON.parse(weaponObj);
                           } catch {
                               // Se falhar, tenta formato ~
                               const parts = weaponObj.split('~');
                               if (parts.length >= 2) {
                                   weaponObj = {
                                       icon: parts[0] || 'Nada.png',
                                       powerpve: parts[1] || null,
                                       powerpvp: parts[2] || null,
                                       cooldown: parts[3] || null,
                                       video: parts[4] || ,
                                       energy: parts[5] || null
                                   };
                                   Object.keys(weaponObj).forEach(k => {
                                       if (weaponObj[k] ===  || weaponObj[k] === null) {
                                           delete weaponObj[k];
                                       }
                                   });
                               } else {
                                   weaponObj = null;
                               }
                           }
                       }
                       if (weaponObj && typeof weaponObj === 'object' && Object.keys(weaponObj).length > 0) {
                           try {
                               item.dataset.weapon = JSON.stringify(weaponObj);
                           } catch (e) {
                               console.error('[Subskills] Erro ao serializar weapon de subskill', subName, e);
                           }
                           // Aplica classe inicial se o toggle já está ativo
                           if (isWeaponModeOn()) {
                               item.classList.add('has-weapon-available');
                           }
                       }
                   }
                   // CORREÇÃO 4: Evento de clique otimizado para não recarregar vídeos
                   logSubVideo('attaching click handler to subicon', {
                       subName: (s.name || s.n || ).trim(),
                       parentIdx,
                       itemClassName: item.className
                   });
                   item.addEventListener('click', () => {
                       const L = getLabels();
                       const subName = (s.name || s.n || ).trim();
                       logSubVideo('subicon click HANDLER EXECUTED', {
                           subName,
                           parentIdx,
                           weaponMode: isWeaponModeOn(),
                           hasWeaponDataAttr: !!item.dataset.weapon,
                           rawWeaponDataset: item.dataset.weapon || null
                       });
                       // Lê weapon diretamente do atributo data-weapon
                       let subWeaponData = null;
                       if (item.dataset.weapon) {
                           try {
                               subWeaponData = JSON.parse(item.dataset.weapon);
                           } catch (e) {
                               subWeaponData = null;
                           }
                       }
                       const hasSubWeapon = !!subWeaponData;
                       const weaponOn = isWeaponModeOn();
                       const weaponEquipped = weaponOn && hasSubWeapon && subWeaponData;
                       logSubVideo('weapon status for subclick', {
                           subName,
                           weaponEquipped,
                           hasSubWeaponData: !!subWeaponData
                       });
                       // Determina descrição (weapon ou normal)
                       const raw = (document.documentElement.lang || 'pt').toLowerCase();
                       const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
                       let chosen = ;
                       if (weaponEquipped && subWeaponData) {
                           // Descrição do weapon
                           const weaponDescPack = subWeaponData.desc_i18n || subWeaponData.desc || {};
                           chosen = weaponDescPack[lang] || weaponDescPack.pt || weaponDescPack.en || ;
                       } else {
                           // Descrição normal
                           const base = s.desc_i18n || s.desc || {};
                           chosen = base[lang] || base.pt || base.en || (s.descPt || );
                       }
                       // Determina atributos (weapon ou normal)
                       let attrsObj = {
                           powerpve: s.powerpve,
                           powerpvp: s.powerpvp,
                           energy: s.energy,
                           cooldown: s.cooldown
                       };
                       if (weaponEquipped && subWeaponData) {
                           attrsObj = {
                               powerpve: subWeaponData.powerpve || s.powerpve,
                               powerpvp: subWeaponData.powerpvp || s.powerpvp,
                               energy: subWeaponData.energy || s.energy,
                               cooldown: subWeaponData.cooldown || s.cooldown
                           };
                       }
                       // Level (weapon ou normal)
                       const level = (weaponEquipped && subWeaponData && subWeaponData.level)
                           ? subWeaponData.level.toString().trim()
                           : (s.level || ).toString().trim();
                       let flagsHTML = ;
                       if (Array.isArray(s.flags) && s.flags.length > 0) {
                           flagsHTML = renderFlagsRow(s.flags);
                       }
                       if (descBox) {

descBox.innerHTML = `

${s.name || nm}

${level ? `

${L.level} ${level}

` : }${renderSubAttrs(attrsObj, L)}

${chosen.replace(/(.*?)/g, '$1')}

`;

                       }
                       if (videoBox) {
                           const oldFlags = videoBox.querySelector('.skill-flags');
                           if (oldFlags) oldFlags.remove();
                           if (flagsHTML) {
                               videoBox.insertAdjacentHTML('beforeend', flagsHTML);
                               applyFlagTooltips(videoBox);
                           }
                       }
                       // Vídeo (weapon ou normal) - usa cache já criado
                       const effectiveVideo = getEffectiveVideo(s);
                       logSubVideo('effectiveVideo for sub', {
                           subName,
                           parentIdx,
                           effectiveVideo,
                           weaponEquipped,
                           rawBaseVideo: s.video,
                           rawWeaponVideo: subWeaponData?.video
                       });
                       if (!effectiveVideo || effectiveVideo.trim() ===  || effectiveVideo === 'Nada.png' || effectiveVideo.toLowerCase().includes('nada.png')) {
                           if (videoBox) videoBox.style.display = 'none';
                       } else {
                           logSubVideo('calling showSubVideo from click handler', { effectiveVideo });
                           // Usa getOrCreateSubskillVideo internamente - nunca recria se já existe
                           showSubVideo(effectiveVideo, videoBox);
                       }
                       Array.from(subBar.children).forEach(c => {
                           c.classList.remove('active');
                           c.classList.remove('weapon-equipped');
                       });
                       item.classList.add('active');
                       // Aplica weapon-equipped se tem weapon e está ativo
                       if (weaponEquipped) {
                           item.classList.add('weapon-equipped');
                       }
                       window.__lastActiveSkillIcon = item;
                   });
                   if (window.__globalSkillTooltip) {
                       const { show, hide, measureAndPos, lockUntil } = window.__globalSkillTooltip;
                       const label = item.title || ;
                       item.setAttribute('aria-label', label);
                       if (item.hasAttribute('title')) item.removeAttribute('title');
                       item.addEventListener('mouseenter', () => show(item, label));
                       item.addEventListener('mousemove', () => { if (performance.now() >= lockUntil.value) measureAndPos(item); });
                       item.addEventListener('click', () => { lockUntil.value = performance.now() + 240; measureAndPos(item); });
                       item.addEventListener('mouseleave', hide);
                   }
                   subBar.appendChild(item);
                   logSubVideo('subicon appended to subBar', {
                       subName: (s.name || s.n || ).trim(),
                       parentIdx,
                       subBarChildrenCount: subBar.children.length
                   });
               });
               // Aplica classes de weapon nas subskills recém-renderizadas
               applyWeaponClassesToSubskills();
               // Dispara evento para notificar que subskills estão prontas
               window.dispatchEvent(new CustomEvent('gla:subskills:ready', { detail: { count: order.length } }));
               // Remove listener anterior se existir (evita duplicação)
               if (subBar._weaponToggleListener) {
                   window.removeEventListener('gla:weaponToggled', subBar._weaponToggleListener);
               }
               // Cria novo listener que opera no subBar atual
               subBar._weaponToggleListener = (e) => {
                   const enabled = e.detail?.enabled ?? false;
                   // Aplica classes usando a função auxiliar
                   applyWeaponClassesToSubskills();
                   // Atualiza a subskill ativa se houver
                   setTimeout(() => {
                       const activeSub = subBar.querySelector('.subicon[data-weapon].active')
                           || subBar.querySelector('.subicon.active');
                       if (activeSub) {
                           activeSub.dispatchEvent(new Event('click', { bubbles: true }));
                       } else {
                           api.refreshCurrentSubSafe();
                       }
                   }, 50);
               };
               window.addEventListener('gla:weaponToggled', subBar._weaponToggleListener);
               requestAnimationFrame(() => {
                   subRail.classList.remove('collapsed');
                   subRail.classList.remove('hidden');
                   const h = subRail.offsetHeight || 48;
                   if (spacer) spacer.style.height = h + 'px';
               });
           }, 10);
       };
       api.hideAll = function (videoBox) {
           const videos = videoBox?.querySelectorAll('.skill-video[data-sub="1"]') || [];
           logSubVideo('api.hideAll called', { videoBoxExists: !!videoBox, videoCount: videos.length });
           videos.forEach(v => {
               try { v.pause(); } catch { }
               v.style.display = 'none';
           });
       };
       window.renderSubskillsBarFrom = function (el, ctx) { api.renderBarFrom(el, ctx); };
       api.preloadAllSubskillImages = function () {
           const allSkillIcons = document.querySelectorAll('.icon-bar .skill-icon[data-subs]');
           const preloadPromises = [];
           let totalImages = 0;
           allSkillIcons.forEach(icon => {
               try {
                   const subsRaw = icon.getAttribute('data-subs');
                   if (!subsRaw) return;
                   const subs = JSON.parse(subsRaw);
                   if (!Array.isArray(subs)) return;
                   subs.forEach(s => {
                       if (s && s.icon && s.icon.trim() !==  && s.icon !== 'Nada.png') {
                           preloadPromises.push(preloadImage(s.icon));
                           totalImages++;
                       }
                       if (s && Array.isArray(s.subs)) {
                           s.subs.forEach(nested => {
                               if (nested && nested.icon && nested.icon.trim() !==  && nested.icon !== 'Nada.png') {
                                   preloadPromises.push(preloadImage(nested.icon));
                                   totalImages++;
                               }
                           });
                       }
                   });
               } catch (e) {
                   console.error('[Subskills] preloadAllSubskillImages error:', e);
               }
           });
           if (totalImages > 0) {
               console.log('[Subskills] preloadAllSubskillImages: pré-carregando', totalImages, 'ícones');
               return Promise.all(preloadPromises).then(() => {
                   console.log('[Subskills] preloadAllSubskillImages: todos os ícones carregados');
               });
           }
           return Promise.resolve();
       };
       // Função para pré-carregar vídeos de subskills usando getOrCreateSubskillVideo
       api.preloadAllSubskillVideos = function () {
           const videoBox = document.querySelector('.video-container');
           if (!videoBox) {
               logSubVideo('preloadAllSubskillVideos: videoBox not found, skipping');
               return;
           }
           const allSkillIcons = document.querySelectorAll('.icon-bar .skill-icon[data-subs]');
           let createdCount = 0;
           logSubVideo('preloadAllSubskillVideos: starting', { iconCount: allSkillIcons.length });
           allSkillIcons.forEach(icon => {
               try {
                   const subsRaw = icon.getAttribute('data-subs');
                   if (!subsRaw) return;
                   const subs = JSON.parse(subsRaw);
                   if (!Array.isArray(subs)) return;
                   subs.forEach(s => {
                       // Pré-cria vídeo normal usando getOrCreateSubskillVideo
                       if (s.video && s.video.trim() !==  && s.video !== 'Nada.png' && !s.video.toLowerCase().includes('nada.png')) {
                           const video = getOrCreateSubskillVideo(s.video);
                           if (video) {
                               if (!video.isConnected) {
                                   videoBox.appendChild(video);
                                   createdCount++;
                               }
                               // FORÇA carregamento imediato
                               if (video.readyState < 2) {
                                   video.muted = true; // Permite autoload em mais navegadores
                                   video.load();
                               }
                           }
                       }
                       // Pré-cria vídeo de weapon usando getOrCreateSubskillVideo
                       if (s.weapon && typeof s.weapon === 'object' && s.weapon.video && s.weapon.video.trim() !==  && s.weapon.video !== 'Nada.png' && !s.weapon.video.toLowerCase().includes('nada.png')) {
                           const weaponVideo = getOrCreateSubskillVideo(s.weapon.video);
                           if (weaponVideo) {
                               if (!weaponVideo.isConnected) {
                                   videoBox.appendChild(weaponVideo);
                                   createdCount++;
                               }
                               // FORÇA carregamento imediato
                               if (weaponVideo.readyState < 2) {
                                   weaponVideo.muted = true; // Permite autoload em mais navegadores
                                   weaponVideo.load();
                               }
                           }
                       }
                   });
               } catch (e) {
                   console.error('[Subskills] preloadAllSubskillVideos error:', e);
               }
           });
           logSubVideo('preloadAllSubskillVideos: completed', { createdCount, cacheSize: subskillVideoElementCache.size });
           // Força carregamento novamente após um delay para garantir
           setTimeout(() => {
               subskillVideoElementCache.forEach(video => {
                   if (video.readyState < 2) {
                       video.muted = true;
                       video.load();
                   }
               });
           }, 100);
       };
       // Função syncVideoCaches removida - não é mais necessária com cache baseado em URL
       // Inicialização
       function init() {
           logSubVideo('init: starting initialization');
           // Constrói cache das skills principais
           getMainSkillsMap();
           // Pré-carrega imagens das subskills IMEDIATAMENTE
           api.preloadAllSubskillImages();
           // Pré-carrega vídeos das subskills usando getOrCreateSubskillVideo IMEDIATAMENTE
           api.preloadAllSubskillVideos();
           // Força carregamento novamente após delays para garantir
           setTimeout(() => {
               api.preloadAllSubskillVideos();
           }, 100);
           setTimeout(() => {
               // Força load em todos os vídeos que ainda não carregaram
               subskillVideoElementCache.forEach(video => {
                   if (video.readyState < 2) {
                       video.muted = true;
                       video.load();
                   }
               });
           }, 500);
           // Escuta mudanças no localStorage
           window.addEventListener('storage', (e) => {
               if (e.key === 'glaWeaponEnabled') {
                   setTimeout(() => api.refreshCurrentSubSafe(), 50);
               }
           });
           // LISTENER GLOBAL: Escuta evento de toggle
           window.addEventListener('gla:weaponToggled', (e) => {
               const enabled = e.detail?.enabled ?? false;
               // Tenta aplicar classes imediatamente
               applyWeaponClassesToSubskills();
               // Atualiza a subskill ativa se houver
               setTimeout(() => {
                   const activeSub = document.querySelector('.subicon[data-weapon].active')
                       || document.querySelector('.subicon.active');
                   if (activeSub) {
                       activeSub.dispatchEvent(new Event('click', { bubbles: true }));
                   } else {
                       api.refreshCurrentSubSafe();
                   }
               }, 50);
           });
           // LISTENER: Escuta quando subskills estão prontas
           window.addEventListener('gla:subskills:ready', (e) => {
               // Aplica classes de weapon se o toggle estiver ativo
               applyWeaponClassesToSubskills();
           });
       }
       if (document.readyState === 'loading') {
           document.addEventListener('DOMContentLoaded', () => {
               setTimeout(init, 100);
           });
       } else {
           setTimeout(init, 100);
       }
   })();

</script> <style>

   .subicon-bar {
       display: flex;
       gap: 10px;
       padding: 6px 6px;
       overflow-x: auto;
       /* Firefox */
       scrollbar-width: thin;
       scrollbar-color: #ababab transparent;
   }
   .subicon-bar::-webkit-scrollbar {
       height: 6px;
   }
   .subicon-bar::-webkit-scrollbar-thumb {
       background: #151515;
       border-radius: 3px;
   }
   .subicon {
       width: var(--icon-size, 42px);
       height: var(--icon-size, 42px);
       border-radius: var(--icon-radius, 10px);
       overflow: hidden;
       position: relative;
       flex: 0 0 auto;
       cursor: pointer;
       isolation: isolate;
   }
   .subicon img {
       width: 100%;
       height: 100%;
       object-fit: cover;
       display: block;
       border-radius: inherit;
   }
   .subicon::after {
       content: "";
       position: absolute;
       inset: 0;
       border-radius: inherit;
       box-shadow: inset 0 0 0 var(--icon-ring-w, 2px) var(--icon-idle, #cfcfcf);
       pointer-events: none;
       z-index: 2;
       transition: box-shadow .12s ease;
   }
   .subicon:hover::after {
       box-shadow: inset 0 0 0 var(--icon-ring-w, 2px) #e6e6e6;
   }
   .subicon.active::after {
       box-shadow: inset 0 0 0 var(--icon-ring-w, 2px) var(--icon-active, #FFD95A);
   }
   .subicon.active::before {
       content: "";
       position: absolute;
       inset: -4px;
       border-radius: calc(var(--icon-radius, 10px) + 4px);
       pointer-events: none;
       z-index: 1;
       opacity: 1;
       box-shadow: 0 0 12px 3px var(--icon-active-glow, rgba(255, 217, 90, .30)),
           0 0 0 calc(var(--icon-ring-w, 2px) * 2) var(--icon-active-ring, rgba(255, 217, 90, .50));
   }
   .top-rail.skills {
       position: relative;
       display: flex;
       flex-direction: column;
       align-items: center;
       overflow: visible;
   }
   .top-rail.skills .icon-bar {
       margin-bottom: 0;
       position: relative;
       z-index: 2;
   }
   .subskills-rail {
       position: absolute;
       left: 50%;
       transform: translateX(-50%);
       top: calc(100% - 1px);
       z-index: 3;
       display: inline-flex;
       justify-content: center;
       align-items: center;
       width: auto;
       max-width: 100%;
       padding: 3px 5px;
       background: rgba(0, 0, 0, .38);
       border: 1px solid rgba(255, 255, 255, .10);
       border-top: 1px solid rgba(255, 255, 255, .08);
       border-radius: 0 0 10px 10px;
       box-shadow: 0 3px 9px rgba(0, 0, 0, .22);
       -webkit-backdrop-filter: blur(2px);
       backdrop-filter: blur(2px);
       overflow: hidden;
       transition: opacity .14s ease, transform .14s ease;
       opacity: 1;
   }
   .subskills-rail::before {
       content: "";
       position: absolute;
       top: -6px;
       left: 0;
       right: 0;
       height: 6px;
       background: linear-gradient(to bottom, rgba(0, 0, 0, .20), rgba(0, 0, 0, 0));
       pointer-events: none;
   }
   .subskills-rail.collapsed {
       opacity: 0;
       pointer-events: none;
       transform: translate(-50%, -6px);
   }
   .subskills-rail.hidden {
       visibility: hidden;
   }
   .subskills-spacer {
       height: 0;
       transition: height .2s ease;
   }
   .subskills-rail .subicon-bar {
       display: inline-flex;
       align-items: center;
       gap: 0;
       overflow-x: auto;
       /* Firefox */
       scrollbar-width: thin;
       scrollbar-color: #ababab transparent;
   }
   .subskills-rail .subicon-bar::-webkit-scrollbar {
       height: 6px;
   }
   .subskills-rail .subicon-bar::-webkit-scrollbar-thumb {
       background: #151515;
       border-radius: 3px;
   }
   .subskills-rail .subicon {
       width: 42px;
       height: 42px;
       border-radius: 6px;
       position: relative;
       overflow: hidden;
       flex: 0 0 auto;
       cursor: pointer;
       isolation: isolate;
       -webkit-backface-visibility: hidden;
       backface-visibility: hidden;
       transform: translateZ(0);
   }
   .subskills-rail .subicon+.subicon {
       margin-left: 4px;
   }
   .subskills-rail .subicon img {
       width: 100%;
       height: 100%;
       object-fit: cover;
       display: block;
       border-radius: inherit;
   }
   .subskills-rail .subicon::after {
       content: "";
       position: absolute;
       inset: 0;
       border-radius: inherit;
       box-shadow: inset 0 0 0 2px var(--icon-idle, #cfcfcf);
       pointer-events: none;
       z-index: 2;
       transition: box-shadow .12s ease;
   }
   .subskills-rail .subicon:hover::after {
       box-shadow: inset 0 0 0 2px #e6e6e6;
   }
   .subskills-rail .subicon.active::after {
       box-shadow: inset 0 0 0 2px var(--icon-active, #FFD95A);
   }
   .video-container .skill-video {
       width: 100%;
       height: auto;
       aspect-ratio: 16 / 9;
       object-fit: cover;
       background: #000;
       border-radius: 10px;
   }
   @media (max-width: 900px) {
       .subskills-rail {
           position: static;
           transform: none;
           margin-top: -2px;
           border-top: 0;
           border-radius: 0 0 10px 10px;
       }
       .subskills-spacer {
           height: 0 !important;
       }
   }
   .skills-rail-wrap {
       position: relative;
       display: block;
       width: max-content;
       margin: 0 auto;
   }
   /* Subskills com arma disponível - borda vermelha quando inativa */
   .character-box .top-rail.skills .subicon.has-weapon-available:not(.active)::after {
       box-shadow: inset 0 0 0 2px rgba(220, 70, 70, 0.85) !important;
   }
   /* Subskill com arma ATIVA - laranja/coral vibrante + brilho forte */
   .character-box .top-rail.skills .subicon.has-weapon-available.active::after {
       box-shadow: inset 0 0 0 2px #FF7043 !important;
   }
   .character-box .top-rail.skills .subicon.has-weapon-available.active::before {
       box-shadow:
           0 0 12px 4px rgba(255, 112, 67, 0.6),
           0 0 24px 8px rgba(255, 87, 34, 0.35),
           0 0 0 3px rgba(255, 112, 67, 0.4) !important;
       opacity: 1 !important;
   }
   /* Modo arma ON - efeito animado nos subicons */
   .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available {
       position: relative;
   }
   .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available::after {
       box-shadow: none !important;
       background: linear-gradient(90deg,
               rgba(255, 80, 80, 0.9) 0%,
               rgba(255, 120, 60, 1) 25%,
               rgba(255, 80, 80, 0.9) 50%,
               rgba(255, 120, 60, 1) 75%,
               rgba(255, 80, 80, 0.9) 100%) !important;
       background-size: 400% 100% !important;
       animation: weapon-subicon-border-scan 4s linear infinite !important;
       -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) !important;
       mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) !important;
       -webkit-mask-composite: xor !important;
       mask-composite: exclude !important;
       padding: 2px !important;
   }
   .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available::before {
       content: "";
       position: absolute;
       inset: -4px;
       border-radius: calc(6px + 4px);
       pointer-events: none;
       z-index: 1;
       box-shadow:
           0 0 10px 3px rgba(255, 80, 80, 0.5),
           0 0 20px 6px rgba(255, 80, 80, 0.25) !important;
       opacity: 1 !important;
       animation: weapon-subicon-pulse 3s ease-in-out infinite !important;
   }
   /* Subskill ativa com arma - mais intenso */
   .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available.active::after {
       background: linear-gradient(90deg,
               rgba(255, 87, 34, 1) 0%,
               rgba(255, 140, 60, 1) 25%,
               rgba(255, 87, 34, 1) 50%,
               rgba(255, 140, 60, 1) 75%,
               rgba(255, 87, 34, 1) 100%) !important;
   }
   .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available.active::before {
       box-shadow:
           0 0 14px 5px rgba(255, 87, 34, 0.65),
           0 0 28px 10px rgba(255, 87, 34, 0.35),
           0 0 0 3px rgba(255, 87, 34, 0.45) !important;
       animation: weapon-subicon-pulse-active 2.5s ease-in-out infinite !important;
   }
   @keyframes weapon-subicon-border-scan {
       0% {
           background-position: 0% 0%;
       }
       100% {
           background-position: 400% 0%;
       }
   }
   @keyframes weapon-subicon-pulse {
       0%,
       100% {
           opacity: 0.8;
           box-shadow: 0 0 8px 2px rgba(255, 80, 80, 0.45), 0 0 16px 4px rgba(255, 80, 80, 0.2);
       }
       50% {
           opacity: 1;
           box-shadow: 0 0 12px 4px rgba(255, 80, 80, 0.6), 0 0 24px 8px rgba(255, 80, 80, 0.3);
       }
   }
   @keyframes weapon-subicon-pulse-active {
       0%,
       100% {
           opacity: 0.85;
           box-shadow: 0 0 10px 4px rgba(255, 87, 34, 0.55), 0 0 22px 8px rgba(255, 87, 34, 0.3), 0 0 0 2px rgba(255, 87, 34, 0.35);
       }
       50% {
           opacity: 1;
           box-shadow: 0 0 16px 6px rgba(255, 87, 34, 0.7), 0 0 32px 12px rgba(255, 87, 34, 0.4), 0 0 0 3px rgba(255, 87, 34, 0.5);
       }
   }

</style>