MediaWiki:Common.js: Difference between revisions

From TimeRO Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
/* Any JavaScript here will be loaded for all users on every page load. */
/* TimeRO MediaWiki:Common.js
(function ($, mw) {
  Fixes Card Database and Beginner Leveling Guide loaders.
'use strict';
  ES5-safe: no const, let, arrow functions, template literals or nullish coalescing. */
 
/* =========================================================
  GLOBAL BASICS
========================================================= */
 
function setLogoHref() {
var logoLink = document.querySelector('#p-logo a');
if (logoLink) {
logoLink.setAttribute('href', 'https://timero.com.br/');
}
}
 
function ensureFontAwesome() {
if (document.querySelector('link[data-timero-fa]')) return;
 
var fa = document.createElement('link');
fa.rel = 'stylesheet';
fa.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css';
fa.setAttribute('data-timero-fa', '1');
document.head.appendChild(fa);
}
 
function bindActionButton(el, handler) {
if (!el || el.dataset.timeroBound === '1') return;
 
el.dataset.timeroBound = '1';
el.setAttribute('role', 'button');
el.setAttribute('tabindex', '0');
 
el.addEventListener('click', function (e) {
e.preventDefault();
handler.call(this, e);
});
 
el.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handler.call(this, e);
}
});
}
 
function toNumber(value, fallback) {
var n = parseFloat(value);
return isNaN(n) ? fallback : n;
}
 
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
 
/* =========================================================
  GUIDE PAGE SKIN SWITCH
========================================================= */
 
function applyTimeroGuideSkin() {
var page = mw.config.get('wgPageName') || '';
var html = document.documentElement;
var body = document.body;
var hasGuideRoot =
document.getElementById('farming-guide-root') ||
document.getElementById('mvp-guide-root') ||
document.getElementById('leveling-guide-root') ||
document.querySelector('.timero-wiki-root');
 
if (!html || !body) return;
 
Array.prototype.slice.call(html.classList).forEach(function (cls) {
if (cls === 'timero-guide-skin' || cls.indexOf('guide-page-') === 0) {
html.classList.remove(cls);
}
});
 
Array.prototype.slice.call(body.classList).forEach(function (cls) {
if (cls === 'timero-guide-skin' || cls.indexOf('guide-page-') === 0) {
body.classList.remove(cls);
}
});
 
if (/^Guia_de_/.test(page) || hasGuideRoot) {
var safePageClass = 'guide-page-' + String(page || 'custom-guide').replace(/[^A-Za-z0-9_-]/g, '-');
html.classList.add('timero-guide-skin');
body.classList.add('timero-guide-skin');
html.classList.add(safePageClass);
body.classList.add(safePageClass);
}
}
 
/* =========================================================
  ROUTES
========================================================= */
 
function resetRoutePill(pill) {
if (!pill) return;
pill.style.transform = '';
pill.style.fontWeight = '800';
pill.style.boxShadow = 'none';
pill.style.filter = 'none';
}
 
function activateRoutePill(pill) {
if (!pill) return;
pill.style.transform = 'translateY(-1px)';
pill.style.fontWeight = '900';
pill.style.boxShadow = '0 0 0 1px rgba(255,255,255,0.05) inset, 0 0 22px rgba(255,255,255,0.05)';
pill.style.filter = 'brightness(1.08)';
}
 
function closeAllRoutes(section) {
if (!section) return;
 
var details = section.querySelectorAll('.route-detail');
var pills = section.querySelectorAll('.route-pill[data-route]');
 
for (var i = 0; i < details.length; i++) {
details[i].style.display = 'none';
}
 
for (var j = 0; j < pills.length; j++) {
resetRoutePill(pills[j]);
}
 
section.setAttribute('data-open-route', '');
}
 
window.expandRoute = function (routeId) {
var section = document.getElementById('routes-section');
if (!section) return;
 
var target = document.getElementById(routeId);
if (!target) return;
 
var currentOpen = section.getAttribute('data-open-route');
 
if (currentOpen === routeId) {
closeAllRoutes(section);
return;
}
 
closeAllRoutes(section);
 
target.style.display = 'block';
target.style.animation = 'route-in 0.3s ease both';
section.setAttribute('data-open-route', routeId);
 
var activePill = document.getElementById('pill-' + routeId);
activateRoutePill(activePill);
 
if (typeof target.scrollIntoView === 'function') {
setTimeout(function () {
target.scrollIntoView({
behavior: 'smooth',
block: 'nearest'
});
}, 80);
}
};
 
function bindRouteViewer(context) {
var scope = context && context.querySelector ? context : document;
var section = scope.querySelector('#routes-section') || document.getElementById('routes-section');
if (!section) return;
 
var pills = section.querySelectorAll('.route-pill[data-route]');
for (var i = 0; i < pills.length; i++) {
(function (pill) {
bindActionButton(pill, function () {
expandRoute(this.getAttribute('data-route'));
});
})(pills[i]);
}
 
var closeButtons = section.querySelectorAll('.route-close[data-route]');
for (var j = 0; j < closeButtons.length; j++) {
(function (closeBtn) {
bindActionButton(closeBtn, function () {
expandRoute(this.getAttribute('data-route'));
});
})(closeButtons[j]);
}
}
 
/* =========================================================
  METHODS
========================================================= */
 
function resetMethodTab(tab, type) {
if (!tab) return;
 
tab.style.background = 'transparent';
tab.style.fontWeight = '700';
 
if (type === 'grind') tab.style.color = 'rgba(249,197,0,0.60)';
if (type === 'market') tab.style.color = 'rgba(0,212,255,0.60)';
if (type === 'passive') tab.style.color = 'rgba(176,108,255,0.60)';
}
 
function activateMethodTab(tab, type) {
if (!tab) return;
 
tab.style.fontWeight = '900';
 
if (type === 'grind') {
tab.style.background = 'linear-gradient(135deg,rgba(249,197,0,0.18),rgba(249,197,0,0.07))';
tab.style.color = '#f9c500';
}
 
if (type === 'market') {
tab.style.background = 'linear-gradient(135deg,rgba(0,212,255,0.14),rgba(0,212,255,0.05))';
tab.style.color = '#00d4ff';
}
 
if (type === 'passive') {
tab.style.background = 'linear-gradient(135deg,rgba(176,108,255,0.14),rgba(176,108,255,0.05))';
tab.style.color = '#b06cff';
}
}
 
window.switchMethod = function (methodId) {
var section = document.getElementById('methods-section');
if (!section) return;
 
var panels = section.querySelectorAll('.method-panel');
var tabs = section.querySelectorAll('.method-tab[data-method]');
 
for (var i = 0; i < panels.length; i++) {
panels[i].style.display = 'none';
}
 
for (var j = 0; j < tabs.length; j++) {
resetMethodTab(tabs[j], tabs[j].getAttribute('data-method'));
}
 
var activePanel = document.getElementById('method-' + methodId);
if (activePanel) {
activePanel.style.display = 'grid';
activePanel.style.animation = 'method-in 0.3s ease both';
}
 
var activeTab = document.getElementById('mtab-' + methodId);
activateMethodTab(activeTab, methodId);
 
section.setAttribute('data-open-method', methodId);
};
 
function bindMethodViewer(context) {
var scope = context && context.querySelector ? context : document;
var section = scope.querySelector('#methods-section') || document.getElementById('methods-section');
if (!section) return;
 
var tabs = section.querySelectorAll('.method-tab[data-method]');
for (var i = 0; i < tabs.length; i++) {
(function (tab) {
bindActionButton(tab, function () {
switchMethod(this.getAttribute('data-method'));
});
})(tabs[i]);
}
 
if (!section.getAttribute('data-open-method')) {
switchMethod('grind');
} else {
switchMethod(section.getAttribute('data-open-method'));
}
}
 
/* =========================================================
  CALCULATOR
========================================================= */
 
var calcState = {
hours: 2,
booster: false,
rate: 70,
label: 'Rota Mid-Game (70M/hr)',
weeklyTarget: 12000
};
 
function formatMillions(value) {
var rounded = Math.round(value * 10) / 10;
 
if (rounded >= 1000) {
var billions = rounded / 1000;
var bText = (Math.round(billions * 10) / 10).toString().replace('.0', '').replace('.', ',');
return bText + 'B z';
}
 
if (rounded >= 1) {
return rounded.toString().replace('.0', '').replace('.', ',') + 'M z';
}
 
return Math.round(rounded * 1000) + 'k z';
}
 
function refreshCalcMethodUI(section) {
if (!section) return;
 
var labelEl = section.querySelector('#calc-method-label');
var menuEl = section.querySelector('#calc-method-menu');
var options = section.querySelectorAll('.calc-method-option');
 
if (labelEl) {
labelEl.textContent = calcState.label;
}
 
for (var i = 0; i < options.length; i++) {
var opt = options[i];
var isActive = String(opt.getAttribute('data-rate')) === String(calcState.rate);
 
if (isActive) {
opt.style.background = 'rgba(0,212,255,0.12)';
opt.style.borderColor = 'rgba(0,212,255,0.25)';
opt.style.color = '#00d4ff';
opt.style.fontWeight = '800';
} else {
opt.style.background = 'rgba(255,255,255,0.03)';
opt.style.borderColor = 'rgba(255,255,255,0.06)';
opt.style.color = 'rgba(232,238,248,0.82)';
opt.style.fontWeight = '700';
}
}
 
if (menuEl) {
menuEl.style.display = section.getAttribute('data-method-menu-open') === '1' ? 'block' : 'none';
}
}
 
function syncCalcStateFromNativeSelect(section) {
if (!section) return;
 
var select = section.querySelector('#calc-method');
if (!select) return;
 
calcState.rate = toNumber(select.value, calcState.rate);
 
if (select.options && select.selectedIndex >= 0) {
calcState.label = select.options[select.selectedIndex].text;
}
}
 
window.adjustCalc = function (field, delta) {
if (field !== 'hours') return;
 
calcState.hours = clamp(calcState.hours + delta, 0.5, 24);
updateCalc();
};
 
window.setBooster = function (enabled) {
calcState.booster = !!enabled;
updateCalc();
};
 
window.setCalcMethod = function (rate, label) {
calcState.rate = toNumber(rate, calcState.rate);
calcState.label = label || calcState.label;
 
var section = document.getElementById('calculator-section');
if (section) {
section.setAttribute('data-method-menu-open', '0');
 
var select = section.querySelector('#calc-method');
if (select) {
select.value = String(calcState.rate);
}
}
 
updateCalc();
};
 
window.toggleCalcMethodMenu = function () {
var section = document.getElementById('calculator-section');
if (!section) return;
 
var isOpen = section.getAttribute('data-method-menu-open') === '1';
section.setAttribute('data-method-menu-open', isOpen ? '0' : '1');
refreshCalcMethodUI(section);
};
 
window.closeCalcMethodMenu = function () {
var section = document.getElementById('calculator-section');
if (!section) return;
 
section.setAttribute('data-method-menu-open', '0');
refreshCalcMethodUI(section);
};
 
window.updateCalc = function () {
var section = document.getElementById('calculator-section');
if (!section) return;
 
syncCalcStateFromNativeSelect(section);
 
var hoursEl = document.getElementById('calc-hours');
var resultSession = document.getElementById('result-session');
var resultDay = document.getElementById('result-day');
var resultWeek = document.getElementById('result-week');
var resultPct = document.getElementById('result-pct');
var resultBar = document.getElementById('result-bar');
var boostNo = document.getElementById('boost-no');
var boostYes = document.getElementById('boost-yes');
 
var effectiveRate = calcState.booster ? calcState.rate * 1.5 : calcState.rate;
var session = calcState.hours * effectiveRate;
var day = session;
var week = day * 7;
var pct = Math.max(0, Math.min(100, Math.round((week / calcState.weeklyTarget) * 100)));
 
if (hoursEl) {
hoursEl.textContent = calcState.hours % 1 === 0 ? String(calcState.hours) : String(calcState.hours).replace('.', ',');
}
if (resultSession) resultSession.textContent = formatMillions(session);
if (resultDay) resultDay.textContent = formatMillions(day);
if (resultWeek) resultWeek.textContent = formatMillions(week);
if (resultPct) resultPct.textContent = pct + '%';
if (resultBar) resultBar.style.width = pct + '%';
 
if (boostNo && boostYes) {
if (calcState.booster) {
boostNo.style.background = 'rgba(255,255,255,0.04)';
boostNo.style.borderColor = 'rgba(255,255,255,0.09)';
boostNo.style.color = 'rgba(122,144,176,0.60)';
boostNo.style.fontWeight = '700';
 
boostYes.style.background = 'rgba(0,212,255,0.15)';
boostYes.style.borderColor = 'rgba(0,212,255,0.30)';
boostYes.style.color = '#00d4ff';
boostYes.style.fontWeight = '800';
} else {
boostNo.style.background = 'rgba(255,61,90,0.15)';
boostNo.style.borderColor = 'rgba(255,61,90,0.30)';
boostNo.style.color = '#ff3d5a';
boostNo.style.fontWeight = '800';
 
boostYes.style.background = 'rgba(255,255,255,0.04)';
boostYes.style.borderColor = 'rgba(255,255,255,0.09)';
boostYes.style.color = 'rgba(122,144,176,0.60)';
boostYes.style.fontWeight = '700';
}
}
 
refreshCalcMethodUI(section);
};
 
function bindCalculator(context) {
var scope = context && context.querySelector ? context : document;
var section = scope.querySelector('#calculator-section') || document.getElementById('calculator-section');
if (!section) return;
 
var adjusters = section.querySelectorAll('[data-calc-adjust]');
for (var i = 0; i < adjusters.length; i++) {
(function (adjuster) {
bindActionButton(adjuster, function (e) {
e.stopPropagation();
adjustCalc(this.getAttribute('data-calc-adjust'), toNumber(this.getAttribute('data-delta'), 0));
});
})(adjusters[i]);
}
 
var boosters = section.querySelectorAll('[data-booster]');
for (var j = 0; j < boosters.length; j++) {
(function (booster) {
bindActionButton(booster, function (e) {
e.stopPropagation();
setBooster(this.getAttribute('data-booster') === '1');
});
})(boosters[j]);
}
 
var methodDisplay = section.querySelector('#calc-method-display');
if (methodDisplay) {
bindActionButton(methodDisplay, function (e) {
e.stopPropagation();
toggleCalcMethodMenu();
});
}
 
var options = section.querySelectorAll('.calc-method-option');
for (var k = 0; k < options.length; k++) {
(function (option) {
bindActionButton(option, function (e) {
e.stopPropagation();
setCalcMethod(this.getAttribute('data-rate'), this.getAttribute('data-label'));
});
})(options[k]);
}
 
var nativeSelect = section.querySelector('#calc-method');
if (nativeSelect && nativeSelect.dataset.timeroBound !== '1') {
nativeSelect.dataset.timeroBound = '1';
nativeSelect.addEventListener('change', function () {
updateCalc();
});
}
 
if (!window.__timeroCalcDocBound) {
window.__timeroCalcDocBound = true;
 
document.addEventListener('click', function (e) {
var liveSection = document.getElementById('calculator-section');
if (!liveSection) return;
 
if (!liveSection.contains(e.target)) {
closeCalcMethodMenu();
}
});
}
 
if (!section.getAttribute('data-method-menu-open')) {
section.setAttribute('data-method-menu-open', '0');
}
 
updateCalc();
}
 
/* =========================================================
  LOADOUT
========================================================= */
 
function setLoadoutItemState(item, active) {
if (!item) return;
 
var check = item.querySelector('.li-check');
if (!check) return;
 
if (active) {
item.classList.add('is-active');
item.style.background = 'rgba(176,108,255,0.08)';
item.style.borderColor = 'rgba(176,108,255,0.24)';
item.style.boxShadow = '0 0 0 1px rgba(176,108,255,0.05) inset';
 
check.style.background = 'rgba(176,108,255,0.16)';
check.style.borderColor = 'rgba(176,108,255,0.45)';
check.style.color = '#b06cff';
check.innerHTML = '✓';
} else {
item.classList.remove('is-active');
item.style.background = 'rgba(255,255,255,0.03)';
item.style.borderColor = 'rgba(255,255,255,0.07)';
item.style.boxShadow = 'none';
 
check.style.background = 'rgba(176,108,255,0.04)';
check.style.borderColor = 'rgba(176,108,255,0.30)';
check.style.color = 'transparent';
check.innerHTML = '';
}
}
 
function updateLoadoutSummary() {
var section = document.getElementById('loadout-section');
if (!section) return;
 
var items = section.querySelectorAll('.loadout-item');
var total = 0;
var count = 0;
 
for (var i = 0; i < items.length; i++) {
if (items[i].classList.contains('is-active')) {
total += toNumber(items[i].getAttribute('data-cost'), 0);
count += 1;
}
}
 
var totalEl = document.getElementById('loadout-total');
var countEl = document.getElementById('loadout-count');
 
if (totalEl) totalEl.textContent = total + 'k z';
if (countEl) countEl.textContent = String(count);
}
 
window.toggleLoadout = function (element) {
if (!element) return;
 
var active = !element.classList.contains('is-active');
setLoadoutItemState(element, active);
updateLoadoutSummary();
};
 
window.clearLoadout = function () {
var section = document.getElementById('loadout-section');
if (!section) return;
 
var items = section.querySelectorAll('.loadout-item');
for (var i = 0; i < items.length; i++) {
setLoadoutItemState(items[i], false);
}
 
updateLoadoutSummary();
};
 
function bindLoadout(context) {
var scope = context && context.querySelector ? context : document;
var section = scope.querySelector('#loadout-section') || document.getElementById('loadout-section');
if (!section) return;
 
var items = section.querySelectorAll('.loadout-item');
for (var i = 0; i < items.length; i++) {
(function (item) {
setLoadoutItemState(item, item.classList.contains('is-active'));
 
bindActionButton(item, function () {
toggleLoadout(this);
});
})(items[i]);
}
 
var clearBtn = section.querySelector('.loadout-clear');
if (clearBtn) {
bindActionButton(clearBtn, function () {
clearLoadout();
});
}
 
updateLoadoutSummary();
}
 
/* =========================================================
  TIMERO CLICKABLE CARDS
========================================================= */
 
function bindTimeroNavCards(context) {
var scope = context && context.querySelector ? context : document;
var cards = scope.querySelectorAll('.timero-nav-card[data-href]');
 
for (var i = 0; i < cards.length; i++) {
(function (card) {
if (card.dataset.timeroNavBound === '1') return;
 
card.dataset.timeroNavBound = '1';
card.setAttribute('role', 'button');
card.setAttribute('tabindex', '0');
card.style.cursor = 'pointer';
 
card.addEventListener('click', function (e) {
if (e.target.closest('a, button, input, select, textarea')) return;
 
var href = this.getAttribute('data-href');
var target = this.getAttribute('data-target') || '';
 
if (!href) return;
 
if (target === '_blank') {
window.open(href, '_blank', 'noopener');
} else {
window.location.href = href;
}
});
 
card.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this.click();
}
});
})(cards[i]);
}
}
 
/* =========================================================
  MVP GUIDE
========================================================= */
 
var MVP_TIMER_STORAGE_KEY = 'timero_mvp_timers_v1';
 
function readStoredMvpTimers() {
try {
var raw = window.localStorage.getItem(MVP_TIMER_STORAGE_KEY);
if (!raw) return [];
var parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed : [];
} catch (err) {
return [];
}
}
 
function writeStoredMvpTimers(list) {
try {
window.localStorage.setItem(MVP_TIMER_STORAGE_KEY, JSON.stringify(list));
} catch (err) {
/* ignore */
}
}
 
function getRemainingTimerMs(timerObj) {
var now = Date.now();
var endAt = timerObj.killTime + timerObj.respawnMs;
return Math.max(0, endAt - now);
}
 
function formatTimerMs(ms) {
var totalSec = Math.max(0, Math.floor(ms / 1000));
var hours = Math.floor(totalSec / 3600);
var mins = Math.floor((totalSec % 3600) / 60);
var secs = totalSec % 60;
 
if (hours > 0) {
return String(hours).padStart(2, '0') + ':' + String(mins).padStart(2, '0') + ':' + String(secs).padStart(2, '0');
}
 
return String(mins).padStart(2, '0') + ':' + String(secs).padStart(2, '0');
}
 
function removeTimerById(timerId) {
var list = readStoredMvpTimers().filter(function (item) {
return item.id !== timerId;
});
writeStoredMvpTimers(list);
 
var row = document.getElementById(timerId);
if (row && row.parentNode) {
row.parentNode.removeChild(row);
}
 
var timerList = document.getElementById('timer-list');
var empty = document.getElementById('timer-empty');
if (timerList && empty && timerList.children.length === 0) {
empty.style.display = '';
}
}
 
function createTimerRow(timerObj) {
var list = document.getElementById('timer-list');
if (!list) return null;
 
var existing = document.getElementById(timerObj.id);
if (existing) return existing;
 
var empty = document.getElementById('timer-empty');
if (empty) empty.style.display = 'none';
 
var row = document.createElement('div');
row.id = timerObj.id;
row.className = 'mvp-timer-row';
row.style.cssText = 'display:grid;grid-template-columns:1fr auto auto;gap:12px;align-items:center;padding:13px 16px;border-radius:12px;background:rgba(0,212,255,0.05);border:1px solid rgba(0,212,255,0.14);';
 
var killTs = new Date(timerObj.killTime);
var h = killTs.getHours().toString().padStart(2, '0');
var m = killTs.getMinutes().toString().padStart(2, '0');
 
row.innerHTML =
'<div>' +
'<div style="font-size:0.90rem;font-weight:800;color:#fff;margin-bottom:2px;">💀 ' + timerObj.name + '</div>' +
'<div style="font-size:0.70rem;color:rgba(122,144,176,0.55);">Kill registrada às ' + h + ':' + m + ' · Respawn ' + timerObj.respawnMins + 'min</div>' +
'</div>' +
'<div class="mvp-timer-countdown" style="font-size:0.86rem;font-weight:900;color:#00d4ff;min-width:78px;text-align:right;">--:--</div>' +
'<button type="button" class="mvp-timer-remove" style="padding:8px 12px;border-radius:8px;background:rgba(255,61,90,0.10);border:1px solid rgba(255,61,90,0.22);color:#ff3d5a;font-size:0.75rem;font-weight:800;cursor:pointer;font-family:inherit;">Remover</button>';
 
var removeBtn = row.querySelector('.mvp-timer-remove');
if (removeBtn) {
removeBtn.addEventListener('click', function () {
removeTimerById(timerObj.id);
});
}
 
list.appendChild(row);
return row;
}
 
function refreshSingleTimerRow(timerObj) {
var row = document.getElementById(timerObj.id);
if (!row) {
row = createTimerRow(timerObj);
}
if (!row) return;
 
var countdown = row.querySelector('.mvp-timer-countdown');
var remaining = getRemainingTimerMs(timerObj);
 
if (countdown) {
if (remaining <= 0) {
countdown.textContent = 'Respawn';
countdown.style.color = '#00ff88';
row.style.borderColor = 'rgba(0,255,136,0.22)';
row.style.background = 'rgba(0,255,136,0.05)';
} else {
countdown.textContent = formatTimerMs(remaining);
countdown.style.color = '#00d4ff';
row.style.borderColor = 'rgba(0,212,255,0.14)';
row.style.background = 'rgba(0,212,255,0.05)';
}
}
}
 
function refreshAllMvpTimers() {
var list = readStoredMvpTimers();
for (var i = 0; i < list.length; i++) {
refreshSingleTimerRow(list[i]);
}
}
 
window.toggleBoss = function (card) {
if (!card) return;
 
var dossier = card.querySelector('.boss-dossier');
var chevron = card.querySelector('.boss-chevron');
if (!dossier) return;
 
var isOpen = dossier.style.display !== 'none';
dossier.style.display = isOpen ? 'none' : 'block';
 
if (chevron) {
chevron.textContent = isOpen ? '▼ Dossiê' : '▲ Fechar';
}
 
card.style.boxShadow = isOpen ? '' : '0 0 32px rgba(255,61,90,0.12)';
};
 
window.filterTier = function (tier) {
var btns = document.querySelectorAll('.tf-btn');
for (var i = 0; i < btns.length; i++) {
btns[i].style.opacity = '0.45';
btns[i].style.fontWeight = '700';
}
 
var active = document.getElementById('tf-' + tier);
if (active) {
active.style.opacity = '1';
active.style.fontWeight = '900';
}
 
var cards = document.querySelectorAll('.boss-card');
var shown = 0;
 
for (var j = 0; j < cards.length; j++) {
var t = cards[j].dataset.tier;
var show = tier === 'all' || t === tier;
cards[j].style.display = show ? '' : 'none';
if (show) shown++;
}
 
var label = document.getElementById('boss-count-label');
if (label) {
label.textContent = shown + (shown === 1 ? ' MVP exibido' : ' MVPs exibidos');
}
};
 
window.sortBosses = function (by) {
var btns = document.querySelectorAll('.sort-btn');
for (var i = 0; i < btns.length; i++) {
btns[i].style.background = 'rgba(255,255,255,0.06)';
btns[i].style.borderColor = 'rgba(255,255,255,0.10)';
btns[i].style.color = 'rgba(232,238,248,0.65)';
}
 
var active = document.getElementById('sort-' + by);
if (active) {
active.style.background = 'rgba(255,61,90,0.12)';
active.style.borderColor = 'rgba(255,61,90,0.30)';
active.style.color = '#ff3d5a';
}
 
var grid = document.getElementById('boss-grid');
if (!grid) return;
 
var cards = Array.prototype.slice.call(grid.querySelectorAll('.boss-card'));
cards.sort(function (a, b) {
if (by === 'danger') return parseInt(b.dataset.danger || '0', 10) - parseInt(a.dataset.danger || '0', 10);
if (by === 'exp') return parseInt(b.dataset.exp || '0', 10) - parseInt(a.dataset.exp || '0', 10);
if (by === 'respawn') return parseInt(a.dataset.respawn || '0', 10) - parseInt(b.dataset.respawn || '0', 10);
return 0;
});
 
for (var j = 0; j < cards.length; j++) {
grid.appendChild(cards[j]);
}
};
 
window.toggleClassMvp = function (row) {
if (!row) return;
var detail = row.querySelector('.class-mvp-detail');
var chevron = row.querySelector('.class-chevron');
if (!detail) return;
 
var open = detail.style.display !== 'none';
detail.style.display = open ? 'none' : 'block';
if (chevron) {
chevron.style.transform = open ? 'rotate(0deg)' : 'rotate(180deg)';
}
};
 
window.logKill = function (btn, name, respawnMins) {
var killTime = Date.now();
var respawnMs = toNumber(respawnMins, 0) * 60 * 1000;
var timerObj = {
id: 'timer-' + killTime,
name: name || 'MVP',
respawnMins: toNumber(respawnMins, 0),
respawnMs: respawnMs,
killTime: killTime
};
 
var list = readStoredMvpTimers();
list.unshift(timerObj);
writeStoredMvpTimers(list);
 
createTimerRow(timerObj);
refreshSingleTimerRow(timerObj);
 
if (btn) {
btn.style.filter = 'brightness(1.08)';
setTimeout(function () {
btn.style.filter = '';
}, 450);
}
};
 
function bindMvpGuide(context) {
var scope = context && context.querySelector ? context : document;
var root = scope.querySelector('#mvp-guide-root') || document.getElementById('mvp-guide-root');
if (!root) return;
 
var tierButtons = root.querySelectorAll('.tf-btn[data-tier]');
for (var i = 0; i < tierButtons.length; i++) {
(function (btn) {
bindActionButton(btn, function () {
filterTier(this.getAttribute('data-tier'));
});
})(tierButtons[i]);
}
 
var sortButtons = root.querySelectorAll('.sort-btn[data-sort]');
for (var j = 0; j < sortButtons.length; j++) {
(function (btn) {
bindActionButton(btn, function () {
sortBosses(this.getAttribute('data-sort'));
});
})(sortButtons[j]);
}
 
if (!window.__timeroMvpTimerInterval) {
window.__timeroMvpTimerInterval = window.setInterval(refreshAllMvpTimers, 1000);
}
 
refreshAllMvpTimers();
 
var label = document.getElementById('boss-count-label');
if (label && label.textContent.replace(/\s+/g, '') === '') {
filterTier('all');
}
}
 
/* =========================================================
  OPTIONAL GUIDE HELPERS
========================================================= */
 
function bindPhaseNav(context) {
var scope = context && context.querySelector ? context : document;
var nav = scope.querySelector('#phase-nav');
if (!nav) return;
 
var links = nav.querySelectorAll('a[href^="#"], a[data-target]');
for (var i = 0; i < links.length; i++) {
(function (link) {
if (link.dataset.timeroPhaseBound === '1') return;
link.dataset.timeroPhaseBound = '1';
 
link.addEventListener('click', function (e) {
var targetSelector = this.getAttribute('href');
if (!targetSelector || targetSelector === '#') {
targetSelector = this.getAttribute('data-target');
}
if (!targetSelector) return;
 
var target = document.querySelector(targetSelector);
if (!target) return;
 
e.preventDefault();
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
});
})(links[i]);
}
}
 
/* =========================================================
  INIT
========================================================= */
 
function init(context) {
setLogoHref();
ensureFontAwesome();
applyTimeroGuideSkin();
bindMethodViewer(context || document);
bindRouteViewer(context || document);
bindCalculator(context || document);
bindLoadout(context || document);
bindTimeroNavCards(context || document);
bindMvpGuide(context || document);
bindPhaseNav(context || document);
}
 
$(function () {
init(document);
});
/* =========================================================
  TIMERO WIKI CARD DATABASE
  Safe MediaWiki version: JS creates inputs/buttons instead of
  placing raw <input>/<button> inside wiki pages.
========================================================= */
(function () {
(function () {
   'use strict';
   'use strict';


   const CARD_DB_SELECTOR = '#timero-card-db-app';
   function esc(v){return String(v==null?'':v).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#039;');}
 
   function style(id,css){if(document.getElementById(id))return;var s=document.createElement('style');s.id=id;s.appendChild(document.createTextNode(css));document.head.appendChild(s);}
  const state = {
  function ajax(url,ok,fail){var x=new XMLHttpRequest();x.open('GET',url,true);x.setRequestHeader('Accept','application/json');x.onreadystatechange=function(){if(x.readyState!==4)return;if(x.status<200||x.status>=300){fail('HTTP '+x.status+': '+x.responseText.substr(0,240));return;}try{ok(JSON.parse(x.responseText));}catch(e){fail('JSON inválido: '+x.responseText.substr(0,240));}};x.onerror=function(){fail('Falha de rede.');};x.send(null);}
    api: '',
  function debounce(fn,ms){var t;return function(){var a=arguments,self=this;clearTimeout(t);t=setTimeout(function(){fn.apply(self,a);},ms);};}
    cards: [],
  function rgba(hex,a){var h=String(hex||'#58d7ff').replace('#',''),n=parseInt(h,16);return 'rgba('+((n>>16)&255)+','+((n>>8)&255)+','+(n&255)+','+a+')';}
    groups: [],
    q: '',
    category: 'all',
    changed: false,
    sort: 'card_name',
    dir: 'asc',
    expandedCardId: null,
    loading: false,
    error: ''
  };
 
   const categories = [
    { id: 'all', label: 'Todas', icon: '◆', color: '#7c6aff' },
    { id: 'weapon', label: 'Armas', icon: '⚔️', color: '#ff6b7a' },
    { id: 'armor', label: 'Armaduras', icon: '🛡', color: '#00d4ff' },
    { id: 'accessory', label: 'Acessórios', icon: '💎', color: '#f9a826' },
    { id: 'headgear', label: 'Headgear', icon: '🎩', color: '#b06cff' },
    { id: 'shield', label: 'Escudos', icon: '🛡️', color: '#70b8ff' },
    { id: 'garment', label: 'Capas', icon: '🧥', color: '#60d090' },
    { id: 'shoes', label: 'Sapatos', icon: '👢', color: '#f0c840' }
  ];
 
  function escapeHtml(value) {
    return String(value ?? '')
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#039;');
  }
 
  function normalize(value) {
    return String(value ?? '').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
  }
 
  function debounce(fn, delay) {
    let timer = null;
    return function () {
      const args = arguments;
      clearTimeout(timer);
      timer = setTimeout(function () {
        fn.apply(null, args);
      }, delay);
    };
  }
 
  function getRoot() {
    return document.querySelector(CARD_DB_SELECTOR);
  }
 
  function cardIconUrl(cardId) {
    return 'https://timero.com.br/images/item/icons/' + encodeURIComponent(cardId) + '.png';
  }
 
  function cardLink(cardId) {
    return 'https://timero.com.br/pt/item?id=' + encodeURIComponent(cardId);
  }
 
  function categoryMeta(category) {
    return categories.find(c => c.id === category) || categories[0];
  }
 
  function changedBadge(card) {
    if (String(card.changed) === '1') {
      return '<span class="tc-status tc-changed">Alterada</span>';
    }
    return '<span class="tc-status tc-original">Original</span>';
  }
 
  function scriptToReadable(script) {
    const raw = String(script || '').trim();
    if (!raw) return '—';
 
    return raw
      .replace(/bonus\s+/gi, '')
      .replace(/;/g, '; ')
      .replace(/bStr/gi, 'STR')
      .replace(/bAgi/gi, 'AGI')
      .replace(/bVit/gi, 'VIT')
      .replace(/bInt/gi, 'INT')
      .replace(/bDex/gi, 'DEX')
      .replace(/bLuk/gi, 'LUK')
      .replace(/bAllStats/gi, 'All Stats')
      .replace(/bMaxHP/gi, 'Max HP')
      .replace(/bMaxSP/gi, 'Max SP')
      .replace(/bBaseAtk/gi, 'ATK')
      .replace(/bMatk/gi, 'MATK')
      .replace(/bAspdRate/gi, 'ASPD %')
      .replace(/bAtkRate/gi, 'ATK %')
      .replace(/bMatkRate/gi, 'MATK %')
      .replace(/\s+/g, ' ')
      .trim();
  }
 
  function injectStyles() {
    if (document.getElementById('timero-card-db-style')) return;
 
    const style = document.createElement('style');
    style.id = 'timero-card-db-style';
    style.textContent = `
      #timero-card-db-app,
      #timero-card-db-app * {
        box-sizing: border-box;
      }
 
      #timero-card-db-app {
        --tc-bg: #050914;
        --tc-card: rgba(8,14,26,0.94);
        --tc-card-2: rgba(4,8,18,0.99);
        --tc-border: rgba(80,170,255,0.18);
        --tc-border-soft: rgba(255,255,255,0.06);
        --tc-cyan: #58d7ff;
        --tc-blue: #4a90d9;
        --tc-muted: #6070a0;
        --tc-text: #d8ecff;
        --tc-gold: #f0c840;
        --tc-green: #70d890;
        --tc-red: #ff6b7a;
        font-family: 'Segoe UI', system-ui, sans-serif;
      }
 
      .tc-shell {
        background:
          radial-gradient(circle at 18% 0%, rgba(74,144,217,0.10), transparent 34%),
          radial-gradient(circle at 88% 24%, rgba(92,70,180,0.08), transparent 30%),
          linear-gradient(135deg, rgba(5,10,22,0.98), rgba(2,5,14,0.99));
        border: 1px solid var(--tc-border);
        border-radius: 18px;
        padding: 24px;
        margin: 0 0 34px 0;
        box-shadow: 0 0 30px rgba(0,0,0,0.35), inset 0 0 28px rgba(80,160,255,0.025);
        color: var(--tc-text);
      }
 
      .tc-head {
        background: linear-gradient(90deg, rgba(74,144,217,0.14), rgba(74,144,217,0.04), transparent);
        border: 1px solid rgba(74,144,217,0.18);
        border-left: 5px solid var(--tc-blue);
        border-radius: 12px;
        padding: 16px 18px;
        margin: 0 0 18px 0;
      }
 
      .tc-kicker {
        color: var(--tc-cyan);
        font-weight: 900;
        letter-spacing: .10em;
        text-transform: uppercase;
        font-size: .78rem;
        margin-bottom: 8px;
      }
 
      .tc-title {
        color: #fff;
        font-size: clamp(1.4rem, 2.6vw, 2rem);
        font-weight: 900;
        margin: 0 0 6px 0;
        line-height: 1.1;
      }
 
      .tc-subtitle {
        color: rgba(180,205,230,0.72);
        font-size: .92rem;
        line-height: 1.65;
      }
 
      .tc-toolbar {
        background: linear-gradient(135deg, rgba(8,14,26,0.94), rgba(4,8,18,0.99));
        border: 1px solid rgba(255,255,255,0.08);
        border-radius: 14px;
        padding: 16px;
        margin: 0 0 16px 0;
      }
 
      .tc-topline {
        display: flex;
        gap: 12px;
        align-items: center;
        flex-wrap: wrap;
      }
 
      .tc-search-wrap {
        flex: 1;
        min-width: 260px;
        position: relative;
      }
 
      .tc-search-icon {
        position: absolute;
        left: 13px;
        top: 50%;
        transform: translateY(-50%);
        opacity: .55;
        pointer-events: none;
      }
 
      .tc-search {
        width: 100%;
        padding: 11px 12px 11px 40px;
        border-radius: 11px;
        background: rgba(0,0,0,0.34);
        border: 1px solid rgba(255,255,255,0.09);
        color: #e8eef8;
        font-size: .92rem;
        outline: none;
        font-family: inherit;
      }
 
      .tc-search:focus {
        border-color: rgba(88,215,255,0.42);
        box-shadow: 0 0 0 3px rgba(88,215,255,0.08);
      }
 
      .tc-toggle {
        display: inline-flex;
        align-items: center;
        gap: 9px;
        cursor: pointer;
        padding: 10px 14px;
        border-radius: 11px;
        background: rgba(255,255,255,0.04);
        border: 1px solid rgba(255,255,255,0.08);
        color: rgba(180,205,230,0.75);
        font-size: .82rem;
        font-weight: 800;
        white-space: nowrap;
        user-select: none;
      }
 
      .tc-toggle input {
        width: 16px;
        height: 16px;
        accent-color: var(--tc-cyan);
      }
 
      .tc-count {
        padding: 10px 15px;
        border-radius: 11px;
        background: rgba(74,144,217,0.10);
        border: 1px solid rgba(74,144,217,0.22);
        color: var(--tc-cyan);
        font-weight: 900;
        font-size: .82rem;
        white-space: nowrap;
      }
 
      .tc-tabs {
        display: flex;
        gap: 8px;
        flex-wrap: wrap;
        margin-top: 12px;
      }
 
      .tc-tab {
        border: 1px solid rgba(255,255,255,0.08);
        background: rgba(255,255,255,0.035);
        color: rgba(180,205,230,0.68);
        border-radius: 999px;
        padding: 8px 13px;
        cursor: pointer;
        font-family: inherit;
        font-weight: 800;
        font-size: .78rem;
      }
 
      .tc-tab.active {
        background: linear-gradient(135deg, rgba(74,144,217,0.22), rgba(74,144,217,0.08));
        border-color: rgba(88,215,255,0.30);
        color: var(--tc-cyan);
        box-shadow: 0 0 16px rgba(88,215,255,0.08);
      }
 
      .tc-table {
        border: 1px solid rgba(255,255,255,0.08);
        border-radius: 14px;
        overflow: hidden;
        background: rgba(3,6,14,0.96);
      }
 
      .tc-table-head {
        display: grid;
        grid-template-columns: 88px 1.35fr 150px 145px 120px;
        background: rgba(0,0,0,0.42);
        border-bottom: 1px solid rgba(255,255,255,0.06);
      }
 
      .tc-th {
        padding: 12px 14px;
        color: var(--tc-muted);
        font-weight: 900;
        letter-spacing: .08em;
        text-transform: uppercase;
        font-size: .68rem;
        border-right: 1px solid rgba(255,255,255,0.045);
      }
 
      .tc-th.sortable {
        cursor: pointer;
      }
 
      .tc-row {
        display: grid;
        grid-template-columns: 88px 1.35fr 150px 145px 120px;
        align-items: center;
        border-bottom: 1px solid rgba(255,255,255,0.045);
        cursor: pointer;
        transition: background .15s ease, border-color .15s ease;
      }
 
      .tc-row:hover {
        background: rgba(74,144,217,0.06);
      }
 
      .tc-cell {
        padding: 13px 14px;
        border-right: 1px solid rgba(255,255,255,0.035);
      }
 
      .tc-id {
        font-family: 'Courier New', monospace;
        color: var(--tc-gold);
        font-weight: 900;
      }
 
      .tc-name-line {
        display: flex;
        align-items: center;
        gap: 12px;
        min-width: 0;
      }
 
      .tc-icon {
        width: 42px;
        height: 42px;
        border-radius: 10px;
        background: rgba(0,0,0,0.28);
        border: 1px solid rgba(255,255,255,0.08);
        display: flex;
        align-items: center;
        justify-content: center;
        overflow: hidden;
        flex: 0 0 42px;
      }
 
      .tc-icon img {
        max-width: 36px;
        max-height: 36px;
        object-fit: contain;
        image-rendering: auto;
      }
 
      .tc-card-name {
        color: var(--tc-cyan);
        font-weight: 900;
        font-size: .96rem;
        line-height: 1.2;
      }
 
      .tc-monster-name {
        color: rgba(180,205,230,0.58);
        font-size: .76rem;
        margin-top: 3px;
      }
 
      .tc-pill {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        padding: 6px 10px;
        border-radius: 999px;
        background: rgba(255,255,255,0.04);
        border: 1px solid rgba(255,255,255,0.08);
        color: rgba(200,220,240,0.72);
        font-weight: 800;
        font-size: .76rem;
        white-space: nowrap;
      }
 
      .tc-status {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        padding: 6px 10px;
        border-radius: 999px;
        font-weight: 900;
        font-size: .72rem;
        white-space: nowrap;
      }
 
      .tc-changed {
        background: rgba(112,216,144,0.10);
        border: 1px solid rgba(112,216,144,0.24);
        color: var(--tc-green);
      }
 
      .tc-original {
        background: rgba(255,255,255,0.035);
        border: 1px solid rgba(255,255,255,0.08);
        color: rgba(180,205,230,0.55);
      }
 
      .tc-detail {
        display: none;
        grid-column: 1 / -1;
        padding: 0;
        background: rgba(0,0,0,0.18);
        border-bottom: 1px solid rgba(255,255,255,0.055);
      }
 
      .tc-row.open + .tc-detail {
        display: block;
      }
 
      .tc-detail-grid {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        gap: 12px;
        padding: 16px;
      }
 
      .tc-effect-box {
        border-radius: 12px;
        padding: 14px;
        background: linear-gradient(135deg, rgba(8,14,26,0.96), rgba(4,8,18,0.99));
        border: 1px solid rgba(255,255,255,0.08);
        min-height: 112px;
      }
 
      .tc-effect-title {
        color: var(--tc-muted);
        font-weight: 900;
        letter-spacing: .08em;
        text-transform: uppercase;
        font-size: .68rem;
        margin-bottom: 8px;
      }
 
      .tc-effect-text {
        color: rgba(220,235,255,0.78);
        font-size: .84rem;
        line-height: 1.65;
        white-space: pre-wrap;
      }
 
      .tc-effect-box.new {
        border-color: rgba(112,216,144,0.18);
      }
 
      .tc-effect-box.collection {
        border-color: rgba(240,200,64,0.18);
      }
 
      .tc-empty,
      .tc-loading,
      .tc-error {
        padding: 34px;
        text-align: center;
        color: rgba(180,205,230,0.58);
        font-weight: 700;
      }
 
      .tc-error {
        color: #ff8a8a;
      }
 
      .tc-groups {
        margin-top: 26px;
      }
 
      .tc-group-list {
        display: grid;
        gap: 10px;
      }
 
      .tc-group-card {
        border-radius: 14px;
        border: 1px solid rgba(255,255,255,0.08);
        background: linear-gradient(135deg, rgba(8,14,26,0.94), rgba(4,8,18,0.99));
        overflow: hidden;
      }
 
      .tc-group-head {
        padding: 15px 18px;
        display: grid;
        grid-template-columns: auto 1fr auto;
        gap: 13px;
        align-items: center;
      }
 
      .tc-group-icon {
        width: 42px;
        height: 42px;
        border-radius: 12px;
        background: rgba(74,144,217,0.10);
        border: 1px solid rgba(74,144,217,0.22);
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 1.25rem;
      }
 
      .tc-group-key {
        color: var(--tc-muted);
        font-size: .64rem;
        font-weight: 900;
        letter-spacing: .11em;
        text-transform: uppercase;
        margin-bottom: 2px;
      }
 
      .tc-group-name {
        color: #fff;
        font-weight: 900;
      }
 
      .tc-group-bonus {
        padding: 11px 18px;
        border-top: 1px solid rgba(255,255,255,0.055);
        color: rgba(220,235,255,0.74);
        line-height: 1.55;
        font-size: .84rem;
      }
 
      @media (max-width: 900px) {
        .tc-table-head {
          display: none;
        }
 
        .tc-row {
          grid-template-columns: 1fr;
          padding: 12px;
        }
 
        .tc-cell {
          border-right: none;
          padding: 7px 4px;
        }
 
        .tc-detail-grid {
          grid-template-columns: 1fr;
        }
 
        .tc-topline {
          align-items: stretch;
        }
 
        .tc-toggle,
        .tc-count {
          width: 100%;
          justify-content: center;
        }
      }
    `;
    document.head.appendChild(style);
  }
 
  function createShell(root) {
    root.innerHTML = `
      <div class="tc-shell">
        <div class="tc-head">
          <div class="tc-kicker">◇ Banco de Dados</div>
          <h2 class="tc-title">Todas as Cartas Balanceadas</h2>
          <div class="tc-subtitle">
            Pesquise por nome da carta, ID, monstro, slot, efeito antigo, efeito novo, bônus de coleção ou script de coleção.
          </div>
        </div>
 
        <div class="tc-toolbar">
          <div class="tc-topline">
            <div class="tc-search-wrap">
              <span class="tc-search-icon">🔍</span>
              <input class="tc-search" id="tc-search" type="text" placeholder="Buscar carta, ID, monstro, efeito ou coleção..." autocomplete="off">
            </div>
 
            <label class="tc-toggle" for="tc-changed">
              <input id="tc-changed" type="checkbox">
              ⚡ Apenas alteradas
            </label>
 
            <div class="tc-count" id="tc-count">— cartas</div>
          </div>
 
          <div class="tc-tabs" id="tc-tabs"></div>
        </div>
 
        <div class="tc-table">
          <div class="tc-table-head">
            <div class="tc-th sortable" data-sort="id">ID ↕</div>
            <div class="tc-th sortable" data-sort="name">Carta / Monstro ↕</div>
            <div class="tc-th sortable" data-sort="slot">Slot ↕</div>
            <div class="tc-th sortable" data-sort="category">Categoria ↕</div>
            <div class="tc-th">Status</div>
          </div>
          <div id="tc-body">
            <div class="tc-loading">Carregando cartas...</div>
          </div>
        </div>
 
        <div class="tc-groups">
          <div class="tc-head" style="margin-top:26px;">
            <div class="tc-kicker">◇ Grupos de Coleção</div>
            <h2 class="tc-title" style="font-size:1.35rem;">Bônus permanentes por grupo</h2>
            <div class="tc-subtitle">Dados carregados da tabela <strong>wiki_card_groups</strong>.</div>
          </div>
          <div class="tc-group-list" id="tc-groups"></div>
        </div>
      </div>
    `;
 
    const tabs = root.querySelector('#tc-tabs');
    categories.forEach(cat => {
      const btn = document.createElement('button');
      btn.type = 'button';
      btn.className = 'tc-tab' + (cat.id === state.category ? ' active' : '');
      btn.dataset.category = cat.id;
      btn.innerHTML = `${cat.icon} ${escapeHtml(cat.label)}`;
      btn.addEventListener('click', function () {
        state.category = cat.id;
        refresh(root);
      });
      tabs.appendChild(btn);
    });
 
    root.querySelector('#tc-search').addEventListener('input', debounce(function (e) {
      state.q = e.target.value.trim();
      refresh(root);
    }, 220));
 
    root.querySelector('#tc-changed').addEventListener('change', function (e) {
      state.changed = !!e.target.checked;
      refresh(root);
    });
 
    root.querySelectorAll('.tc-th.sortable').forEach(th => {
      th.addEventListener('click', function () {
        const sort = th.dataset.sort;
        if (state.sort === sort) {
          state.dir = state.dir === 'asc' ? 'desc' : 'asc';
        } else {
          state.sort = sort;
          state.dir = 'asc';
        }
        refresh(root);
      });
    });
  }
 
  async function fetchCards(root) {
    state.loading = true;
    state.error = '';
    renderBody(root);
 
    const params = new URLSearchParams();
    params.set('q', state.q);
    params.set('category', state.category);
    params.set('changed', state.changed ? '1' : '0');
    params.set('sort', state.sort);
    params.set('dir', state.dir);
 
    try {
      const response = await fetch(state.api + '?' + params.toString(), {
        credentials: 'same-origin',
        cache: 'no-store'
      });
 
      const text = await response.text();
      let data;
 
      try {
        data = JSON.parse(text);
      } catch (err) {
        throw new Error('Invalid JSON returned by API: ' + text.slice(0, 300));
      }
 
      if (!data.ok) {
        throw new Error(data.error || 'API returned an unknown error.');
      }
 
      state.cards = Array.isArray(data.cards) ? data.cards : [];
      state.groups = Array.isArray(data.groups) ? data.groups : [];
      state.expandedCardId = null;
    } catch (err) {
      state.cards = [];
      state.groups = [];
      state.error = err.message || String(err);
    } finally {
      state.loading = false;
      renderAll(root);
    }
  }
 
  function refresh(root) {
    updateTabs(root);
    fetchCards(root);
  }
 
  function updateTabs(root) {
    root.querySelectorAll('.tc-tab').forEach(btn => {
      btn.classList.toggle('active', btn.dataset.category === state.category);
    });
  }
 
  function renderAll(root) {
    renderBody(root);
    renderGroups(root);
    const count = root.querySelector('#tc-count');
    if (count) {
      count.textContent = state.cards.length + ' carta' + (state.cards.length === 1 ? '' : 's');
    }
  }
 
  function renderBody(root) {
    const body = root.querySelector('#tc-body');
    if (!body) return;
 
    if (state.loading) {
      body.innerHTML = '<div class="tc-loading">Carregando cartas...</div>';
      return;
    }
 
    if (state.error) {
      body.innerHTML = '<div class="tc-error">Erro ao carregar cartas: ' + escapeHtml(state.error) + '</div>';
      return;
    }
 
    if (!state.cards.length) {
      body.innerHTML = '<div class="tc-empty">Nenhuma carta encontrada com os filtros atuais.</div>';
      return;
    }
 
    body.innerHTML = '';
 
    state.cards.forEach(card => {
      const meta = categoryMeta(card.category);
      const row = document.createElement('div');
      row.className = 'tc-row';
      row.dataset.cardId = card.card_id;
 
      const monster = card.monster_name ? `<div class="tc-monster-name">${escapeHtml(card.monster_name)}</div>` : '';
      const group = card.collection_group_name || '—';
 
      row.innerHTML = `
        <div class="tc-cell tc-id">#${escapeHtml(card.card_id)}</div>
        <div class="tc-cell">
          <div class="tc-name-line">
            <div class="tc-icon">
              <img src="${cardIconUrl(card.card_id)}" alt="${escapeHtml(card.card_name)}" onerror="this.style.display='none';this.parentNode.textContent='🃏';">
            </div>
            <div>
              <div class="tc-card-name">
                <a href="${cardLink(card.card_id)}" target="_blank" rel="noopener" style="color:inherit;text-decoration:none;">${escapeHtml(card.card_name)}</a>
              </div>
              ${monster}
            </div>
          </div>
        </div>
        <div class="tc-cell"><span class="tc-pill">${escapeHtml(card.slot_type || 'Unknown')}</span></div>
        <div class="tc-cell"><span class="tc-pill" style="border-color:${meta.color}44;color:${meta.color};">${meta.icon} ${escapeHtml(group)}</span></div>
        <div class="tc-cell">${changedBadge(card)}</div>
      `;
 
      const detail = document.createElement('div');
      detail.className = 'tc-detail';
 
      detail.innerHTML = `
        <div class="tc-detail-grid">
          <div class="tc-effect-box">
            <div class="tc-effect-title">Efeito Antigo</div>
            <div class="tc-effect-text">${escapeHtml(card.old_effect || '—')}</div>
          </div>
 
          <div class="tc-effect-box new">
            <div class="tc-effect-title">Efeito Novo</div>
            <div class="tc-effect-text">${escapeHtml(card.new_effect || '—')}</div>
          </div>
 
          <div class="tc-effect-box collection">
            <div class="tc-effect-title">Coleção</div>
            <div class="tc-effect-text"><strong style="color:#f0c840;">Grupo:</strong> ${escapeHtml(card.collection_group_name || '—')}<br><strong style="color:#f0c840;">Pontos:</strong> ${escapeHtml(card.collection_points || 0)}<br><strong style="color:#f0c840;">Efeito:</strong> ${escapeHtml(card.collection_effect || '—')}<br><strong style="color:#f0c840;">Script:</strong> ${escapeHtml(scriptToReadable(card.collection_script))}</div>
          </div>
        </div>
      `;
 
      row.addEventListener('click', function () {
        const isOpen = row.classList.contains('open');
        body.querySelectorAll('.tc-row.open').forEach(r => r.classList.remove('open'));
        if (!isOpen) row.classList.add('open');
      });
 
      body.appendChild(row);
      body.appendChild(detail);
    });
  }
 
  function renderGroups(root) {
    const wrap = root.querySelector('#tc-groups');
    if (!wrap) return;
 
    if (state.error) {
      wrap.innerHTML = '';
      return;
    }
 
    if (!state.groups.length) {
      wrap.innerHTML = '<div class="tc-empty">Nenhum grupo de coleção cadastrado.</div>';
      return;
    }
 
    wrap.innerHTML = '';
 
    state.groups.forEach(group => {
      const color = group.color || '#7c6aff';
      const el = document.createElement('div');
      el.className = 'tc-group-card';
      el.style.borderColor = color + '33';
 
      el.innerHTML = `
        <div class="tc-group-head">
          <div class="tc-group-icon" style="border-color:${color}44;background:${color}14;">${escapeHtml(group.icon || '🃏')}</div>
          <div>
            <div class="tc-group-key" style="color:${color};">Grupo · ${escapeHtml(group.group_key)}</div>
            <div class="tc-group-name">${escapeHtml(group.group_name)}</div>
          </div>
          <div class="tc-pill" style="border-color:${color}33;color:${color};">${escapeHtml(group.card_count || 0)} cartas · Min ${escapeHtml(group.min_cards || 1)}</div>
        </div>
        <div class="tc-group-bonus">
          <strong style="color:${color};">Bônus:</strong> ${escapeHtml(group.collection_effect || '—')}
        </div>
      `;
 
      wrap.appendChild(el);
    });
  }
 
  function initCardDb() {
    const root = getRoot();
    if (!root || root.dataset.tcReady === '1') return;


     root.dataset.tcReady = '1';
  function injectSharedCss(){style('timero-apps-fixed-css',
     state.api = root.dataset.api || '/pt/api/wiki_cards.php';
     '.tr-app,.tr-app *{box-sizing:border-box}.tr-app{font-family:Segoe UI,system-ui,sans-serif;color:#d8ecff}.tr-shell{background:radial-gradient(circle at 18% 0%,rgba(74,144,217,.10),transparent 34%),radial-gradient(circle at 88% 24%,rgba(92,70,180,.08),transparent 30%),linear-gradient(135deg,rgba(5,10,22,.98),rgba(2,5,14,.99));border:1px solid rgba(80,170,255,.18);border-radius:18px;padding:24px;margin:0 0 34px;box-shadow:0 0 30px rgba(0,0,0,.35),inset 0 0 28px rgba(80,160,255,.025)}'+
    '.tr-head{background:linear-gradient(90deg,rgba(74,144,217,.14),rgba(74,144,217,.04),transparent);border:1px solid rgba(74,144,217,.18);border-left:5px solid #4a90d9;border-radius:12px;padding:16px 18px;margin:0 0 18px}.tr-kicker{color:#58d7ff;font-weight:900;letter-spacing:.10em;text-transform:uppercase;font-size:.78rem;margin-bottom:8px}.tr-title{color:#fff;font-size:clamp(1.4rem,2.6vw,2rem);font-weight:900;margin:0 0 6px;line-height:1.1}.tr-sub{color:rgba(180,205,230,.72);font-size:.92rem;line-height:1.65}.tr-btn{border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.04);color:rgba(180,205,230,.72);border-radius:999px;padding:8px 13px;cursor:pointer;font-family:inherit;font-weight:800;font-size:.78rem}.tr-btn.active{background:linear-gradient(135deg,rgba(74,144,217,.22),rgba(74,144,217,.08));border-color:rgba(88,215,255,.30);color:#58d7ff}.tr-pill{display:inline-flex;align-items:center;justify-content:center;padding:6px 10px;border-radius:999px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);font-weight:800;font-size:.76rem;white-space:nowrap}'+
    '.tc-toolbar{background:linear-gradient(135deg,rgba(8,14,26,.94),rgba(4,8,18,.99));border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:16px;margin:0 0 16px}.tc-top{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.tc-search-wrap{flex:1;min-width:260px;position:relative}.tc-search-wrap span{position:absolute;left:13px;top:50%;transform:translateY(-50%);opacity:.55}.tc-search{width:100%;padding:11px 12px 11px 40px;border-radius:11px;background:rgba(0,0,0,.34);border:1px solid rgba(255,255,255,.09);color:#e8eef8;font-size:.92rem;outline:none}.tc-toggle{display:inline-flex;gap:9px;align-items:center;cursor:pointer;padding:10px 14px;border-radius:11px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);font-weight:800}.tc-tabs{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}.tc-table{border:1px solid rgba(255,255,255,.08);border-radius:14px;overflow:hidden;background:rgba(3,6,14,.96)}.tc-head-row,.tc-row{display:grid;grid-template-columns:88px 1.35fr 150px 145px 120px;align-items:center}.tc-head-row{background:rgba(0,0,0,.42);border-bottom:1px solid rgba(255,255,255,.06)}.tc-th{padding:12px 14px;color:#6070a0;font-weight:900;letter-spacing:.08em;text-transform:uppercase;font-size:.68rem;border-right:1px solid rgba(255,255,255,.045);cursor:pointer}.tc-row{border-bottom:1px solid rgba(255,255,255,.045);cursor:pointer}.tc-row:hover{background:rgba(74,144,217,.06)}.tc-cell{padding:13px 14px;border-right:1px solid rgba(255,255,255,.035)}.tc-id{font-family:Courier New,monospace;color:#f0c840;font-weight:900}.tc-name{display:flex;align-items:center;gap:12px}.tc-icon{width:42px;height:42px;border-radius:10px;background:rgba(0,0,0,.28);border:1px solid rgba(255,255,255,.08);display:flex;align-items:center;justify-content:center;overflow:hidden;flex:0 0 42px}.tc-icon img{max-width:36px;max-height:36px}.tc-card-title{color:#58d7ff;font-weight:900}.tc-small{color:rgba(180,205,230,.58);font-size:.76rem;margin-top:3px}.tc-status{display:inline-flex;padding:6px 10px;border-radius:999px;font-size:.72rem;font-weight:900}.tc-changed{background:rgba(112,216,144,.10);border:1px solid rgba(112,216,144,.24);color:#70d890}.tc-original{background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.08);color:rgba(180,205,230,.55)}.tc-detail{display:none;background:rgba(0,0,0,.18);border-bottom:1px solid rgba(255,255,255,.055)}.tc-detail.open{display:block}.tc-detail-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;padding:16px}.tc-box{border-radius:12px;padding:14px;background:linear-gradient(135deg,rgba(8,14,26,.96),rgba(4,8,18,.99));border:1px solid rgba(255,255,255,.08);min-height:112px}.tc-box h4{color:#6070a0;font-weight:900;letter-spacing:.08em;text-transform:uppercase;font-size:.68rem;margin:0 0 8px}.tc-box div{color:rgba(220,235,255,.78);font-size:.84rem;line-height:1.65;white-space:pre-wrap}.tc-msg{padding:34px;text-align:center;color:rgba(180,205,230,.58);font-weight:700}.tc-error{color:#ff8a8a}.tc-groups{margin-top:26px}.tc-group{border-radius:14px;border:1px solid rgba(255,255,255,.08);background:linear-gradient(135deg,rgba(8,14,26,.94),rgba(4,8,18,.99));overflow:hidden;margin-bottom:10px}.tc-group-main{padding:15px 18px;display:grid;grid-template-columns:auto 1fr auto;gap:13px;align-items:center}.tc-group-icon{width:42px;height:42px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:1.25rem}.tc-group-key{font-size:.64rem;font-weight:900;letter-spacing:.11em;text-transform:uppercase;margin-bottom:2px}.tc-group-name{color:#fff;font-weight:900}.tc-group-bonus{padding:11px 18px;border-top:1px solid rgba(255,255,255,.055);color:rgba(220,235,255,.74);line-height:1.55;font-size:.84rem}'+
     '.tl-root{margin:-1em -1.5em;min-height:100vh;background:radial-gradient(circle at 18% 0%,rgba(88,215,255,.09),transparent 34%),radial-gradient(circle at 88% 24%,rgba(176,108,255,.08),transparent 30%),linear-gradient(135deg,#050914,#02050e);overflow:hidden}.tl-inner{max-width:1280px;margin:0 auto;padding:44px 28px 60px}.tl-hero{background:linear-gradient(135deg,rgba(8,14,26,.94),rgba(4,8,18,.99));border:1px solid rgba(88,215,255,.18);border-radius:18px;padding:34px;margin-bottom:18px}.tl-hero h1{font-size:clamp(2.2rem,5vw,4rem);line-height:.96;font-weight:900;color:#fff;letter-spacing:-.035em;margin:0 0 16px}.tl-hero h1 span{background:linear-gradient(90deg,#00ff88,#58d7ff,#f9a826);-webkit-background-clip:text;background-clip:text;color:transparent}.tl-stats,.tl-grid,.tl-checks,.tl-related{display:grid;gap:12px}.tl-stats{grid-template-columns:repeat(5,1fr);margin:18px 0}.tl-stat{background:rgba(0,0,0,.24);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:14px;text-align:center}.tl-stat strong{display:block;font-size:1.35rem;color:#58d7ff;font-weight:900}.tl-stat span{font-size:.72rem;color:rgba(122,144,176,.72);letter-spacing:.12em;text-transform:uppercase;font-weight:800}.tl-panel{background:linear-gradient(135deg,rgba(8,14,26,.94),rgba(4,8,18,.99));border:1px solid rgba(255,255,255,.08);border-radius:18px;padding:16px;margin-bottom:18px}.tl-label{color:#7a90b0;font-weight:900;letter-spacing:.11em;text-transform:uppercase;font-size:.72rem;margin-bottom:10px}.tl-flex{display:flex;gap:9px;flex-wrap:wrap}.tl-track{height:12px;border-radius:999px;background:rgba(255,255,255,.05);overflow:hidden;display:grid;grid-template-columns:24.5fr 24.5fr 20fr 15fr 16fr;margin-bottom:14px}.tl-track span:nth-child(1){background:#00ff88}.tl-track span:nth-child(2){background:#00d4ff}.tl-track span:nth-child(3){background:#f9a826}.tl-track span:nth-child(4){background:#b06cff}.tl-track span:nth-child(5){background:#ff3d5a}.tl-phase{background:linear-gradient(135deg,rgba(8,14,26,.94),rgba(4,8,18,.99));border:1px solid var(--line);border-radius:18px;padding:24px;margin-bottom:22px}.tl-head{display:flex;gap:18px;margin-bottom:22px}.tl-icon{width:64px;height:64px;border-radius:18px;display:flex;align-items:center;justify-content:center;flex:0 0 64px;font-size:1.8rem;background:var(--soft);border:1px solid var(--line)}.tl-chip{display:inline-flex;margin:0 8px 8px 0;padding:3px 10px;border-radius:999px;background:var(--soft);border:1px solid var(--line);color:var(--color);font-size:.65rem;letter-spacing:.13em;text-transform:uppercase;font-weight:900}.tl-phase h2{color:#fff;margin:0 0 8px;font-size:clamp(1.35rem,2.5vw,2rem);border:0}.tl-phase p{color:rgba(176,192,224,.75);line-height:1.7;margin:0}.tl-grid{grid-template-columns:1fr 1fr;margin-bottom:18px}.tl-box{border-radius:16px;overflow:hidden;background:rgba(0,0,0,.24);border:1px solid var(--line)}.tl-box-head{padding:13px 16px;background:var(--soft);border-bottom:1px solid var(--line);color:var(--color);font-weight:900;letter-spacing:.08em;text-transform:uppercase;font-size:.78rem}.tl-box-body{padding:15px;display:flex;flex-direction:column;gap:10px}.tl-card{border-radius:12px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);padding:12px 14px}.tl-map{display:flex;justify-content:space-between;gap:14px}.tl-name{color:#fff;font-size:.95rem;font-weight:900}.tl-muted{color:rgba(122,144,176,.72);font-size:.76rem;margin-top:3px}.tl-level{color:var(--color);font-size:.78rem;font-weight:900;white-space:nowrap;text-align:right}.tl-mob{cursor:pointer}.tl-mob-main{display:flex;align-items:center;gap:12px}.tl-mob-icon{width:42px;height:42px;border-radius:10px;display:flex;align-items:center;justify-content:center;background:var(--soft);border:1px solid var(--line);font-size:1.2rem}.tl-mob-info{flex:1}.tl-detail{display:none;padding:10px 0 0 54px;color:rgba(180,205,230,.78);font-size:.82rem;line-height:1.6}.tl-mob.open .tl-detail{display:block}.tl-checks{grid-template-columns:1fr 1fr}.tl-check{display:flex;align-items:center;gap:10px;cursor:pointer}.tl-mark{width:18px;height:18px;border-radius:5px;border:1px solid var(--line);background:var(--soft);display:flex;align-items:center;justify-content:center;color:var(--color);font-weight:900}.tl-check.checked .tl-text{text-decoration:line-through;opacity:.55}.tl-note{display:none;margin-top:14px;border-radius:12px;padding:14px 16px;color:rgba(220,235,255,.78);line-height:1.65;font-size:.88rem}.tl-note strong{display:block;margin-bottom:5px;letter-spacing:.09em;text-transform:uppercase;font-size:.75rem}.tl-footer{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-top:20px}.tl-related{grid-template-columns:repeat(4,1fr);margin-top:24px}.tl-related .tl-panel{text-align:center;margin:0}@media(max-width:980px){.tl-stats,.tl-grid,.tl-checks,.tl-related{grid-template-columns:1fr}.tl-inner{padding:26px 14px 44px}.tl-hero,.tl-phase{padding:20px}.tl-head{flex-direction:column}.tl-map{flex-direction:column}.tl-level{text-align:left}}');}


    injectStyles();
  /* Card app */
     createShell(root);
  function initCards(){var root=document.getElementById('timero-card-db-app');if(!root||root.getAttribute('data-ready')==='1')return;root.setAttribute('data-ready','1');injectSharedCss();var state={api:root.getAttribute('data-api')||'/pt/api/wiki_cards.php',q:'',cat:'all',changed:false,sort:'card_name',dir:'asc',cards:[],groups:[],err:'',load:false};var cats=[['all','Todas','◆','#7c6aff'],['weapon','Armas','⚔️','#ff6b7a'],['armor','Armaduras','🛡','#00d4ff'],['accessory','Acessórios','💎','#f9a826'],['headgear','Headgear','🎩','#b06cff'],['shield','Escudos','🛡️','#70b8ff'],['garment','Capas','🧥','#60d090'],['shoes','Sapatos','👢','#f0c840']];function meta(id){for(var i=0;i<cats.length;i++)if(cats[i][0]===id)return cats[i];return cats[0];}function readScript(s){return String(s||'').replace(/bonus\s+/gi,'').replace(/;/g,'; ').replace(/bAllStats/gi,'All Stats').replace(/bMaxHP/gi,'Max HP').replace(/bMaxSP/gi,'Max SP').replace(/bBaseAtk/gi,'ATK').replace(/bMatk/gi,'MATK').replace(/bStr/gi,'STR').replace(/bAgi/gi,'AGI').replace(/bVit/gi,'VIT').replace(/bInt/gi,'INT').replace(/bDex/gi,'DEX').replace(/bLuk/gi,'LUK')||'—';}
    fetchCards(root);
    root.className+=' tr-app';root.innerHTML='<div class="tr-shell"><div class="tr-head"><div class="tr-kicker">◇ Banco de Dados</div><h2 class="tr-title">Todas as Cartas Balanceadas</h2><div class="tr-sub">Pesquise por nome da carta, ID, monstro, slot, efeito antigo, efeito novo, bônus de coleção ou script de coleção.</div></div><div class="tc-toolbar"><div class="tc-top"><div class="tc-search-wrap"><span>🔍</span><input id="tc-search" class="tc-search" placeholder="Buscar carta, ID, monstro, efeito ou coleção..."></div><label class="tc-toggle"><input id="tc-changed" type="checkbox"> ⚡ Apenas alteradas</label><div id="tc-count" class="tr-pill">— cartas</div></div><div id="tc-tabs" class="tc-tabs"></div></div><div class="tc-table"><div class="tc-head-row"><div class="tc-th" data-sort="id">ID ↕</div><div class="tc-th" data-sort="name">Carta / Monstro ↕</div><div class="tc-th" data-sort="slot">Slot ↕</div><div class="tc-th" data-sort="category">Categoria ↕</div><div class="tc-th">Status</div></div><div id="tc-body"><div class="tc-msg">Carregando cartas...</div></div></div><div class="tc-groups"><div class="tr-head" style="margin-top:26px"><div class="tr-kicker">◇ Grupos de Coleção</div><h2 class="tr-title" style="font-size:1.35rem">Bônus permanentes por grupo</h2><div class="tr-sub">Dados carregados da tabela <strong>wiki_card_groups</strong>.</div></div><div id="tc-groups"></div></div></div>';
  }
     var tabs=root.querySelector('#tc-tabs');for(var i=0;i<cats.length;i++)(function(c){var b=document.createElement('button');b.className='tr-btn'+(c[0]===state.cat?' active':'');b.innerHTML=esc(c[2])+' '+esc(c[1]);b.onclick=function(){state.cat=c[0];fetchCards();};tabs.appendChild(b);})(cats[i]);root.querySelector('#tc-search').oninput=debounce(function(){state.q=this.value;fetchCards();},220);root.querySelector('#tc-changed').onchange=function(){state.changed=this.checked;fetchCards();};var hs=root.querySelectorAll('.tc-th[data-sort]');for(i=0;i<hs.length;i++)hs[i].onclick=function(){var s=this.getAttribute('data-sort');if(state.sort===s)state.dir=state.dir==='asc'?'desc':'asc';else{state.sort=s;state.dir='asc';}fetchCards();};function fetchCards(){var bs=root.querySelectorAll('#tc-tabs .tr-btn');for(var b=0;b<bs.length;b++)bs[b].className='tr-btn'+(cats[b][0]===state.cat?' active':'');state.load=true;render();var q='q='+encodeURIComponent(state.q)+'&category='+state.cat+'&changed='+(state.changed?'1':'0')+'&sort='+state.sort+'&dir='+state.dir;ajax(state.api+(state.api.indexOf('?')===-1?'?':'&')+q,function(d){state.load=false;if(!d||!d.ok){state.err=(d&&d.error)||'Erro desconhecido';state.cards=[];state.groups=[];}else{state.err='';state.cards=d.cards||[];state.groups=d.groups||[];}render();},function(e){state.load=false;state.err=e;state.cards=[];state.groups=[];render();});}function render(){var body=root.querySelector('#tc-body'),count=root.querySelector('#tc-count');count.innerHTML=state.cards.length+' carta'+(state.cards.length===1?'':'s');if(state.load){body.innerHTML='<div class="tc-msg">Carregando cartas...</div>';return;}if(state.err){body.innerHTML='<div class="tc-msg tc-error">Erro: '+esc(state.err)+'</div>';return;}if(!state.cards.length){body.innerHTML='<div class="tc-msg">Nenhuma carta encontrada.</div>';return;}body.innerHTML='';for(var i=0;i<state.cards.length;i++)(function(card){var m=meta(card.category),row=document.createElement('div'),det=document.createElement('div');row.className='tc-row';row.innerHTML='<div class="tc-cell tc-id">#'+esc(card.card_id)+'</div><div class="tc-cell"><div class="tc-name"><div class="tc-icon"><img src="https://timero.com.br/images/item/icons/'+esc(card.card_id)+'.png"></div><div><div class="tc-card-title">'+esc(card.card_name)+'</div>'+(card.monster_name?'<div class="tc-small">'+esc(card.monster_name)+'</div>':'')+'</div></div></div><div class="tc-cell"><span class="tr-pill">'+esc(card.slot_type||'Unknown')+'</span></div><div class="tc-cell"><span class="tr-pill" style="color:'+m[3]+';border-color:'+m[3]+'44">'+m[2]+' '+esc(card.collection_group_name||'—')+'</span></div><div class="tc-cell">'+(String(card.changed)==='1'?'<span class="tc-status tc-changed">Alterada</span>':'<span class="tc-status tc-original">Original</span>')+'</div>';var img=row.querySelector('img');img.onerror=function(){this.parentNode.innerHTML='🃏';};det.className='tc-detail';det.innerHTML='<div class="tc-detail-grid"><div class="tc-box"><h4>Efeito Antigo</h4><div>'+esc(card.old_effect||'—')+'</div></div><div class="tc-box"><h4>Efeito Novo</h4><div>'+esc(card.new_effect||'—')+'</div></div><div class="tc-box"><h4>Coleção</h4><div><b style="color:#f0c840">Grupo:</b> '+esc(card.collection_group_name||'—')+'<br><b style="color:#f0c840">Pontos:</b> '+esc(card.collection_points||0)+'<br><b style="color:#f0c840">Efeito:</b> '+esc(card.collection_effect||'—')+'<br><b style="color:#f0c840">Script:</b> '+esc(readScript(card.collection_script))+'</div></div></div>';row.onclick=function(){var open=det.className.indexOf('open')>-1,ds=body.querySelectorAll('.tc-detail.open'),rs=body.querySelectorAll('.tc-row.open');for(var a=0;a<ds.length;a++)ds[a].className='tc-detail';for(var r=0;r<rs.length;r++)rs[r].className='tc-row';if(!open){det.className='tc-detail open';row.className='tc-row open';}};body.appendChild(row);body.appendChild(det);})(state.cards[i]);var gw=root.querySelector('#tc-groups');gw.innerHTML='';if(!state.groups.length){gw.innerHTML='<div class="tc-msg">Nenhum grupo de coleção cadastrado.</div>';return;}for(i=0;i<state.groups.length;i++){var g=state.groups[i],c=g.color||'#7c6aff',el=document.createElement('div');el.className='tc-group';el.style.borderColor=c+'33';el.innerHTML='<div class="tc-group-main"><div class="tc-group-icon" style="background:'+c+'14;border:1px solid '+c+'44">'+esc(g.icon||'🃏')+'</div><div><div class="tc-group-key" style="color:'+c+'">Grupo · '+esc(g.group_key)+'</div><div class="tc-group-name">'+esc(g.group_name)+'</div></div><div class="tr-pill" style="color:'+c+';border-color:'+c+'33">'+esc(g.card_count||0)+' cartas · Min '+esc(g.min_cards||1)+'</div></div><div class="tc-group-bonus"><b style="color:'+c+'">Bônus:</b> '+esc(g.collection_effect||'—')+'</div>';gw.appendChild(el);}}fetchCards();}


   if (document.readyState === 'loading') {
   /* Leveling app */
     document.addEventListener('DOMContentLoaded', initCardDb);
  function initLeveling(){var root=document.getElementById('timero-leveling-guide-app');if(!root||root.getAttribute('data-ready')==='1')return;root.setAttribute('data-ready','1');injectSharedCss();var cls=[['all','Todas','◆','#58d7ff'],['swordsman','Espadachim','⚔️','#ff6b7a'],['mage','Mago','🔮','#b06cff'],['archer','Arqueiro','🏹','#00ff88'],['acolyte','Acolyte','✨','#f9a826'],['thief','Ladrão','🗡️','#7a90b0']],notes={swordsman:['⚔️','Dica: Espadachim','Priorize sobrevivência e dano consistente.'],mage:['🔮','Dica: Mago','Controle distância, elemento e consumo de SP.'],archer:['🏹','Dica: Arqueiro','Use vantagem de alcance e kite.'],acolyte:['✨','Dica: Acolyte','Heal contra Undead pode ser muito eficiente.'],thief:['🗡️','Dica: Ladrão','AGI e evasão tornam a progressão mais segura.']},ph=[['phase-1','FASE 1','Nível 1 → 25','~XX–YY minutos','🌿','#00ff88','Primeiros Passos — Poring Island & Prontera Field'],['phase-2','FASE 2','Nível 25 → 50','~XX–YY horas','⚡','#00d4ff','Construindo Momentum — Mapas Intermediários'],['phase-3','FASE 3','Nível 50 → 70','~XX–YY horas','🔥','#f9a826','Mid-Game Eficiente — Zonas de Farming'],['phase-4','FASE 4','Nível 70 → 85','~XX–YY horas','⚔️','#b06cff','Pré-Endgame — Transições e Dungeons'],['phase-5','FASE FINAL','Nível 85 → 99','~XX–YY horas','🏁','#ff3d5a','Reta Final — Caminho ao 99']];function phase(p,i){var c=p[5],maps='',mobs='',checks='',j;for(j=0;j<(i<2?3:2);j++)maps+='<div class="tl-card tl-map"><div><div class="tl-name">Mapa Fase '+(i+1)+' · '+(j+1)+'</div><div class="tl-muted">mapa_code</div></div><div><div class="tl-level">Nv. '+(i*20+1)+'–'+(i*20+20)+'</div><div class="tl-muted">Rota recomendada</div></div></div>';for(j=0;j<(i===0?2:1);j++)mobs+='<div class="tl-card tl-mob"><div class="tl-mob-main"><div class="tl-mob-icon">'+p[4]+'</div><div class="tl-mob-info"><div class="tl-name">Monstro Fase '+(i+1)+' · '+String.fromCharCode(65+j)+'</div><div class="tl-muted">Substitua pelo monstro real</div></div><div class="tl-level">EXP: ???</div><div>▼</div></div><div class="tl-detail">HP: ??? · Drops: Item 1, Item 2<br><span style="color:'+c+'">Dica:</span> Adicione dicas de combate aqui.</div></div>';var list=['Confirmar rota de leveling','Comprar consumíveis necessários','Atualizar equipamentos principais','Avançar para a próxima fase'];for(j=0;j<list.length;j++)checks+='<div class="tl-card tl-check"><div class="tl-mark"></div><div class="tl-text">'+list[j]+'</div></div>';return '<section id="'+p[0]+'" class="tl-phase" style="--color:'+c+';--soft:'+rgba(c,.08)+';--line:'+rgba(c,.22)+'"><div class="tl-head"><div class="tl-icon">'+p[4]+'</div><div><span class="tl-chip">'+p[1]+'</span><span class="tl-chip">'+p[2]+'</span><span class="tl-chip">⏱ '+p[3]+'</span><h2>'+p[6]+'</h2><p>Substitua com a descrição real da fase, mapas ideais, monstros principais, consumíveis e momento correto de troca de classe.</p></div></div><div class="tl-grid"><div class="tl-box"><div class="tl-box-head">🗺️ Mapas Recomendados</div><div class="tl-box-body">'+maps+'</div></div><div class="tl-box"><div class="tl-box-head">🐉 Monstros-Alvo</div><div class="tl-box-body">'+mobs+'</div></div></div><div class="tl-box"><div class="tl-box-head">✅ Checklist da '+p[1]+'</div><div class="tl-box-body"><div class="tl-checks">'+checks+'</div></div></div><div class="tl-note"></div><div class="tl-footer">'+(i>0?'<button class="tr-btn" data-scroll="'+ph[i-1][0]+'">← '+ph[i-1][1]+'</button>':'<span></span>')+(i<ph.length-1?'<button class="tr-btn active" data-scroll="'+ph[i+1][0]+'">Próxima Fase: '+ph[i+1][2]+' →</button>':'<button class="tr-btn active" data-scroll="timero-leveling-guide-app">Voltar ao topo ↑</button>')+'</div></section>';}
  } else {
     root.className+=' tr-app';var filters='',nav='',blocks='',i;for(i=0;i<cls.length;i++)filters+='<button class="tr-btn'+(i===0?' active':'')+'" data-class="'+cls[i][0]+'">'+cls[i][2]+' '+cls[i][1]+'</button>';for(i=0;i<ph.length;i++){nav+='<button class="tr-btn'+(i===0?' active':'')+'" data-scroll="'+ph[i][0]+'">'+ph[i][1]+' · '+ph[i][2]+'</button>';blocks+=phase(ph[i],i);}root.innerHTML='<div class="tl-root"><div class="tl-inner"><header class="tl-hero"><div class="tr-kicker">// Guia de Progressão</div><h1>Guia de Leveling<br><span>para Iniciantes</span></h1><p class="tr-sub">Do nível 1 ao 99 — rotas otimizadas, mapas ideais por fase, dicas de consumíveis e estratégias para cada etapa da progressão no TimeRO.</p></header><div class="tl-stats"><div class="tl-stat"><strong>5</strong><span>Fases</span></div><div class="tl-stat"><strong>10+</strong><span>Mapas</span></div><div class="tl-stat"><strong>98</strong><span>Níveis</span></div><div class="tl-stat"><strong>Solo</strong><span>Estilo</span></div><div class="tl-stat"><strong>Pre-R</strong><span>Modalidade</span></div></div><div class="tl-panel"><div class="tl-label">Filtrar por classe</div><div class="tl-flex">'+filters+'</div></div><div class="tl-panel"><div class="tl-label">Progressão de Níveis</div><div class="tl-track"><span></span><span></span><span></span><span></span><span></span></div><div class="tl-flex">'+nav+'</div></div>'+blocks+'<div class="tl-related"><div class="tl-panel">🧰<b>Random Options</b></div><div class="tl-panel">🏰<b>Instances</b></div><div class="tl-panel">🔥<b>Fever System</b></div><div class="tl-panel">⏱️<b>MVP Timer</b></div></div></div></div>';var sc=root.querySelectorAll('[data-scroll]');for(i=0;i<sc.length;i++)sc[i].onclick=function(){var t=document.getElementById(this.getAttribute('data-scroll'));if(t)t.scrollIntoView({behavior:'smooth',block:'start'});};var mb=root.querySelectorAll('.tl-mob');for(i=0;i<mb.length;i++)mb[i].onclick=function(){this.className=this.className.indexOf('open')<0?this.className+' open':this.className.replace(' open','');};var ch=root.querySelectorAll('.tl-check');for(i=0;i<ch.length;i++)ch[i].onclick=function(){var on=this.className.indexOf('checked')<0,m=this.querySelector('.tl-mark');this.className=on?this.className+' checked':this.className.replace(' checked','');if(m)m.innerHTML=on?'✓':'';};var cb=root.querySelectorAll('[data-class]');for(i=0;i<cb.length;i++)cb[i].onclick=function(){var v=this.getAttribute('data-class'),j;for(j=0;j<cb.length;j++){cb[j].className='tr-btn'+(cb[j]===this?' active':'');cb[j].style.opacity=(v!=='all'&&cb[j]!==this)?.45:1;}var nt=root.querySelectorAll('.tl-note');for(j=0;j<nt.length;j++){if(v==='all'||!notes[v]){nt[j].style.display='none';nt[j].innerHTML='';}else{var n=notes[v],color='#58d7ff';for(var k=0;k<cls.length;k++)if(cls[k][0]===v)color=cls[k][3];nt[j].style.display='block';nt[j].style.border='1px solid '+rgba(color,.24);nt[j].style.background=rgba(color,.07);nt[j].innerHTML='<strong style="color:'+color+'">'+n[0]+' '+n[1]+'</strong>'+esc(n[2]);}}};}
    initCardDb();
  }


   if (window.mw && mw.hook) {
  function boot(){initCards();initLeveling();}
    mw.hook('wikipage.content').add(initCardDb);
  if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',boot);else boot();
  }
   if(window.mw&&mw.hook)mw.hook('wikipage.content').add(boot);
})();
})();
if (mw && mw.hook) {
mw.hook('wikipage.content').add(function ($content) {
init(($content && $content[0]) || document);
});
}
})(jQuery, mediaWiki);

Revision as of 16:03, 30 April 2026

/* TimeRO MediaWiki:Common.js
   Fixes Card Database and Beginner Leveling Guide loaders.
   ES5-safe: no const, let, arrow functions, template literals or nullish coalescing. */
(function () {
  'use strict';

  function esc(v){return String(v==null?'':v).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#039;');}
  function style(id,css){if(document.getElementById(id))return;var s=document.createElement('style');s.id=id;s.appendChild(document.createTextNode(css));document.head.appendChild(s);}
  function ajax(url,ok,fail){var x=new XMLHttpRequest();x.open('GET',url,true);x.setRequestHeader('Accept','application/json');x.onreadystatechange=function(){if(x.readyState!==4)return;if(x.status<200||x.status>=300){fail('HTTP '+x.status+': '+x.responseText.substr(0,240));return;}try{ok(JSON.parse(x.responseText));}catch(e){fail('JSON inválido: '+x.responseText.substr(0,240));}};x.onerror=function(){fail('Falha de rede.');};x.send(null);}
  function debounce(fn,ms){var t;return function(){var a=arguments,self=this;clearTimeout(t);t=setTimeout(function(){fn.apply(self,a);},ms);};}
  function rgba(hex,a){var h=String(hex||'#58d7ff').replace('#',''),n=parseInt(h,16);return 'rgba('+((n>>16)&255)+','+((n>>8)&255)+','+(n&255)+','+a+')';}

  function injectSharedCss(){style('timero-apps-fixed-css',
    '.tr-app,.tr-app *{box-sizing:border-box}.tr-app{font-family:Segoe UI,system-ui,sans-serif;color:#d8ecff}.tr-shell{background:radial-gradient(circle at 18% 0%,rgba(74,144,217,.10),transparent 34%),radial-gradient(circle at 88% 24%,rgba(92,70,180,.08),transparent 30%),linear-gradient(135deg,rgba(5,10,22,.98),rgba(2,5,14,.99));border:1px solid rgba(80,170,255,.18);border-radius:18px;padding:24px;margin:0 0 34px;box-shadow:0 0 30px rgba(0,0,0,.35),inset 0 0 28px rgba(80,160,255,.025)}'+
    '.tr-head{background:linear-gradient(90deg,rgba(74,144,217,.14),rgba(74,144,217,.04),transparent);border:1px solid rgba(74,144,217,.18);border-left:5px solid #4a90d9;border-radius:12px;padding:16px 18px;margin:0 0 18px}.tr-kicker{color:#58d7ff;font-weight:900;letter-spacing:.10em;text-transform:uppercase;font-size:.78rem;margin-bottom:8px}.tr-title{color:#fff;font-size:clamp(1.4rem,2.6vw,2rem);font-weight:900;margin:0 0 6px;line-height:1.1}.tr-sub{color:rgba(180,205,230,.72);font-size:.92rem;line-height:1.65}.tr-btn{border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.04);color:rgba(180,205,230,.72);border-radius:999px;padding:8px 13px;cursor:pointer;font-family:inherit;font-weight:800;font-size:.78rem}.tr-btn.active{background:linear-gradient(135deg,rgba(74,144,217,.22),rgba(74,144,217,.08));border-color:rgba(88,215,255,.30);color:#58d7ff}.tr-pill{display:inline-flex;align-items:center;justify-content:center;padding:6px 10px;border-radius:999px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);font-weight:800;font-size:.76rem;white-space:nowrap}'+
    '.tc-toolbar{background:linear-gradient(135deg,rgba(8,14,26,.94),rgba(4,8,18,.99));border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:16px;margin:0 0 16px}.tc-top{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.tc-search-wrap{flex:1;min-width:260px;position:relative}.tc-search-wrap span{position:absolute;left:13px;top:50%;transform:translateY(-50%);opacity:.55}.tc-search{width:100%;padding:11px 12px 11px 40px;border-radius:11px;background:rgba(0,0,0,.34);border:1px solid rgba(255,255,255,.09);color:#e8eef8;font-size:.92rem;outline:none}.tc-toggle{display:inline-flex;gap:9px;align-items:center;cursor:pointer;padding:10px 14px;border-radius:11px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);font-weight:800}.tc-tabs{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}.tc-table{border:1px solid rgba(255,255,255,.08);border-radius:14px;overflow:hidden;background:rgba(3,6,14,.96)}.tc-head-row,.tc-row{display:grid;grid-template-columns:88px 1.35fr 150px 145px 120px;align-items:center}.tc-head-row{background:rgba(0,0,0,.42);border-bottom:1px solid rgba(255,255,255,.06)}.tc-th{padding:12px 14px;color:#6070a0;font-weight:900;letter-spacing:.08em;text-transform:uppercase;font-size:.68rem;border-right:1px solid rgba(255,255,255,.045);cursor:pointer}.tc-row{border-bottom:1px solid rgba(255,255,255,.045);cursor:pointer}.tc-row:hover{background:rgba(74,144,217,.06)}.tc-cell{padding:13px 14px;border-right:1px solid rgba(255,255,255,.035)}.tc-id{font-family:Courier New,monospace;color:#f0c840;font-weight:900}.tc-name{display:flex;align-items:center;gap:12px}.tc-icon{width:42px;height:42px;border-radius:10px;background:rgba(0,0,0,.28);border:1px solid rgba(255,255,255,.08);display:flex;align-items:center;justify-content:center;overflow:hidden;flex:0 0 42px}.tc-icon img{max-width:36px;max-height:36px}.tc-card-title{color:#58d7ff;font-weight:900}.tc-small{color:rgba(180,205,230,.58);font-size:.76rem;margin-top:3px}.tc-status{display:inline-flex;padding:6px 10px;border-radius:999px;font-size:.72rem;font-weight:900}.tc-changed{background:rgba(112,216,144,.10);border:1px solid rgba(112,216,144,.24);color:#70d890}.tc-original{background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.08);color:rgba(180,205,230,.55)}.tc-detail{display:none;background:rgba(0,0,0,.18);border-bottom:1px solid rgba(255,255,255,.055)}.tc-detail.open{display:block}.tc-detail-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;padding:16px}.tc-box{border-radius:12px;padding:14px;background:linear-gradient(135deg,rgba(8,14,26,.96),rgba(4,8,18,.99));border:1px solid rgba(255,255,255,.08);min-height:112px}.tc-box h4{color:#6070a0;font-weight:900;letter-spacing:.08em;text-transform:uppercase;font-size:.68rem;margin:0 0 8px}.tc-box div{color:rgba(220,235,255,.78);font-size:.84rem;line-height:1.65;white-space:pre-wrap}.tc-msg{padding:34px;text-align:center;color:rgba(180,205,230,.58);font-weight:700}.tc-error{color:#ff8a8a}.tc-groups{margin-top:26px}.tc-group{border-radius:14px;border:1px solid rgba(255,255,255,.08);background:linear-gradient(135deg,rgba(8,14,26,.94),rgba(4,8,18,.99));overflow:hidden;margin-bottom:10px}.tc-group-main{padding:15px 18px;display:grid;grid-template-columns:auto 1fr auto;gap:13px;align-items:center}.tc-group-icon{width:42px;height:42px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:1.25rem}.tc-group-key{font-size:.64rem;font-weight:900;letter-spacing:.11em;text-transform:uppercase;margin-bottom:2px}.tc-group-name{color:#fff;font-weight:900}.tc-group-bonus{padding:11px 18px;border-top:1px solid rgba(255,255,255,.055);color:rgba(220,235,255,.74);line-height:1.55;font-size:.84rem}'+
    '.tl-root{margin:-1em -1.5em;min-height:100vh;background:radial-gradient(circle at 18% 0%,rgba(88,215,255,.09),transparent 34%),radial-gradient(circle at 88% 24%,rgba(176,108,255,.08),transparent 30%),linear-gradient(135deg,#050914,#02050e);overflow:hidden}.tl-inner{max-width:1280px;margin:0 auto;padding:44px 28px 60px}.tl-hero{background:linear-gradient(135deg,rgba(8,14,26,.94),rgba(4,8,18,.99));border:1px solid rgba(88,215,255,.18);border-radius:18px;padding:34px;margin-bottom:18px}.tl-hero h1{font-size:clamp(2.2rem,5vw,4rem);line-height:.96;font-weight:900;color:#fff;letter-spacing:-.035em;margin:0 0 16px}.tl-hero h1 span{background:linear-gradient(90deg,#00ff88,#58d7ff,#f9a826);-webkit-background-clip:text;background-clip:text;color:transparent}.tl-stats,.tl-grid,.tl-checks,.tl-related{display:grid;gap:12px}.tl-stats{grid-template-columns:repeat(5,1fr);margin:18px 0}.tl-stat{background:rgba(0,0,0,.24);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:14px;text-align:center}.tl-stat strong{display:block;font-size:1.35rem;color:#58d7ff;font-weight:900}.tl-stat span{font-size:.72rem;color:rgba(122,144,176,.72);letter-spacing:.12em;text-transform:uppercase;font-weight:800}.tl-panel{background:linear-gradient(135deg,rgba(8,14,26,.94),rgba(4,8,18,.99));border:1px solid rgba(255,255,255,.08);border-radius:18px;padding:16px;margin-bottom:18px}.tl-label{color:#7a90b0;font-weight:900;letter-spacing:.11em;text-transform:uppercase;font-size:.72rem;margin-bottom:10px}.tl-flex{display:flex;gap:9px;flex-wrap:wrap}.tl-track{height:12px;border-radius:999px;background:rgba(255,255,255,.05);overflow:hidden;display:grid;grid-template-columns:24.5fr 24.5fr 20fr 15fr 16fr;margin-bottom:14px}.tl-track span:nth-child(1){background:#00ff88}.tl-track span:nth-child(2){background:#00d4ff}.tl-track span:nth-child(3){background:#f9a826}.tl-track span:nth-child(4){background:#b06cff}.tl-track span:nth-child(5){background:#ff3d5a}.tl-phase{background:linear-gradient(135deg,rgba(8,14,26,.94),rgba(4,8,18,.99));border:1px solid var(--line);border-radius:18px;padding:24px;margin-bottom:22px}.tl-head{display:flex;gap:18px;margin-bottom:22px}.tl-icon{width:64px;height:64px;border-radius:18px;display:flex;align-items:center;justify-content:center;flex:0 0 64px;font-size:1.8rem;background:var(--soft);border:1px solid var(--line)}.tl-chip{display:inline-flex;margin:0 8px 8px 0;padding:3px 10px;border-radius:999px;background:var(--soft);border:1px solid var(--line);color:var(--color);font-size:.65rem;letter-spacing:.13em;text-transform:uppercase;font-weight:900}.tl-phase h2{color:#fff;margin:0 0 8px;font-size:clamp(1.35rem,2.5vw,2rem);border:0}.tl-phase p{color:rgba(176,192,224,.75);line-height:1.7;margin:0}.tl-grid{grid-template-columns:1fr 1fr;margin-bottom:18px}.tl-box{border-radius:16px;overflow:hidden;background:rgba(0,0,0,.24);border:1px solid var(--line)}.tl-box-head{padding:13px 16px;background:var(--soft);border-bottom:1px solid var(--line);color:var(--color);font-weight:900;letter-spacing:.08em;text-transform:uppercase;font-size:.78rem}.tl-box-body{padding:15px;display:flex;flex-direction:column;gap:10px}.tl-card{border-radius:12px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);padding:12px 14px}.tl-map{display:flex;justify-content:space-between;gap:14px}.tl-name{color:#fff;font-size:.95rem;font-weight:900}.tl-muted{color:rgba(122,144,176,.72);font-size:.76rem;margin-top:3px}.tl-level{color:var(--color);font-size:.78rem;font-weight:900;white-space:nowrap;text-align:right}.tl-mob{cursor:pointer}.tl-mob-main{display:flex;align-items:center;gap:12px}.tl-mob-icon{width:42px;height:42px;border-radius:10px;display:flex;align-items:center;justify-content:center;background:var(--soft);border:1px solid var(--line);font-size:1.2rem}.tl-mob-info{flex:1}.tl-detail{display:none;padding:10px 0 0 54px;color:rgba(180,205,230,.78);font-size:.82rem;line-height:1.6}.tl-mob.open .tl-detail{display:block}.tl-checks{grid-template-columns:1fr 1fr}.tl-check{display:flex;align-items:center;gap:10px;cursor:pointer}.tl-mark{width:18px;height:18px;border-radius:5px;border:1px solid var(--line);background:var(--soft);display:flex;align-items:center;justify-content:center;color:var(--color);font-weight:900}.tl-check.checked .tl-text{text-decoration:line-through;opacity:.55}.tl-note{display:none;margin-top:14px;border-radius:12px;padding:14px 16px;color:rgba(220,235,255,.78);line-height:1.65;font-size:.88rem}.tl-note strong{display:block;margin-bottom:5px;letter-spacing:.09em;text-transform:uppercase;font-size:.75rem}.tl-footer{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-top:20px}.tl-related{grid-template-columns:repeat(4,1fr);margin-top:24px}.tl-related .tl-panel{text-align:center;margin:0}@media(max-width:980px){.tl-stats,.tl-grid,.tl-checks,.tl-related{grid-template-columns:1fr}.tl-inner{padding:26px 14px 44px}.tl-hero,.tl-phase{padding:20px}.tl-head{flex-direction:column}.tl-map{flex-direction:column}.tl-level{text-align:left}}');}

  /* Card app */
  function initCards(){var root=document.getElementById('timero-card-db-app');if(!root||root.getAttribute('data-ready')==='1')return;root.setAttribute('data-ready','1');injectSharedCss();var state={api:root.getAttribute('data-api')||'/pt/api/wiki_cards.php',q:'',cat:'all',changed:false,sort:'card_name',dir:'asc',cards:[],groups:[],err:'',load:false};var cats=[['all','Todas','◆','#7c6aff'],['weapon','Armas','⚔️','#ff6b7a'],['armor','Armaduras','🛡','#00d4ff'],['accessory','Acessórios','💎','#f9a826'],['headgear','Headgear','🎩','#b06cff'],['shield','Escudos','🛡️','#70b8ff'],['garment','Capas','🧥','#60d090'],['shoes','Sapatos','👢','#f0c840']];function meta(id){for(var i=0;i<cats.length;i++)if(cats[i][0]===id)return cats[i];return cats[0];}function readScript(s){return String(s||'').replace(/bonus\s+/gi,'').replace(/;/g,'; ').replace(/bAllStats/gi,'All Stats').replace(/bMaxHP/gi,'Max HP').replace(/bMaxSP/gi,'Max SP').replace(/bBaseAtk/gi,'ATK').replace(/bMatk/gi,'MATK').replace(/bStr/gi,'STR').replace(/bAgi/gi,'AGI').replace(/bVit/gi,'VIT').replace(/bInt/gi,'INT').replace(/bDex/gi,'DEX').replace(/bLuk/gi,'LUK')||'—';}
    root.className+=' tr-app';root.innerHTML='<div class="tr-shell"><div class="tr-head"><div class="tr-kicker">◇ Banco de Dados</div><h2 class="tr-title">Todas as Cartas Balanceadas</h2><div class="tr-sub">Pesquise por nome da carta, ID, monstro, slot, efeito antigo, efeito novo, bônus de coleção ou script de coleção.</div></div><div class="tc-toolbar"><div class="tc-top"><div class="tc-search-wrap"><span>🔍</span><input id="tc-search" class="tc-search" placeholder="Buscar carta, ID, monstro, efeito ou coleção..."></div><label class="tc-toggle"><input id="tc-changed" type="checkbox"> ⚡ Apenas alteradas</label><div id="tc-count" class="tr-pill">— cartas</div></div><div id="tc-tabs" class="tc-tabs"></div></div><div class="tc-table"><div class="tc-head-row"><div class="tc-th" data-sort="id">ID ↕</div><div class="tc-th" data-sort="name">Carta / Monstro ↕</div><div class="tc-th" data-sort="slot">Slot ↕</div><div class="tc-th" data-sort="category">Categoria ↕</div><div class="tc-th">Status</div></div><div id="tc-body"><div class="tc-msg">Carregando cartas...</div></div></div><div class="tc-groups"><div class="tr-head" style="margin-top:26px"><div class="tr-kicker">◇ Grupos de Coleção</div><h2 class="tr-title" style="font-size:1.35rem">Bônus permanentes por grupo</h2><div class="tr-sub">Dados carregados da tabela <strong>wiki_card_groups</strong>.</div></div><div id="tc-groups"></div></div></div>';
    var tabs=root.querySelector('#tc-tabs');for(var i=0;i<cats.length;i++)(function(c){var b=document.createElement('button');b.className='tr-btn'+(c[0]===state.cat?' active':'');b.innerHTML=esc(c[2])+' '+esc(c[1]);b.onclick=function(){state.cat=c[0];fetchCards();};tabs.appendChild(b);})(cats[i]);root.querySelector('#tc-search').oninput=debounce(function(){state.q=this.value;fetchCards();},220);root.querySelector('#tc-changed').onchange=function(){state.changed=this.checked;fetchCards();};var hs=root.querySelectorAll('.tc-th[data-sort]');for(i=0;i<hs.length;i++)hs[i].onclick=function(){var s=this.getAttribute('data-sort');if(state.sort===s)state.dir=state.dir==='asc'?'desc':'asc';else{state.sort=s;state.dir='asc';}fetchCards();};function fetchCards(){var bs=root.querySelectorAll('#tc-tabs .tr-btn');for(var b=0;b<bs.length;b++)bs[b].className='tr-btn'+(cats[b][0]===state.cat?' active':'');state.load=true;render();var q='q='+encodeURIComponent(state.q)+'&category='+state.cat+'&changed='+(state.changed?'1':'0')+'&sort='+state.sort+'&dir='+state.dir;ajax(state.api+(state.api.indexOf('?')===-1?'?':'&')+q,function(d){state.load=false;if(!d||!d.ok){state.err=(d&&d.error)||'Erro desconhecido';state.cards=[];state.groups=[];}else{state.err='';state.cards=d.cards||[];state.groups=d.groups||[];}render();},function(e){state.load=false;state.err=e;state.cards=[];state.groups=[];render();});}function render(){var body=root.querySelector('#tc-body'),count=root.querySelector('#tc-count');count.innerHTML=state.cards.length+' carta'+(state.cards.length===1?'':'s');if(state.load){body.innerHTML='<div class="tc-msg">Carregando cartas...</div>';return;}if(state.err){body.innerHTML='<div class="tc-msg tc-error">Erro: '+esc(state.err)+'</div>';return;}if(!state.cards.length){body.innerHTML='<div class="tc-msg">Nenhuma carta encontrada.</div>';return;}body.innerHTML='';for(var i=0;i<state.cards.length;i++)(function(card){var m=meta(card.category),row=document.createElement('div'),det=document.createElement('div');row.className='tc-row';row.innerHTML='<div class="tc-cell tc-id">#'+esc(card.card_id)+'</div><div class="tc-cell"><div class="tc-name"><div class="tc-icon"><img src="https://timero.com.br/images/item/icons/'+esc(card.card_id)+'.png"></div><div><div class="tc-card-title">'+esc(card.card_name)+'</div>'+(card.monster_name?'<div class="tc-small">'+esc(card.monster_name)+'</div>':'')+'</div></div></div><div class="tc-cell"><span class="tr-pill">'+esc(card.slot_type||'Unknown')+'</span></div><div class="tc-cell"><span class="tr-pill" style="color:'+m[3]+';border-color:'+m[3]+'44">'+m[2]+' '+esc(card.collection_group_name||'—')+'</span></div><div class="tc-cell">'+(String(card.changed)==='1'?'<span class="tc-status tc-changed">Alterada</span>':'<span class="tc-status tc-original">Original</span>')+'</div>';var img=row.querySelector('img');img.onerror=function(){this.parentNode.innerHTML='🃏';};det.className='tc-detail';det.innerHTML='<div class="tc-detail-grid"><div class="tc-box"><h4>Efeito Antigo</h4><div>'+esc(card.old_effect||'—')+'</div></div><div class="tc-box"><h4>Efeito Novo</h4><div>'+esc(card.new_effect||'—')+'</div></div><div class="tc-box"><h4>Coleção</h4><div><b style="color:#f0c840">Grupo:</b> '+esc(card.collection_group_name||'—')+'<br><b style="color:#f0c840">Pontos:</b> '+esc(card.collection_points||0)+'<br><b style="color:#f0c840">Efeito:</b> '+esc(card.collection_effect||'—')+'<br><b style="color:#f0c840">Script:</b> '+esc(readScript(card.collection_script))+'</div></div></div>';row.onclick=function(){var open=det.className.indexOf('open')>-1,ds=body.querySelectorAll('.tc-detail.open'),rs=body.querySelectorAll('.tc-row.open');for(var a=0;a<ds.length;a++)ds[a].className='tc-detail';for(var r=0;r<rs.length;r++)rs[r].className='tc-row';if(!open){det.className='tc-detail open';row.className='tc-row open';}};body.appendChild(row);body.appendChild(det);})(state.cards[i]);var gw=root.querySelector('#tc-groups');gw.innerHTML='';if(!state.groups.length){gw.innerHTML='<div class="tc-msg">Nenhum grupo de coleção cadastrado.</div>';return;}for(i=0;i<state.groups.length;i++){var g=state.groups[i],c=g.color||'#7c6aff',el=document.createElement('div');el.className='tc-group';el.style.borderColor=c+'33';el.innerHTML='<div class="tc-group-main"><div class="tc-group-icon" style="background:'+c+'14;border:1px solid '+c+'44">'+esc(g.icon||'🃏')+'</div><div><div class="tc-group-key" style="color:'+c+'">Grupo · '+esc(g.group_key)+'</div><div class="tc-group-name">'+esc(g.group_name)+'</div></div><div class="tr-pill" style="color:'+c+';border-color:'+c+'33">'+esc(g.card_count||0)+' cartas · Min '+esc(g.min_cards||1)+'</div></div><div class="tc-group-bonus"><b style="color:'+c+'">Bônus:</b> '+esc(g.collection_effect||'—')+'</div>';gw.appendChild(el);}}fetchCards();}

  /* Leveling app */
  function initLeveling(){var root=document.getElementById('timero-leveling-guide-app');if(!root||root.getAttribute('data-ready')==='1')return;root.setAttribute('data-ready','1');injectSharedCss();var cls=[['all','Todas','◆','#58d7ff'],['swordsman','Espadachim','⚔️','#ff6b7a'],['mage','Mago','🔮','#b06cff'],['archer','Arqueiro','🏹','#00ff88'],['acolyte','Acolyte','✨','#f9a826'],['thief','Ladrão','🗡️','#7a90b0']],notes={swordsman:['⚔️','Dica: Espadachim','Priorize sobrevivência e dano consistente.'],mage:['🔮','Dica: Mago','Controle distância, elemento e consumo de SP.'],archer:['🏹','Dica: Arqueiro','Use vantagem de alcance e kite.'],acolyte:['✨','Dica: Acolyte','Heal contra Undead pode ser muito eficiente.'],thief:['🗡️','Dica: Ladrão','AGI e evasão tornam a progressão mais segura.']},ph=[['phase-1','FASE 1','Nível 1 → 25','~XX–YY minutos','🌿','#00ff88','Primeiros Passos — Poring Island & Prontera Field'],['phase-2','FASE 2','Nível 25 → 50','~XX–YY horas','⚡','#00d4ff','Construindo Momentum — Mapas Intermediários'],['phase-3','FASE 3','Nível 50 → 70','~XX–YY horas','🔥','#f9a826','Mid-Game Eficiente — Zonas de Farming'],['phase-4','FASE 4','Nível 70 → 85','~XX–YY horas','⚔️','#b06cff','Pré-Endgame — Transições e Dungeons'],['phase-5','FASE FINAL','Nível 85 → 99','~XX–YY horas','🏁','#ff3d5a','Reta Final — Caminho ao 99']];function phase(p,i){var c=p[5],maps='',mobs='',checks='',j;for(j=0;j<(i<2?3:2);j++)maps+='<div class="tl-card tl-map"><div><div class="tl-name">Mapa Fase '+(i+1)+' · '+(j+1)+'</div><div class="tl-muted">mapa_code</div></div><div><div class="tl-level">Nv. '+(i*20+1)+'–'+(i*20+20)+'</div><div class="tl-muted">Rota recomendada</div></div></div>';for(j=0;j<(i===0?2:1);j++)mobs+='<div class="tl-card tl-mob"><div class="tl-mob-main"><div class="tl-mob-icon">'+p[4]+'</div><div class="tl-mob-info"><div class="tl-name">Monstro Fase '+(i+1)+' · '+String.fromCharCode(65+j)+'</div><div class="tl-muted">Substitua pelo monstro real</div></div><div class="tl-level">EXP: ???</div><div>▼</div></div><div class="tl-detail">HP: ??? · Drops: Item 1, Item 2<br><span style="color:'+c+'">Dica:</span> Adicione dicas de combate aqui.</div></div>';var list=['Confirmar rota de leveling','Comprar consumíveis necessários','Atualizar equipamentos principais','Avançar para a próxima fase'];for(j=0;j<list.length;j++)checks+='<div class="tl-card tl-check"><div class="tl-mark"></div><div class="tl-text">'+list[j]+'</div></div>';return '<section id="'+p[0]+'" class="tl-phase" style="--color:'+c+';--soft:'+rgba(c,.08)+';--line:'+rgba(c,.22)+'"><div class="tl-head"><div class="tl-icon">'+p[4]+'</div><div><span class="tl-chip">'+p[1]+'</span><span class="tl-chip">'+p[2]+'</span><span class="tl-chip">⏱ '+p[3]+'</span><h2>'+p[6]+'</h2><p>Substitua com a descrição real da fase, mapas ideais, monstros principais, consumíveis e momento correto de troca de classe.</p></div></div><div class="tl-grid"><div class="tl-box"><div class="tl-box-head">🗺️ Mapas Recomendados</div><div class="tl-box-body">'+maps+'</div></div><div class="tl-box"><div class="tl-box-head">🐉 Monstros-Alvo</div><div class="tl-box-body">'+mobs+'</div></div></div><div class="tl-box"><div class="tl-box-head">✅ Checklist da '+p[1]+'</div><div class="tl-box-body"><div class="tl-checks">'+checks+'</div></div></div><div class="tl-note"></div><div class="tl-footer">'+(i>0?'<button class="tr-btn" data-scroll="'+ph[i-1][0]+'">← '+ph[i-1][1]+'</button>':'<span></span>')+(i<ph.length-1?'<button class="tr-btn active" data-scroll="'+ph[i+1][0]+'">Próxima Fase: '+ph[i+1][2]+' →</button>':'<button class="tr-btn active" data-scroll="timero-leveling-guide-app">Voltar ao topo ↑</button>')+'</div></section>';}
    root.className+=' tr-app';var filters='',nav='',blocks='',i;for(i=0;i<cls.length;i++)filters+='<button class="tr-btn'+(i===0?' active':'')+'" data-class="'+cls[i][0]+'">'+cls[i][2]+' '+cls[i][1]+'</button>';for(i=0;i<ph.length;i++){nav+='<button class="tr-btn'+(i===0?' active':'')+'" data-scroll="'+ph[i][0]+'">'+ph[i][1]+' · '+ph[i][2]+'</button>';blocks+=phase(ph[i],i);}root.innerHTML='<div class="tl-root"><div class="tl-inner"><header class="tl-hero"><div class="tr-kicker">// Guia de Progressão</div><h1>Guia de Leveling<br><span>para Iniciantes</span></h1><p class="tr-sub">Do nível 1 ao 99 — rotas otimizadas, mapas ideais por fase, dicas de consumíveis e estratégias para cada etapa da progressão no TimeRO.</p></header><div class="tl-stats"><div class="tl-stat"><strong>5</strong><span>Fases</span></div><div class="tl-stat"><strong>10+</strong><span>Mapas</span></div><div class="tl-stat"><strong>98</strong><span>Níveis</span></div><div class="tl-stat"><strong>Solo</strong><span>Estilo</span></div><div class="tl-stat"><strong>Pre-R</strong><span>Modalidade</span></div></div><div class="tl-panel"><div class="tl-label">Filtrar por classe</div><div class="tl-flex">'+filters+'</div></div><div class="tl-panel"><div class="tl-label">Progressão de Níveis</div><div class="tl-track"><span></span><span></span><span></span><span></span><span></span></div><div class="tl-flex">'+nav+'</div></div>'+blocks+'<div class="tl-related"><div class="tl-panel">🧰<b>Random Options</b></div><div class="tl-panel">🏰<b>Instances</b></div><div class="tl-panel">🔥<b>Fever System</b></div><div class="tl-panel">⏱️<b>MVP Timer</b></div></div></div></div>';var sc=root.querySelectorAll('[data-scroll]');for(i=0;i<sc.length;i++)sc[i].onclick=function(){var t=document.getElementById(this.getAttribute('data-scroll'));if(t)t.scrollIntoView({behavior:'smooth',block:'start'});};var mb=root.querySelectorAll('.tl-mob');for(i=0;i<mb.length;i++)mb[i].onclick=function(){this.className=this.className.indexOf('open')<0?this.className+' open':this.className.replace(' open','');};var ch=root.querySelectorAll('.tl-check');for(i=0;i<ch.length;i++)ch[i].onclick=function(){var on=this.className.indexOf('checked')<0,m=this.querySelector('.tl-mark');this.className=on?this.className+' checked':this.className.replace(' checked','');if(m)m.innerHTML=on?'✓':'';};var cb=root.querySelectorAll('[data-class]');for(i=0;i<cb.length;i++)cb[i].onclick=function(){var v=this.getAttribute('data-class'),j;for(j=0;j<cb.length;j++){cb[j].className='tr-btn'+(cb[j]===this?' active':'');cb[j].style.opacity=(v!=='all'&&cb[j]!==this)?.45:1;}var nt=root.querySelectorAll('.tl-note');for(j=0;j<nt.length;j++){if(v==='all'||!notes[v]){nt[j].style.display='none';nt[j].innerHTML='';}else{var n=notes[v],color='#58d7ff';for(var k=0;k<cls.length;k++)if(cls[k][0]===v)color=cls[k][3];nt[j].style.display='block';nt[j].style.border='1px solid '+rgba(color,.24);nt[j].style.background=rgba(color,.07);nt[j].innerHTML='<strong style="color:'+color+'">'+n[0]+' '+n[1]+'</strong>'+esc(n[2]);}}};}

  function boot(){initCards();initLeveling();}
  if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',boot);else boot();
  if(window.mw&&mw.hook)mw.hook('wikipage.content').add(boot);
})();