(function () {
    "use strict";

    /* ============================================================
       DEBUG MODE
       ============================================================ */
    var DEBUG = true;
    function log() {
        if (DEBUG && typeof console !== "undefined") {
            console.log.apply(console, arguments);
        }
    }
    log("App: Hardened ES5 sandbox initializing…");

    /* ============================================================
       SAFE NAMESPACE (prevents LiteSpeed collisions)
       ============================================================ */
    var App = {};
    window.__IronDungeonApp = App; // exposed only for debugging

    /* ============================================================
       DOM READY (LiteSpeed‑safe)
       ============================================================ */
    function onReady(fn) {
        if (document.readyState === "complete" || document.readyState === "interactive") {
            setTimeout(fn, 0);
        } else {
            document.addEventListener("DOMContentLoaded", fn);
        }
    }

    /* ============================================================
       XP CONFIG
       ============================================================ */
    var TASK_XP = {
        omeprazole: 5,
        setrona_morning: 5,
        acv_morning: 5,
        greens: 5,
        mushroom: 5,
        joint: 5,
        protein_morning: 5,
        water_morning: 10,
        no_doomscroll: 10,
        light_movement: 10,

        l_theanine: 5,
        breathing_break: 10,
        stress_check: 5,
        coffee_here: 5,
        setrona_midday: 5,
        acv_midday: 5,
        voltaren_midday: 5,
        manshake: 5,
        midday_walk: 10,
        protein_lunch: 10,
        step_check: 10,

        creatine: 5,
        pre_stim: 5,
        water_pre: 10,
        warmup: 10,
        energy_rating: 5,

        bcaa: 5,
        complete_workout: 10,
        track_reps: 5,
        stretch_post: 5,

        voltaren_evening: 5,
        magnesium: 5,
        inositol: 5,
        prazosin: 5,
        no_screens: 10,
        evening_breathing: 10,
        plan_meals: 10,
        sleep_goal: 10,

        protein_target: 10,
        calorie_target: 10,
        steps_target: 10,
        water_total: 10,

        no_caffeine_after_2: 10,
        breathing_total: 10,
        sleep_routine: 10
    };

    /* ============================================================
       SECTION MAPPING
       ============================================================ */
    var TASK_SECTION = {
        omeprazole: "morning",
        setrona_morning: "morning",
        acv_morning: "morning",
        greens: "morning",
        mushroom: "morning",
        joint: "morning",
        protein_morning: "morning",
        water_morning: "morning",
        no_doomscroll: "morning",
        light_movement: "morning",

        l_theanine: "midday",
        breathing_break: "midday",
        stress_check: "midday",
        coffee_here: "midday",
        setrona_midday: "midday",
        acv_midday: "midday",
        voltaren_midday: "midday",
        manshake: "midday",
        midday_walk: "midday",
        protein_lunch: "midday",
        step_check: "midday",

        creatine: "pre",
        pre_stim: "pre",
        water_pre: "pre",
        warmup: "pre",
        energy_rating: "pre",

        bcaa: "workout",
        complete_workout: "workout",
        track_reps: "workout",
        stretch_post: "workout",

        voltaren_evening: "evening",
        magnesium: "evening",
        inositol: "evening",
        prazosin: "evening",
        no_screens: "evening",
        evening_breathing: "evening",
        plan_meals: "evening",
        sleep_goal: "evening",

        protein_target: "fat",
        calorie_target: "fat",
        steps_target: "fat",
        water_total: "fat",

        no_caffeine_after_2: "anx",
        breathing_total: "anx",
        sleep_routine: "anx"
    };

    /* ============================================================
       STATE
       ============================================================ */
    var STORAGE_KEY = "iron_dungeon_paladin_state_v3";

    App.xpTotal = 0;
    App.level = 1;
    App.streak = 0;
    App.todayXP = 0;
    App.dailyPerfect = false;
    App.completedTasks = {};
    App.days = {};
    App.unlockedSkills = {};

    /* ============================================================
       HELPERS
       ============================================================ */
    function todayISO() {
        return new Date().toISOString().slice(0, 10);
    }

    function getXPToNextLevel(level) {
        if (level <= 1) return 500;
        if (level <= 2) return 1000;
        if (level <= 3) return 1500;
        if (level <= 4) return 2000;
        if (level <= 10) return 2500;
        if (level <= 20) return 3000;
        if (level <= 30) return 4000;
        if (level <= 40) return 5000;
        return 7500;
    }

    function formatDateShort(dateStr) {
        var d = new Date(dateStr + "T00:00:00");
        return d.toLocaleDateString(undefined, {
            weekday: "short",
            month: "short",
            day: "numeric"
        });
    }

    log("App: Core engine loaded.");
    /* ============================================================
       SAVE / LOAD
       ============================================================ */
    function saveState() {
        try {
            var state = {
                xpTotal: App.xpTotal,
                level: App.level,
                streak: App.streak,
                todayXP: App.todayXP,
                dailyPerfect: App.dailyPerfect,
                completedTasks: App.completedTasks,
                days: App.days,
                unlockedSkills: App.unlockedSkills,
                lastDate: todayISO()
            };
            localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
            log("App: State saved.");
        } catch (e) {
            log("App: Failed to save state", e);
        }
    }

    function loadState() {
        var raw = null;
        try {
            raw = localStorage.getItem(STORAGE_KEY);
        } catch (e) {
            log("App: Failed to read localStorage", e);
            return;
        }
        if (!raw) {
            log("App: No saved state found.");
            return;
        }

        try {
            var state = JSON.parse(raw);
            var today = todayISO();

            App.days = state.days || {};
            App.unlockedSkills = state.unlockedSkills || {};

            if (state.lastDate !== today) {
                App.xpTotal = state.xpTotal || 0;
                App.level = state.level || 1;
                App.streak = state.streak || 0;
                App.todayXP = 0;
                App.dailyPerfect = false;
                App.completedTasks = {};
                log("App: New day detected, resetting daily data.");
            } else {
                App.xpTotal = state.xpTotal || 0;
                App.level = state.level || 1;
                App.streak = state.streak || 0;
                App.todayXP = state.todayXP || 0;
                App.dailyPerfect = state.dailyPerfect || false;
                App.completedTasks = state.completedTasks || {};
                log("App: Restored same-day state.");
            }
        } catch (e2) {
            log("App: Failed to parse state", e2);
        }
    }

    /* ============================================================
       XP & LEVEL LOGIC
       ============================================================ */
    function updateLevel() {
        var xpNeeded = getXPToNextLevel(App.level);
        while (App.xpTotal >= xpNeeded && App.level < 50) {
            App.xpTotal -= xpNeeded;
            App.level++;
            xpNeeded = getXPToNextLevel(App.level);
            log("App: Level up! New level:", App.level);
        }
    }

    function addXP(amount) {
        if (!amount || amount <= 0) return;
        App.xpTotal += amount;
        App.todayXP += amount;
        updateLevel();
        updateUI();
        saveState();
        log("App: XP added:", amount, "Total XP:", App.xpTotal);
    }

    /* ============================================================
       UI UPDATE ENGINE
       ============================================================ */
    function updateUI() {
        var levelEl = document.getElementById("level");
        var xpEl = document.getElementById("xp");
        var streakEl = document.getElementById("streak");
        var xpToNextEl = document.getElementById("xp-to-next");
        var xpBarEl = document.getElementById("xp-bar");

        if (!levelEl || !xpEl || !streakEl || !xpToNextEl || !xpBarEl) {
            log("App: UI elements missing, skipping updateUI.");
            return;
        }

        levelEl.textContent = App.level;
        xpEl.textContent = App.xpTotal;
        streakEl.textContent = App.streak;

        var xpToNext = getXPToNextLevel(App.level);
        xpToNextEl.textContent = xpToNext;

        var ratio = Math.min(1, App.xpTotal / xpToNext);
        xpBarEl.style.width = (ratio * 100) + "%";

        // Update quest checkboxes
        var checkboxes = document.querySelectorAll("input[data-task-id]");
        var i, cb, id, item;
        for (i = 0; i < checkboxes.length; i++) {
            cb = checkboxes[i];
            id = cb.getAttribute("data-task-id");
            cb.checked = !!App.completedTasks[id];
            item = cb.closest(".quest-item");
            if (item) {
                if (cb.checked) {
                    item.classList.add("completed");
                } else {
                    item.classList.remove("completed");
                }
            }
        }

        renderWeeklyView();
        renderSkillTrees();
    }

    /* ============================================================
       TASK HANDLING
       ============================================================ */
    function onTaskToggle(e) {
        var cb = e.target || e.srcElement;
        if (!cb || cb.tagName !== "INPUT" || cb.type !== "checkbox") return;

        var id = cb.getAttribute("data-task-id");
        if (!id) return;

        var xp = TASK_XP[id] || 0;
        var item = cb.closest(".quest-item");

        if (cb.checked) {
            App.completedTasks[id] = true;
            if (item) item.classList.add("completed");
            addXP(xp);
        } else {
            delete App.completedTasks[id];
            if (item) item.classList.remove("completed");
            App.xpTotal -= xp;
            App.todayXP -= xp;
            if (App.xpTotal < 0) App.xpTotal = 0;
            if (App.todayXP < 0) App.todayXP = 0;
            updateLevel();
            updateUI();
            saveState();
            log("App: XP removed:", xp, "Total XP:", App.xpTotal);
        }
    }

    /* ============================================================
       DAILY PERFECT
       ============================================================ */
    var DAILY_PERFECT_BONUS = 50;

    function markDailyPerfect() {
        if (App.dailyPerfect) {
            log("App: Daily perfect already marked.");
            return;
        }
        App.dailyPerfect = true;
        App.streak++;
        addXP(DAILY_PERFECT_BONUS);
        log("App: Daily perfect marked. Streak:", App.streak);
    }

    /* ============================================================
       DAY SAVE / LEDGER
       ============================================================ */
    function computeSections(taskMap) {
        var sections = {
            morning: 0,
            midday: 0,
            pre: 0,
            workout: 0,
            evening: 0,
            fat: 0,
            anx: 0
        };

        var total = 0;
        var id, xp, sec;

        for (id in taskMap) {
            if (!taskMap.hasOwnProperty(id)) continue;
            xp = TASK_XP[id] || 0;
            sec = TASK_SECTION[id] || "morning";
            sections[sec] += xp;
            total += xp;
        }

        if (App.dailyPerfect) {
            total += DAILY_PERFECT_BONUS;
        }

        return { total: total, sections: sections };
    }

    function saveDay() {
        var date = todayISO();
        var result = computeSections(App.completedTasks);

        App.days[date] = {
            totalXP: result.total,
            sections: result.sections,
            dailyPerfect: App.dailyPerfect
        };

        saveState();
        log("App: Day saved for", date, "Total XP:", result.total);
        if (typeof alert !== "undefined") {
            alert("Day saved.");
        }
    }

    /* ============================================================
       WEEKLY LEDGER
       ============================================================ */
    function getLast7Dates() {
        var arr = [];
        var today = new Date(todayISO() + "T00:00:00");
        var i, d;

        for (i = 6; i >= 0; i--) {
            d = new Date(today);
            d.setDate(d.getDate() - i);
            arr.push(d.toISOString().slice(0, 10));
        }
        return arr;
    }

    function renderWeeklyView() {
        var tbody = document.getElementById("weekly-table-body");
        var bars = document.getElementById("weekly-bars");
        var summary = document.getElementById("weekly-summary");

        if (!tbody || !bars || !summary) {
            log("App: Weekly ledger elements missing, skipping renderWeeklyView.");
            return;
        }

        tbody.innerHTML = "";
        bars.innerHTML = "";
        summary.textContent = "";

        var dates = getLast7Dates();
        var maxXP = 0;
        var weeklyTotal = 0;
        var perfectCount = 0;
        var i, date, entry;

        var rows = [];
        for (i = 0; i < dates.length; i++) {
            date = dates[i];
            entry = App.days[date] || {
                totalXP: 0,
                sections: {
                    morning: 0,
                    midday: 0,
                    pre: 0,
                    workout: 0,
                    evening: 0,
                    fat: 0,
                    anx: 0
                },
                dailyPerfect: false
            };

            if (entry.totalXP > maxXP) maxXP = entry.totalXP;
            weeklyTotal += entry.totalXP;
            if (entry.dailyPerfect) perfectCount++;

            rows.push({ date: date, entry: entry });
        }

        // Table rows
        var tr, html;
        for (i = 0; i < rows.length; i++) {
            date = rows[i].date;
            entry = rows[i].entry;

            tr = document.createElement("tr");
            html = ""
                + "<td>" + formatDateShort(date) + "</td>"
                + "<td>" + entry.totalXP + "</td>"
                + "<td>" + entry.sections.morning + "</td>"
                + "<td>" + entry.sections.midday + "</td>"
                + "<td>" + entry.sections.pre + "</td>"
                + "<td>" + entry.sections.workout + "</td>"
                + "<td>" + entry.sections.evening + "</td>"
                + "<td>" + entry.sections.fat + "</td>"
                + "<td>" + entry.sections.anx + "</td>"
                + "<td>" + (entry.dailyPerfect ? "⭐" : "") + "</td>";

            tr.innerHTML = html;
            tbody.appendChild(tr);
        }

        // Bars
        var bar, fill, label;
        for (i = 0; i < rows.length; i++) {
            date = rows[i].date;
            entry = rows[i].entry;

            bar = document.createElement("div");
            bar.className = "ledger-bar";

            fill = document.createElement("div");
            fill.className = "ledger-bar-fill";
            fill.style.height = maxXP ? ((entry.totalXP / maxXP) * 100) + "%" : "0%";

            label = document.createElement("div");
            label.className = "ledger-bar-label";
            label.textContent = new Date(date).toLocaleDateString(undefined, {
                weekday: "short"
            });

            bar.appendChild(fill);
            bar.appendChild(label);
            bars.appendChild(bar);
        }

        summary.textContent = "Weekly total: " + weeklyTotal + " XP • Perfect days: " + perfectCount + "/7";
    }

    /* ============================================================
       SCREEN SWITCHING
       ============================================================ */
    function switchScreen(target) {
        var screens = document.querySelectorAll(".screen");
        var i, s;
        for (i = 0; i < screens.length; i++) {
            s = screens[i];
            s.classList.toggle("active", s.getAttribute("data-screen") === target);
        }

        var navBtns = document.querySelectorAll(".nav-btn");
        var btn;
        for (i = 0; i < navBtns.length; i++) {
            btn = navBtns[i];
            btn.classList.toggle("active", btn.getAttribute("data-target") === target);
        }

        log("App: Switched screen to", target);
    }

    /* ============================================================
       QUEST TAB SWITCHING
       ============================================================ */
    function switchQuestTab(tab) {
        var tabs = document.querySelectorAll(".quest-tab");
        var panels = document.querySelectorAll(".quest-panel");
        var i, el;

        for (i = 0; i < tabs.length; i++) {
            el = tabs[i];
            el.classList.toggle("active", el.getAttribute("data-tab") === tab);
        }

        for (i = 0; i < panels.length; i++) {
            el = panels[i];
            el.classList.toggle("active", el.getAttribute("data-tab-panel") === tab);
        }

        log("App: Switched quest tab to", tab);
    }
    /* ============================================================
       INVENTORY — PRODUCT LOADER
       ============================================================ */
    function loadProducts() {
        var url = "https://idontbyte.com/products/products.txt";
        log("App: Fetching products from", url);

        try {
            fetch(url)
                .then(function (res) {
                    return res.text();
                })
                .then(function (txt) {
                    log("App: Products file loaded.");
                    var products = parseProducts(txt);
                    renderProducts(products);
                })
                .catch(function (err) {
                    log("App: Failed to load products", err);
                });
        } catch (e) {
            log("App: Fetch threw an error", e);
        }
    }

    function parseProducts(text) {
        var lines = text.split("\n");
        var products = [];
        var i, line, parts;

        for (i = 0; i < lines.length; i++) {
            line = lines[i].trim();
            if (!line) continue;

            parts = line.split("|");
            if (parts.length < 3) continue;

            products.push({
                name: parts[0].trim(),
                brand: parts[1].trim(),
                type: parts[2].trim()
            });
        }

        log("App: Parsed products:", products.length);
        return products;
    }

    function renderProducts(products) {
        var container = document.getElementById("products-container");
        if (!container) {
            log("App: Inventory container missing.");
            return;
        }

        container.innerHTML = "";

        var i, p, card, html;
        for (i = 0; i < products.length; i++) {
            p = products[i];

            card = document.createElement("div");
            card.className = "loot-card";

            html = ""
                + "<div class='loot-name'>" + p.name + "</div>"
                + "<div class='loot-brand'>" + p.brand + "</div>"
                + "<div class='loot-type'>" + p.type + "</div>";

            card.innerHTML = html;
            container.appendChild(card);
        }

        log("App: Inventory rendered.");
    }

    /* ============================================================
       SKILL TREES — DEFINITIONS
       ============================================================ */
    var SKILL_TREES = {
        strength: [
            { id: "str_1", name: "Iron Grip", cost: 50 },
            { id: "str_2", name: "Hammer Blow", cost: 100 },
            { id: "str_3", name: "Titan Arms", cost: 200 }
        ],
        discipline: [
            { id: "dis_1", name: "Morning Mastery", cost: 50 },
            { id: "dis_2", name: "Focus Aura", cost: 100 },
            { id: "dis_3", name: "Unbreakable Routine", cost: 200 }
        ],
        vitality: [
            { id: "vit_1", name: "Healthy Core", cost: 50 },
            { id: "vit_2", name: "Regeneration", cost: 100 },
            { id: "vit_3", name: "Life Fountain", cost: 200 }
        ],
        willpower: [
            { id: "wil_1", name: "Calm Mind", cost: 50 },
            { id: "wil_2", name: "Resolve", cost: 100 },
            { id: "wil_3", name: "Unshakable Spirit", cost: 200 }
        ],
        endurance: [
            { id: "end_1", name: "Stamina Boost", cost: 50 },
            { id: "end_2", name: "Long March", cost: 100 },
            { id: "end_3", name: "Endless Engine", cost: 200 }
        ],
        recovery: [
            { id: "rec_1", name: "Quick Heal", cost: 50 },
            { id: "rec_2", name: "Deep Rest", cost: 100 },
            { id: "rec_3", name: "Phoenix Rise", cost: 200 }
        ]
    };

    /* ============================================================
       SKILL TREE RENDERING
       ============================================================ */
    function renderSkillTrees() {
        var trees = Object.keys(SKILL_TREES);
        var i, treeName;

        for (i = 0; i < trees.length; i++) {
            treeName = trees[i];
            renderSkillTree(treeName);
        }
    }

    function renderSkillTree(treeName) {
        var container = document.getElementById("tree-" + treeName);
        if (!container) {
            log("App: Missing skill tree container:", treeName);
            return;
        }

        container.innerHTML = "";

        var skills = SKILL_TREES[treeName];
        var i, s, card, html, unlocked;

        for (i = 0; i < skills.length; i++) {
            s = skills[i];
            unlocked = !!App.unlockedSkills[s.id];

            card = document.createElement("div");
            card.className = "skill-node" + (unlocked ? " unlocked" : "");
            card.setAttribute("data-skill-id", s.id);

            html = ""
                + "<div class='skill-name'>" + s.name + "</div>"
                + "<div class='skill-cost'>" + s.cost + " XP</div>";

            card.innerHTML = html;
            container.appendChild(card);
        }

        log("App: Rendered skill tree:", treeName);
    }

    /* ============================================================
       SKILL UNLOCK LOGIC
       ============================================================ */
    function unlockSkill(skillId) {
        var skill = findSkill(skillId);
        if (!skill) {
            log("App: Skill not found:", skillId);
            return;
        }

        if (App.unlockedSkills[skillId]) {
            log("App: Skill already unlocked:", skillId);
            return;
        }

        if (App.xpTotal < skill.cost) {
            log("App: Not enough XP to unlock", skillId);
            if (typeof alert !== "undefined") {
                alert("Not enough XP to unlock this skill.");
            }
            return;
        }

        App.xpTotal -= skill.cost;
        App.unlockedSkills[skillId] = true;

        updateLevel();
        updateUI();
        saveState();

        log("App: Skill unlocked:", skillId);
    }

    function findSkill(skillId) {
        var trees = Object.keys(SKILL_TREES);
        var i, j, tree, skill;

        for (i = 0; i < trees.length; i++) {
            tree = SKILL_TREES[trees[i]];
            for (j = 0; j < tree.length; j++) {
                skill = tree[j];
                if (skill.id === skillId) return skill;
            }
        }
        return null;
    }
    /* ============================================================
       IMPORT / EXPORT
       ============================================================ */
    function exportSave() {
        try {
            var state = {
                xpTotal: App.xpTotal,
                level: App.level,
                streak: App.streak,
                todayXP: App.todayXP,
                dailyPerfect: App.dailyPerfect,
                completedTasks: App.completedTasks,
                days: App.days,
                unlockedSkills: App.unlockedSkills,
                lastDate: todayISO()
            };
            var blob = new Blob([JSON.stringify(state, null, 2)], { type: "application/json" });
            var url = URL.createObjectURL(blob);

            var a = document.createElement("a");
            a.href = url;
            a.download = "iron-dungeon-save.json";
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);

            log("App: Exported save.");
        } catch (e) {
            log("App: Failed to export save", e);
        }
    }

    function importSave(file) {
        if (!file) return;
        var reader = new FileReader();
        reader.onload = function (e) {
            try {
                var text = e.target.result;
                var state = JSON.parse(text);

                App.xpTotal = state.xpTotal || 0;
                App.level = state.level || 1;
                App.streak = state.streak || 0;
                App.todayXP = state.todayXP || 0;
                App.dailyPerfect = state.dailyPerfect || false;
                App.completedTasks = state.completedTasks || {};
                App.days = state.days || {};
                App.unlockedSkills = state.unlockedSkills || {};

                saveState();
                updateUI();
                log("App: Imported save.");
                if (typeof alert !== "undefined") {
                    alert("Save imported.");
                }
            } catch (err) {
                log("App: Failed to import save", err);
                if (typeof alert !== "undefined") {
                    alert("Failed to import save file.");
                }
            }
        };
        reader.readAsText(file);
    }

    /* ============================================================
       EVENT BINDING
       ============================================================ */
    function bindEvents() {
        // Bottom nav
        var navBtns = document.querySelectorAll(".nav-btn");
        var i, btn;
        for (i = 0; i < navBtns.length; i++) {
            btn = navBtns[i];
            btn.addEventListener("click", function (e) {
                var target = e.currentTarget.getAttribute("data-target");
                if (target) switchScreen(target);
            });
        }

        // Quest tabs
        var questTabs = document.querySelectorAll(".quest-tab");
        var tabBtn;
        for (i = 0; i < questTabs.length; i++) {
            tabBtn = questTabs[i];
            tabBtn.addEventListener("click", function (e) {
                var tab = e.currentTarget.getAttribute("data-tab");
                if (tab) switchQuestTab(tab);
            });
        }

        // Task checkboxes (event delegation)
        var questScroll = document.querySelector(".quest-scroll");
        if (questScroll) {
            questScroll.addEventListener("change", onTaskToggle);
        }

        // Daily perfect
        var dailyBtn = document.getElementById("daily-perfect-btn");
        if (dailyBtn) {
            dailyBtn.addEventListener("click", function () {
                markDailyPerfect();
            });
        }

        // Save day
        var saveDayBtn = document.getElementById("save-day-btn");
        if (saveDayBtn) {
            saveDayBtn.addEventListener("click", function () {
                saveDay();
            });
        }

        // Export
        var exportBtn = document.getElementById("export-btn");
        if (exportBtn) {
            exportBtn.addEventListener("click", function () {
                exportSave();
            });
        }

        // Import
        var importInput = document.getElementById("import-input");
        if (importInput) {
            importInput.addEventListener("change", function (e) {
                var file = e.target.files && e.target.files[0];
                importSave(file);
                importInput.value = "";
            });
        }

        // Skill clicks
        var skillScroll = document.querySelector(".skill-tree-scroll");
        if (skillScroll) {
            skillScroll.addEventListener("click", function (e) {
                var target = e.target || e.srcElement;
                var node = target.closest ? target.closest(".skill-node") : null;
                if (!node) return;
                var skillId = node.getAttribute("data-skill-id");
                if (!skillId) return;
                unlockSkill(skillId);
            });
        }

        // Skill tree tabs
        var skillTabs = document.querySelectorAll(".skill-tree-tab");
        var st;
        for (i = 0; i < skillTabs.length; i++) {
            st = skillTabs[i];
            st.addEventListener("click", function (e) {
                var tree = e.currentTarget.getAttribute("data-tree");
                switchSkillTree(tree);
            });
        }

        log("App: Events bound.");
    }

    /* ============================================================
       SKILL TREE TAB SWITCHING
       ============================================================ */
    function switchSkillTree(tree) {
        var tabs = document.querySelectorAll(".skill-tree-tab");
        var panels = document.querySelectorAll(".skill-tree-panel");
        var i, el;

        for (i = 0; i < tabs.length; i++) {
            el = tabs[i];
            el.classList.toggle("active", el.getAttribute("data-tree") === tree);
        }

        for (i = 0; i < panels.length; i++) {
            el = panels[i];
            el.classList.toggle("active", el.getAttribute("data-tree-panel") === tree);
        }

        log("App: Switched skill tree to", tree);
    }

    /* ============================================================
       BOOTSTRAP
       ============================================================ */
    function init() {
        log("App: init() starting…");

        loadState();
        bindEvents();
        loadProducts();
        renderSkillTrees();
        updateUI();
        switchScreen("character");
        switchQuestTab("morning");
        switchSkillTree("strength");

        log("App: init() complete.");
    }

    onReady(init);

    log("App: Hardened ES5 sandbox ready.");
})();