Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 

Repository files navigation

<title>Zelda Chronology Interactive Mindmap</title> <script src="https://cdn.tailwindcss.com"></script> <script> tailwind.config = { darkMode: 'class', theme: { extend: { colors: { zelda: { gold: '#D4AF37', lightgold: '#F3E5AB', darkgold: '#996515', green: '#1E4D2B', lightgreen: '#2E8B57', triforce: '#FFD700', zonai: '#00F0FF', bg: '#090C10', card: '#131822', border: '#2A303C' } }, fontFamily: { serif: ['Cinzel', 'Georgia', 'serif'], sans: ['Inter', 'sans-serif'] } } } } </script>
<style>
    body {
        font-family: 'Inter', sans-serif;
        background-color: #090C10;
        color: #E6EDF3;
        background-image: 
            radial-gradient(circle at 50% 0%, rgba(212, 175, 55, 0.08) 0%, transparent 60%),
            radial-gradient(circle at 10% 50%, rgba(0, 240, 255, 0.03) 0%, transparent 40%),
            radial-gradient(circle at 90% 80%, rgba(46, 139, 87, 0.04) 0%, transparent 40%);
        background-attachment: fixed;
        overflow-x: hidden;
    }

    .font-cinzel { font-family: 'Cinzel', Georgia, serif; }

    .triforce-glow { 
        text-shadow: 0 0 12px rgba(255, 215, 0, 0.7), 0 0 22px rgba(212, 175, 55, 0.5); 
    }

    /* Custom Scrollbar */
    ::-webkit-scrollbar { width: 7px; height: 7px; }
    ::-webkit-scrollbar-track { background: #0D1117; }
    ::-webkit-scrollbar-thumb { background: #30363D; border-radius: 4px; }
    ::-webkit-scrollbar-thumb:hover { background: #D4AF37; }

    /* Mindmap SVG Connectors */
    .mindmap-connector {
        fill: none;
        stroke-width: 3.5px;
        transition: stroke 0.3s ease, stroke-width 0.3s ease, opacity 0.3s ease;
        opacity: 0.55;
        stroke-dasharray: 8 2;
        animation: flowLine 30s linear infinite;
    }
    @keyframes flowLine {
        to { stroke-dashoffset: -100; }
    }

    .mindmap-connector.active {
        stroke-width: 5.5px;
        opacity: 1;
        stroke-dasharray: none;
        filter: drop-shadow(0px 0px 8px rgba(255, 215, 0, 0.9));
    }

    /* Line Color Variations */
    .path-unified { stroke: #EAB308; }
    .path-fallen { stroke: #EF4444; }
    .path-child { stroke: #3B82F6; }
    .path-adult { stroke: #06B6D4; }
    .path-wild { stroke: #10B981; }

    /* Mindmap Node styling */
    .mindmap-node {
        transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
        cursor: pointer;
        user-select: none;
        touch-action: none;
    }
    .mindmap-node:hover {
        transform: translateY(-2px) scale(1.04);
        z-index: 30 !important;
        box-shadow: 0 0 22px rgba(212, 175, 55, 0.45);
    }
    .mindmap-node.selected {
        border-color: #FFD700 !important;
        box-shadow: 0 0 28px rgba(255, 215, 0, 0.7);
        transform: scale(1.06);
        z-index: 40 !important;
    }

    /* Canvas viewport setup */
    #mindmap-viewport {
        cursor: grab;
        background-image: radial-gradient(rgba(255, 255, 255, 0.06) 1px, transparent 1px);
        background-size: 28px 28px;
        touch-action: none;
    }
    #mindmap-viewport:active {
        cursor: grabbing;
    }

    .fade-in {
        animation: fadeIn 0.3s ease-out forwards;
    }
    @keyframes fadeIn {
        from { opacity: 0; transform: translateY(8px); }
        to { opacity: 1; transform: translateY(0); }
    }
</style>
<header class="sticky top-0 z-40 bg-zelda-bg/95 backdrop-blur-md border-b border-zelda-border py-3 px-4 sm:px-8 shadow-xl">
    <div class="max-w-7xl mx-auto flex flex-col lg:flex-row items-center justify-between gap-4">
        
        <!-- Logo and Main Title -->
        <div class="flex items-center space-x-3 text-center lg:text-left">
            <div class="w-10 h-10 rounded-full bg-zelda-gold/10 border border-zelda-gold/40 flex items-center justify-center text-zelda-gold text-xl triforce-glow shrink-0">
                ▲
            </div>
            <div>
                <h1 class="font-cinzel text-xl sm:text-2xl font-bold text-zelda-lightgold tracking-wider flex items-center justify-center lg:justify-start gap-2">
                    <span id="ui-app-title">ZELDA CHRONOLOGY</span>
                    <span class="text-[10px] text-zelda-gold font-sans font-semibold px-2 py-0.5 rounded border border-zelda-gold/40 bg-zelda-gold/10 tracking-normal">MINDMAP</span>
                </h1>
                <p id="ui-app-subtitle" class="text-xs text-gray-400">Interactive story timeline & canonical connections of Hyrule</p>
            </div>
        </div>

        <!-- View Modes & Controls -->
        <div class="flex items-center gap-2 flex-wrap justify-center">
            <!-- Navigation Tabs -->
            <button id="btn-view-mindmap" onclick="switchView('mindmap')" class="px-3 py-1.5 rounded-lg text-xs sm:text-sm font-medium transition bg-zelda-gold text-black hover:bg-yellow-400 font-cinzel shadow-lg">
                <i class="fa-solid fa-sitemap mr-1.5"></i><span id="ui-tab-mindmap">Interactive Canvas</span>
            </button>
            <button id="btn-view-timeline" onclick="switchView('timeline')" class="px-3 py-1.5 rounded-lg text-xs sm:text-sm font-medium transition bg-zelda-card border border-zelda-border text-gray-300 hover:text-zelda-gold font-cinzel">
                <i class="fa-solid fa-list-ol mr-1.5"></i><span id="ui-tab-timeline">Timeline Order</span>
            </button>
            <button id="btn-view-story" onclick="switchView('story')" class="px-3 py-1.5 rounded-lg text-xs sm:text-sm font-medium transition bg-zelda-card border border-zelda-border text-gray-300 hover:text-zelda-gold font-cinzel">
                <i class="fa-solid fa-book-open mr-1.5"></i><span id="ui-tab-story">Story Reader</span>
            </button>

            <!-- Language Selector Dropdown -->
            <div class="relative ml-2">
                <select id="lang-selector" onchange="changeLanguage(this.value)" class="bg-zelda-card border border-zelda-gold/50 text-zelda-lightgold text-xs font-semibold rounded-lg px-2.5 py-1.5 cursor-pointer focus:outline-none focus:ring-1 focus:ring-zelda-gold">
                    <option value="en" selected>🇬🇧 English</option>
                    <option value="fr">🇫🇷 Français</option>
                    <option value="es">🇪🇸 Español</option>
                </select>
            </div>
        </div>
    </div>
</header>

<nav class="bg-zelda-card/70 border-b border-zelda-border/60 py-2.5 px-4 overflow-x-auto z-30 backdrop-blur-sm">
    <div class="max-w-7xl mx-auto flex items-center justify-between gap-4 min-w-max text-xs sm:text-sm">
        
        <!-- Era Filter Buttons -->
        <div class="flex items-center space-x-2">
            <span id="ui-filter-label" class="text-gray-400 font-medium mr-1 font-cinzel text-xs"><i class="fa-solid fa-filter mr-1"></i> Filter Era:</span>
            <button onclick="filterBranch('all')" class="branch-filter-btn px-3 py-1 rounded-full bg-zelda-gold/20 text-zelda-lightgold border border-zelda-gold/50 font-medium transition" data-branch="all">
                <span id="ui-filter-all">All Games</span> (<span id="count-all">19</span>)
            </button>
            <button onclick="filterBranch('unified')" class="branch-filter-btn px-3 py-1 rounded-full bg-gray-800/80 text-yellow-400 border border-yellow-500/30 hover:border-yellow-500 transition" data-branch="unified">
                <span id="ui-filter-unified">Unified Era</span>
            </button>
            <button onclick="filterBranch('fallen')" class="branch-filter-btn px-3 py-1 rounded-full bg-gray-800/80 text-red-400 border border-red-500/30 hover:border-red-500 transition" data-branch="fallen">
                <span id="ui-filter-fallen">Fallen Hero</span>
            </button>
            <button onclick="filterBranch('child')" class="branch-filter-btn px-3 py-1 rounded-full bg-gray-800/80 text-blue-400 border border-blue-500/30 hover:border-blue-500 transition" data-branch="child">
                <span id="ui-filter-child">Child Era</span>
            </button>
            <button onclick="filterBranch('adult')" class="branch-filter-btn px-3 py-1 rounded-full bg-gray-800/80 text-cyan-400 border border-cyan-500/30 hover:border-cyan-500 transition" data-branch="adult">
                <span id="ui-filter-adult">Adult Era</span>
            </button>
            <button onclick="filterBranch('wild')" class="branch-filter-btn px-3 py-1 rounded-full bg-gray-800/80 text-emerald-400 border border-emerald-500/30 hover:border-emerald-500 transition" data-branch="wild">
                <span id="ui-filter-wild">Wild Era</span>
            </button>
        </div>

        <!-- Search & Mindmap Controls -->
        <div class="flex items-center space-x-3">
            <!-- Search Box -->
            <div class="relative">
                <i class="fa-solid fa-magnifying-glass absolute left-2.5 top-2 text-xs text-gray-400"></i>
                <input type="text" id="search-input" oninput="handleSearch(this.value)" placeholder="Search game..." class="pl-8 pr-3 py-1 bg-zelda-bg border border-zelda-border rounded-lg text-xs text-gray-200 placeholder-gray-500 focus:outline-none focus:border-zelda-gold w-36 sm:w-48">
            </div>

            <!-- Canvas Zoom Controls -->
            <div id="mindmap-controls" class="flex items-center space-x-1 bg-zelda-bg border border-zelda-border rounded-lg p-1">
                <button onclick="zoomMindmap(1.2)" class="px-2 py-1 text-gray-300 hover:text-zelda-gold text-xs rounded hover:bg-zelda-card" title="Zoom In">
                    <i class="fa-solid fa-magnifying-glass-plus"></i>
                </button>
                <button onclick="zoomMindmap(0.8)" class="px-2 py-1 text-gray-300 hover:text-zelda-gold text-xs rounded hover:bg-zelda-card" title="Zoom Out">
                    <i class="fa-solid fa-magnifying-glass-minus"></i>
                </button>
                <button onclick="resetMindmapView()" class="px-2.5 py-1 text-gray-300 hover:text-zelda-gold text-xs rounded hover:bg-zelda-card font-cinzel" title="Recenter View">
                    <i class="fa-solid fa-arrows-to-dot mr-1"></i><span id="ui-btn-reset">Reset</span>
                </button>
            </div>
        </div>
    </div>
</nav>

<main class="flex-grow relative flex flex-col w-full h-[calc(100vh-125px)] overflow-hidden">

    <!-- VIEW 1: INTERACTIVE MINDMAP CANVAS -->
    <div id="view-mindmap" class="relative w-full h-full overflow-hidden flex-grow">
        <!-- Canvas viewport -->
        <div id="mindmap-viewport" class="w-full h-full relative overflow-hidden select-none">
            <div id="mindmap-container" class="absolute origin-top-left transition-transform duration-75 ease-out" style="width: 1600px; height: 1850px;">
                <!-- SVG Overlay for connections -->
                <svg id="mindmap-svg" class="absolute inset-0 w-full h-full pointer-events-none" style="z-index: 5;">
                    <!-- Dynamic Bezier path curves rendered via JS -->
                </svg>
                <!-- HTML Nodes rendered here -->
                <div id="mindmap-nodes" class="absolute inset-0" style="z-index: 10;"></div>
            </div>
        </div>

        <!-- Mindmap Floating Side Panel (Node Info Drawer) -->
        <div id="mindmap-drawer" class="absolute right-4 top-4 bottom-4 w-80 sm:w-96 bg-zelda-card/95 backdrop-blur-xl border border-zelda-gold/50 rounded-2xl p-5 shadow-2xl z-40 flex flex-col justify-between transform transition-transform duration-300 translate-x-[110%]">
            <div class="flex items-center justify-between border-b border-zelda-border pb-3">
                <span id="drawer-era" class="text-[11px] px-2.5 py-1 rounded-full border bg-yellow-500/20 text-yellow-300 font-semibold font-cinzel">
                    Unified Era
                </span>
                <button onclick="closeDrawer()" class="text-gray-400 hover:text-white text-lg p-1">
                    <i class="fa-solid fa-xmark"></i>
                </button>
            </div>

            <!-- Drawer Content -->
            <div class="flex-grow overflow-y-auto my-4 pr-1 space-y-4 text-xs sm:text-sm">
                <div>
                    <h2 id="drawer-title" class="font-cinzel text-xl font-bold text-zelda-lightgold">The Legend of Zelda</h2>
                    <div id="drawer-meta" class="text-xs text-gray-400 mt-1">Platform (Year)</div>
                </div>

                <div>
                    <h3 id="ui-drawer-summary-title" class="font-cinzel text-xs font-bold text-zelda-gold uppercase tracking-wider mb-1.5 flex items-center">
                        <i class="fa-solid fa-book mr-1.5"></i> Story Summary
                    </h3>
                    <p id="drawer-summary" class="bg-zelda-bg/80 p-3 rounded-xl border border-zelda-border text-gray-300 leading-relaxed">
                        ...
                    </p>
                </div>

                <div>
                    <h3 id="ui-drawer-transition-title" class="font-cinzel text-xs font-bold text-emerald-400 uppercase tracking-wider mb-1.5 flex items-center">
                        <i class="fa-solid fa-link mr-1.5"></i> Story Continuity to Next Game
                    </h3>
                    <p id="drawer-transition" class="bg-emerald-950/20 p-3 rounded-xl border border-emerald-500/30 text-emerald-200/90 italic leading-relaxed">
                        ...
                    </p>
                </div>

                <div>
                    <h3 id="ui-drawer-relics-title" class="font-cinzel text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Key Relics & Elements</h3>
                    <div id="drawer-artifacts" class="flex flex-wrap gap-1.5">
                        <!-- Injected -->
                    </div>
                </div>
            </div>

            <!-- Drawer Controls -->
            <div class="border-t border-zelda-border pt-3 flex justify-between items-center text-xs">
                <button id="drawer-prev-btn" onclick="navigateMindmapNode(-1)" class="px-3 py-1.5 bg-zelda-bg border border-zelda-border rounded-lg text-gray-300 hover:text-zelda-gold hover:border-zelda-gold transition">
                    <i class="fa-solid fa-chevron-left mr-1"></i><span id="ui-btn-prev">Previous</span>
                </button>
                <button id="drawer-next-btn" onclick="navigateMindmapNode(1)" class="px-3 py-1.5 bg-zelda-gold text-black font-semibold rounded-lg hover:bg-yellow-400 transition">
                    <span id="ui-btn-next">Next</span><i class="fa-solid fa-chevron-right ml-1"></i>
                </button>
            </div>
        </div>

        <!-- Legend Overlay -->
        <div class="absolute left-4 bottom-4 bg-zelda-bg/90 border border-zelda-border rounded-xl p-3 text-xs space-y-2 backdrop-blur-md z-30 hidden sm:block shadow-lg">
            <div id="ui-legend-title" class="font-cinzel font-bold text-zelda-gold border-b border-zelda-border/60 pb-1">Timeline Branches Key</div>
            <div class="grid grid-cols-2 gap-x-4 gap-y-1 text-gray-300 text-[11px]">
                <div class="flex items-center gap-1.5"><span class="w-3 h-3 rounded-full bg-yellow-500"></span> <span id="ui-leg-unified">Unified Era</span></div>
                <div class="flex items-center gap-1.5"><span class="w-3 h-3 rounded-full bg-red-500"></span> <span id="ui-leg-fallen">Fallen Hero</span></div>
                <div class="flex items-center gap-1.5"><span class="w-3 h-3 rounded-full bg-blue-500"></span> <span id="ui-leg-child">Child Era</span></div>
                <div class="flex items-center gap-1.5"><span class="w-3 h-3 rounded-full bg-cyan-500"></span> <span id="ui-leg-adult">Adult Era</span></div>
                <div class="flex items-center gap-1.5"><span class="w-3 h-3 rounded-full bg-emerald-500"></span> <span id="ui-leg-wild">Era of the Wild</span></div>
            </div>
        </div>
    </div>

    <!-- VIEW 2: VERTICAL TIMELINE LIST VIEW -->
    <div id="view-timeline" class="hidden overflow-y-auto p-4 sm:p-6 lg:p-8 max-w-7xl mx-auto w-full space-y-6">
        <div class="bg-zelda-card border border-zelda-border rounded-xl p-4 sm:p-6 text-sm text-gray-300 flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
            <div class="space-y-1">
                <h2 id="ui-tl-header-title" class="font-cinzel text-lg font-bold text-zelda-gold">Official Canonical Order of Hyrule's History</h2>
                <p id="ui-tl-header-desc" class="text-xs sm:text-sm text-gray-400">
                    Chronological sequence following Nintendo's official lore continuity.
                </p>
            </div>
        </div>

        <div class="relative pl-6 sm:pl-10 space-y-6 border-l-2 border-zelda-border" id="timeline-container">
            <!-- Injected via JS -->
        </div>
    </div>

    <!-- VIEW 3: CONTINUOUS STORY READER MODE -->
    <div id="view-story" class="hidden overflow-y-auto p-4 sm:p-6 max-w-4xl mx-auto w-full space-y-6 fade-in">
        <div class="bg-zelda-card border border-zelda-gold/40 rounded-xl p-6 text-center space-y-3 shadow-xl">
            <span id="ui-story-badge" class="text-xs font-semibold tracking-widest text-zelda-gold uppercase">The Great Hyrule Chronicle</span>
            <h2 id="ui-story-heading" class="font-cinzel text-2xl sm:text-3xl font-bold text-zelda-lightgold">The Entire Story Told Game by Game</h2>
            <div class="flex justify-center gap-4 pt-2">
                <button id="story-prev-btn" onclick="navigateStory(-1)" class="px-4 py-2 bg-zelda-bg border border-zelda-border rounded-lg text-sm hover:border-zelda-gold transition disabled:opacity-40">
                    <i class="fa-solid fa-arrow-left mr-2"></i><span id="ui-story-prev">Previous</span>
                </button>
                <span id="story-indicator" class="text-xs flex items-center font-cinzel text-zelda-gold font-bold px-3">1 / 19</span>
                <button id="story-next-btn" onclick="navigateStory(1)" class="px-4 py-2 bg-zelda-gold text-black font-semibold rounded-lg text-sm hover:bg-yellow-400 transition">
                    <span id="ui-story-next">Next</span><i class="fa-solid fa-arrow-right ml-2"></i>
                </button>
            </div>
        </div>

        <div id="story-card-display" class="bg-zelda-card border border-zelda-border rounded-xl p-6 sm:p-8 space-y-6 shadow-2xl">
            <!-- Injected via JS -->
        </div>
    </div>

</main>

<script>
    // UI Translations Dictionary
    const translations = {
        en: {
            appTitle: "ZELDA CHRONOLOGY",
            appSubtitle: "Interactive story timeline & canonical connections of Hyrule",
            tabMindmap: "Interactive Canvas",
            tabTimeline: "Timeline Order",
            tabStory: "Story Reader",
            filterLabel: "Filter Era:",
            filterAll: "All Games",
            filterUnified: "Unified Era",
            filterFallen: "Fallen Hero",
            filterChild: "Child Era",
            filterAdult: "Adult Era",
            filterWild: "Era of the Wild",
            btnReset: "Reset",
            drawerSummaryTitle: "Story Summary",
            drawerTransitionTitle: "Story Continuity to Next Game",
            drawerRelicsTitle: "Key Relics & Elements",
            btnPrev: "Previous",
            btnNext: "Next",
            legendTitle: "Timeline Branches Key",
            legUnified: "Unified Era",
            legFallen: "Fallen Hero",
            legChild: "Child Era",
            legAdult: "Adult Era",
            legWild: "Era of the Wild",
            tlHeaderTitle: "Official Canonical Order of Hyrule's History",
            tlHeaderDesc: "Chronological sequence following Nintendo's official lore continuity.",
            storyBadge: "The Great Hyrule Chronicle",
            storyHeading: "The Entire Story Told Game by Game",
            storyPrev: "Previous Game",
            storyNext: "Next Game",
            searchPlaceholder: "Search game...",
            transitionPrefix: "Transition to Next Game:"
        },
        fr: {
            appTitle: "LA CHRONOLOGIE ZELDA",
            appSubtitle: "Arbre chronologique interactif & récits de la saga Hyrule",
            tabMindmap: "Carte Interactive",
            tabTimeline: "Liste Chronologique",
            tabStory: "Mode Récit Continu",
            filterLabel: "Filtrer les branches :",
            filterAll: "Tous les jeux",
            filterUnified: "Ère Unifiée",
            filterFallen: "Héros Déchu",
            filterChild: "Ère Enfant",
            filterAdult: "Ère Adulte",
            filterWild: "Ère Sauvagerie",
            btnReset: "Re-centrer",
            drawerSummaryTitle: "Scénario du Jeu",
            drawerTransitionTitle: "Transition vers le jeu suivant",
            drawerRelicsTitle: "Reliques & Éléments Clés",
            btnPrev: "Précédent",
            btnNext: "Suivant",
            legendTitle: "Légende des Branches",
            legUnified: "Ère Unifiée",
            legFallen: "Héros Déchu",
            legChild: "Ère Enfant",
            legAdult: "Ère Adulte",
            legWild: "Ère Sauvagerie",
            tlHeaderTitle: "Ordre Officiel de l'Histoire d'Hyrule",
            tlHeaderDesc: "Séquence chronologique officielle selon le lore de Nintendo.",
            storyBadge: "Grande Chronique d'Hyrule",
            storyHeading: "L'Histoire d'Hyrule Racontée de Jeu en Jeu",
            storyPrev: "Jeu Précédent",
            storyNext: "Jeu Suivant",
            searchPlaceholder: "Rechercher un jeu...",
            transitionPrefix: "Transition vers la suite :"
        },
        es: {
            appTitle: "CRONOLOGÍA ZELDA",
            appSubtitle: "Mapa mental interactivo e historia canónica de Hyrule",
            tabMindmap: "Mapa Interactivo",
            tabTimeline: "Orden Cronológico",
            tabStory: "Modo Historia",
            filterLabel: "Filtrar Era:",
            filterAll: "Todos",
            filterUnified: "Era Unificada",
            filterFallen: "Héroe Caído",
            filterChild: "Era Niño",
            filterAdult: "Era Adulto",
            filterWild: "Era de la Salvajada",
            btnReset: "Centrar",
            drawerSummaryTitle: "Resumen de la Historia",
            drawerTransitionTitle: "Continuidad con el siguiente juego",
            drawerRelicsTitle: "Reliquias y Objetos Clave",
            btnPrev: "Anterior",
            btnNext: "Siguiente",
            legendTitle: "Leyenda de Líneas Temporales",
            legUnified: "Era Unificada",
            legFallen: "Héroe Caído",
            legChild: "Era Niño",
            legAdult: "Era Adulto",
            legWild: "Era Salvaje",
            tlHeaderTitle: "Orden Canónico Oficial de Hyrule",
            tlHeaderDesc: "Secuencia cronológica basada en la historia oficial de Nintendo.",
            storyBadge: "La Gran Crónica de Hyrule",
            storyHeading: "La Historia Completa Juego por Juego",
            storyPrev: "Juego Anterior",
            storyNext: "Juego Siguiente",
            searchPlaceholder: "Buscar juego...",
            transitionPrefix: "Transición al siguiente juego:"
        }
    };

    const zeldaGamesData = [
        // UNIFIED ERA
        {
            id: 'ss', parentId: 'root', year: '2011', platform: 'Wii / Switch', branch: 'unified',
            badgeColor: 'bg-yellow-500/20 text-yellow-300 border-yellow-500/40', icon: 'fa-wand-magic-sparkles',
            x: 700, y: 150,
            title: { en: 'Skyward Sword', fr: 'Skyward Sword', es: 'Skyward Sword' },
            fullTitle: { en: 'The Legend of Zelda: Skyward Sword', fr: 'The Legend of Zelda: Skyward Sword', es: 'The Legend of Zelda: Skyward Sword' },
            era: { en: 'Unified Era', fr: 'Ère Unifiée', es: 'Era Unificada' },
            summary: {
                en: "Above the clouds in Skyloft, Link's childhood friend Zelda is swept away to the surface below. Guided by Fi, the spirit of a holy blade forged by the Goddess Hylia, Link descends into the abandoned Surface. He discovers Zelda is the mortal reincarnation of Hylia. Together, they fight Demon King Demise, who seeks the Triforce. Link reforges the Master Sword and seals Demise.",
                fr: "Au-dessus des nuages à Célesbourg, Zelda est aspirée vers la surface. Guidé par Fay, l'esprit de l'épée sacrée de la Déesse Hylia, Link explore la terre oubliée. Il découvre que Zelda est la réincarnation d'Hylia. Ils affrontent le Roi Démon Énimath. Link forge la véritable Épée de Légende et scelle le démon.",
                es: "En Altárea, sobre las nubes, Zelda es arrastrada a la superficie. Guiado por Fay, el espíritu de la espada de la Diosa Hylia, Link desciende al mundo terrenal. Descubre que Zelda es la encarnación de Hylia. Vencen al Rey Demonio El Heraldo de la Muerte, y Link forja la Espada Maestra."
            },
            transition: {
                en: "Upon his defeat, Demise casts an ancient curse: his eternal hatred will endlessly reincarnate to haunt those with the blood of the Goddess and the spirit of the Hero. This primordial grudge initiates the endless cycle of Hyrule.",
                fr: "À sa mort, Énimath prononce une malédiction : sa haine se réincarnera sans fin pour traquer le sang de la Déesse et l'âme du Héros. C'est le point de départ du cycle éternel d'Hyrule.",
                es: "Al morir, El Heraldo lanza una maldición: su odio se reencarnará eternamente para perseguir a la sangre de la Diosa y al alma del Héroe. Esto da inicio al ciclo eterno de Hyrule."
            },
            artifacts: ["Master Sword", "Triforce", "Fi", "Demise's Curse"],
            nextGameId: 'mc'
        },
        {
            id: 'mc', parentId: 'ss', year: '2004', platform: 'Game Boy Advance', branch: 'unified',
            badgeColor: 'bg-yellow-500/20 text-yellow-300 border-yellow-500/40', icon: 'fa-hat-wizard',
            x: 700, y: 270,
            title: { en: 'The Minish Cap', fr: 'The Minish Cap', es: 'The Minish Cap' },
            fullTitle: { en: 'The Legend of Zelda: The Minish Cap', fr: 'The Legend of Zelda: The Minish Cap', es: 'The Legend of Zelda: The Minish Cap' },
            era: { en: 'Unified Era', fr: 'Ère Unifiée', es: 'Era Unificada' },
            summary: {
                en: "Centuries after the founding of Hyrule, sorcerer Vaati petrifies Princess Zelda and shatters the Picori Blade. Link meets Ezlo, a talking hat-like Minish sage. Shrinking to the tiny size of the Minish, Link infuses the blade with the four elements to forge the Four Sword, defeating Vaati and saving Zelda.",
                fr: "Des siècles après la fondation d'Hyrule, le sorcier Vaati pétrifie Zelda. Link rencontre Exelo, un chapeau parlé Minish. En rétrécissant à la taille des Minish, Link restaure la lame pour créer l'Épée des Quatre et terrasse Vaati.",
                es: "Siglos después, el brujo Vaati petrifica a Zelda. Link conoce a Ezlo, un gorro parlante Minish. Reduciéndose al tamaño de los Minish, recarga la espada con los cuatro elementos para crear la Espada de los Cuatro y vencer a Vaati."
            },
            transition: {
                en: "Vaati is sealed away in the Four Sword Sanctuary, where the blade remains guarded for generations to prevent the wind mage's resurgence.",
                fr: "Vaati est scellé dans le Sanctuaire de l'Épée des Quatre, préservant le royaume d'Hyrule durant de nombreuses générations.",
                es: "Vaati queda sellado en el Santuario de la Espada de los Cuatro, garantizando la paz durante generaciones."
            },
            artifacts: ["Four Sword", "Ezlo", "Elemental Stones"],
            nextGameId: 'fs'
        },
        {
            id: 'fs', parentId: 'mc', year: '2002', platform: 'Game Boy Advance', branch: 'unified',
            badgeColor: 'bg-yellow-500/20 text-yellow-300 border-yellow-500/40', icon: 'fa-users',
            x: 700, y: 390,
            title: { en: 'Four Swords', fr: 'Four Swords', es: 'Four Swords' },
            fullTitle: { en: 'The Legend of Zelda: Four Swords', fr: 'The Legend of Zelda: Four Swords', es: 'The Legend of Zelda: Four Swords' },
            era: { en: 'Unified Era', fr: 'Ère Unifiée', es: 'Era Unificada' },
            summary: {
                en: "The seal holding Vaati weakens. The wind mage escapes and kidnaps Princess Zelda. Link draws the Four Sword, splitting into four distinct clones. Working together, the four Links overcome dungeons and reseal Vaati.",
                fr: "Le sceau de Vaati faiblit. Le sorcier s'échappe et enlève Zelda. Link tire l'Épée des Quatre, se divisant en quatre clones, et scelle à nouveau le sorcier.",
                es: "El sello de Vaati se debilita. El brujo escapa y secuestra a Zelda. Link extrae la Espada de los Cuatro, dividiéndose en cuatro clones para volver a sellar a Vaati."
            },
            transition: {
                en: "Following this victory, Hyrule enters a long period of prosperity until the devastating Hyrulean Civil War. A dying mother entrusts her infant to the Great Deku Tree... this boy becomes the Hero of Time.",
                fr: "Hyrule traverse ensuite la grande Guerre Civile. Une mère confie son nourrisson à l'Arbre Mojo... l'enfant deviendra le Héros du Temps.",
                es: "Tras esto, ocurre la Guerra Civil de Hyrule. Una madre moribunda confía su bebé al Gran Árbol Deku... quien será el Héroe del Tiempo."
            },
            artifacts: ["Four Sword", "Vaati's Seal"],
            nextGameId: 'oot'
        },
        {
            id: 'oot', parentId: 'fs', year: '1998', platform: 'Nintendo 64', branch: 'unified',
            badgeColor: 'bg-yellow-400 text-black font-bold ring-2 ring-yellow-400/50', icon: 'fa-hourglass-half',
            x: 700, y: 530,
            title: { en: 'Ocarina of Time', fr: 'Ocarina of Time', es: 'Ocarina of Time' },
            fullTitle: { en: 'The Legend of Zelda: Ocarina of Time', fr: 'The Legend of Zelda: Ocarina of Time', es: 'The Legend of Zelda: Ocarina of Time' },
            era: { en: 'Timeline Crossroads', fr: 'Carrefour Temporel', es: 'Encrucijada Temporal' },
            summary: {
                en: "Ganondorf, King of the Gerudo, tricks young Link into opening the Sacred Realm. Link draws the Master Sword and is sealed for 7 years. Ganondorf touches the Triforce, splitting it into three. Awakening as an adult, Link awakens the 7 Sages to confront the King of Evil.",
                fr: "Ganondorf manipule le jeune Link pour ouvrir le Royaume Sacré. Link tire l'Épée de Légende et dort 7 ans. Ganondorf vole la Triforce qui se brise. Link réveille les 7 Sages et affronte Ganondorf.",
                es: "Ganondorf engaña al joven Link para abrir el Reino Sagrado. Link duerme 7 años. Ganondorf toca la Trifuerza y la divide. Como adulto, Link despierta a los 7 Sabios para enfrentar a Ganondorf."
            },
            transition: {
                en: "CRITICAL TIMELINE BRANCH POINT! The outcome creates 3 distinct timelines: 1) Link falls in battle (Fallen Hero). 2) Link is sent back to his childhood (Child Era). 3) Adult Link saves Hyrule and departs (Adult Era).",
                fr: "CARREFOUR SPATIO-TEMPOREL ! L'issue du combat crée 3 lignes temporelles : 1) Link est vaincu (Héros Déchu). 2) Link est renvoyé enfant (Ère Enfant). 3) Link sauve le futur mais repart (Ère Adulte).",
                es: "¡PUNTO DE BIFURCACIÓN! El resultado crea 3 líneas: 1) El Héroe cae vencido (Héroe Caído). 2) Link vuelve a su infancia (Era Niño). 3) Link salva el futuro y regresa (Era Adulto)."
            },
            artifacts: ["Ocarina of Time", "Master Sword", "Triforce Pieces"],
            nextGameId: 'alttp'
        },

        // BRANCH A: FALLEN HERO (LEFT BRANCH)
        {
            id: 'alttp', parentId: 'oot', year: '1991', platform: 'SNES', branch: 'fallen',
            badgeColor: 'bg-red-500/20 text-red-300 border-red-500/40', icon: 'fa-dungeon',
            x: 200, y: 680,
            title: { en: 'A Link to the Past', fr: 'A Link to the Past', es: 'A Link to the Past' },
            fullTitle: { en: 'The Legend of Zelda: A Link to the Past', fr: 'The Legend of Zelda: A Link to the Past', es: 'The Legend of Zelda: A Link to the Past' },
            era: { en: 'Fallen Hero', fr: 'Héros Déchu', es: 'Héroe Caído' },
            summary: {
                en: "In the timeline where Ganondorf defeated Link, the Sacred Realm turned into the Dark World. Sorcerer Agahnim sacrifices descendants of the Seven Sages. A new Link takes up the Master Sword, enters the Dark World, and slays Ganon.",
                fr: "Ganon a vaincu Link dans OoT et transformé le Royaume Sacré en Monde des Ténèbres. Le sorcier Agahnim sacrifie les sages. Un nouveau Link prend l'Épée de Légende et détruit Ganon.",
                es: "Ganondorf vence a Link y convierte el Reino Sagrado en el Mundo Oscuro. El brujo Agahnim sacrifica a las descendientes de los Sabios. Un nuevo Link toma la Espada Maestra y vence a Ganon."
            },
            transition: {
                en: "Link touches the complete Triforce, wishing to restore all damage caused by Ganon. The Triforce later tests Link's courage by sending him on trials abroad.",
                fr: "En touchant la Triforce réunifiée, Link fait le vœu de restaurer Hyrule. Plus tard, la Triforce envoie Link en mission dans des royaumes voisins.",
                es: "Link toca la Trifuerza completa y restaura Hyrule. Tiempo después, la Trifuerza lo envía a misiones en tierras lejanas."
            },
            artifacts: ["Full Triforce", "Master Sword", "Magic Mirror"],
            nextGameId: 'oracles'
        },
        {
            id: 'oracles', parentId: 'alttp', year: '2001', platform: 'Game Boy Color', branch: 'fallen',
            badgeColor: 'bg-red-500/20 text-red-300 border-red-500/40', icon: 'fa-sun',
            x: 200, y: 790,
            title: { en: 'Oracle of Seasons / Ages', fr: 'Oracle of Seasons / Ages', es: 'Oracle of Seasons / Ages' },
            fullTitle: { en: 'Zelda: Oracle of Seasons & Oracle of Ages', fr: 'Zelda: Oracle of Seasons & Oracle of Ages', es: 'Zelda: Oracle of Seasons & Oracle of Ages' },
            era: { en: 'Fallen Hero', fr: 'Héros Déchu', es: 'Héroe Caído' },
            summary: {
                en: "The Triforce summons Link to save the distant lands of Holodrum and Labrynna. Twinrova witches plot to resurrect Ganon through sorrow and destruction, but Link thwarts their ritual.",
                fr: "La Triforce envoie Link sauver Holodrum et Labrynna. Les sorcières Twinrova tentent de ressusciter Ganon, mais Link déjoue leur plan.",
                es: "La Trifuerza envía a Link a salvar Holodrum y Labrynna. Las brujas Twinrova intentan resucitar a Ganon, pero Link lo impide."
            },
            transition: {
                en: "With his quest fulfilled, Link sets sail on a wooden ship to return home to Hyrule, but a sudden ocean storm strikes...",
                fr: "Son devoir accompli, Link prend la mer pour rentrer à Hyrule, mais une violente tempête éclate...",
                es: "Cumplida su misión, Link se embarca hacia Hyrule, pero una feroz tormenta lo sorprende en el mar..."
            },
            artifacts: ["Rod of Seasons", "Harp of Ages"],
            nextGameId: 'la'
        },
        {
            id: 'la', parentId: 'oracles', year: '1993', platform: 'Game Boy / Switch', branch: 'fallen',
            badgeColor: 'bg-red-500/20 text-red-300 border-red-500/40', icon: 'fa-cloud-moon',
            x: 200, y: 900,
            title: { en: "Link's Awakening", fr: "Link's Awakening", es: "Link's Awakening" },
            fullTitle: { en: "The Legend of Zelda: Link's Awakening", fr: "The Legend of Zelda: Link's Awakening", es: "The Legend of Zelda: Link's Awakening" },
            era: { en: 'Fallen Hero', fr: 'Héros Déchu', es: 'Héroe Caído' },
            summary: {
                en: "Shipwrecked on Koholint Island, Link must wake the Wind Fish to leave. He discovers the entire island, its villages, and inhabitants exist only inside the sleeping Wind Fish's dream.",
                fr: "Naufragé sur l'île de Cocolint, Link doit réveiller le Poisson-Rêve. Il découvre que toute l'île n'est qu'un rêve qui disparaîtra à son réveil.",
                es: "Náufrago en la Isla Koholint, Link debe despertar al Pez del Viento. Descubre que toda la isla es un sueño que desaparecerá al despertar."
            },
            transition: {
                en: "Link wakes up afloat on ship wreckage in the middle of the ocean, holding on to the memories of Koholint Island.",
                fr: "Link se réveille au milieu de l'océan, accroché à un débris de son bateau, avec le souvenir éternel de Cocolint.",
                es: "Link despierta flotando en el océano sobre los restos de su navío, conservando el recuerdo de Koholint."
            },
            artifacts: ["Sirens' Instruments", "Wind Fish"],
            nextGameId: 'albw'
        },
        {
            id: 'albw', parentId: 'la', year: '2013', platform: 'Nintendo 3DS', branch: 'fallen',
            badgeColor: 'bg-red-500/20 text-red-300 border-red-500/40', icon: 'fa-paint-brush',
            x: 200, y: 1010,
            title: { en: 'A Link Between Worlds', fr: 'A Link Between Worlds', es: 'A Link Between Worlds' },
            fullTitle: { en: 'The Legend of Zelda: A Link Between Worlds', fr: 'The Legend of Zelda: A Link Between Worlds', es: 'The Legend of Zelda: A Link Between Worlds' },
            era: { en: 'Fallen Hero', fr: 'Héros Déchu', es: 'Héroe Caído' },
            summary: {
                en: "Sorcerer Yuga escapes into Lorule, a parallel dimension. Link acquires the ability to merge into walls as a painting, traveling between worlds to defeat Yuga-Ganon and restore Lorule's Triforce.",
                fr: "Le sorcier Yuga s'enfuit dans la dimension de Lorule. Link apprend à se transformer en peinture murale pour voyager entre les deux mondes et restaurer les Triforces.",
                es: "El brujo Yuga huye a Lorule, una dimensión paralela. Link aprende a convertirse en pintura para viajar entre ambos mundos y restaurar la Trifuerza."
            },
            transition: {
                en: "Restoring Lorule's Triforce restores cosmic balance. However, over centuries, Hyrule enters an era of decline, losing its grand kingdom structure.",
                fr: "La restauration de Lorule ramène l'équilibre inter-dimensionnel. Cependant, Hyrule entre peu à peu dans une ère de grand déclin.",
                es: "Restaurar la Trifuerza de Lorule equilibra los dos mundos, pero Hyrule cae paulatinamente en una era de declive."
            },
            artifacts: ["Hyrule & Lorule Triforces", "Ravio's Bracelet"],
            nextGameId: 'tfh'
        },
        {
            id: 'tfh', parentId: 'albw', year: '2015', platform: 'Nintendo 3DS', branch: 'fallen',
            badgeColor: 'bg-red-500/20 text-red-300 border-red-500/40', icon: 'fa-shirt',
            x: 200, y: 1120,
            title: { en: 'Tri Force Heroes', fr: 'Tri Force Heroes', es: 'Tri Force Heroes' },
            fullTitle: { en: 'The Legend of Zelda: Tri Force Heroes', fr: 'The Legend of Zelda: Tri Force Heroes', es: 'The Legend of Zelda: Tri Force Heroes' },
            era: { en: 'Fallen Hero', fr: 'Héros Déchu', es: 'Héroe Caído' },
            summary: {
                en: "In fashion-obsessed Hytopia, Link teams up with two other heroes in totem outfits to break a curse placed on Princess Styla by the Lady Maud witch.",
                fr: "Dans le royaume d'Hytopia, Link s'associe à deux héros en tenue totémique pour briser la malédiction vestimentaire de la princesse Styla.",
                es: "En el reino de Pasarela, Link se une a dos héroes vistiendo trajes especiales para romper la maldición de la princesa Styla."
            },
            transition: {
                en: "A quirky side adventure illustrating how true heroes travel far beyond Hyrule while the main kingdom decays into wild wilderness.",
                fr: "Une aventure à l'étranger pendant que le royaume principal d'Hyrule décline petit à petit.",
                es: "Una aventura en tierras lejanas mientras el reino de Hyrule cae lentamente en la ruina."
            },
            artifacts: ["Totem Outfits", "Style Medals"],
            nextGameId: 'loz1'
        },
        {
            id: 'loz1', parentId: 'tfh', year: '1986', platform: 'NES', branch: 'fallen',
            badgeColor: 'bg-red-500/20 text-red-300 border-red-500/40', icon: 'fa-shield-halved',
            x: 200, y: 1230,
            title: { en: 'The Legend of Zelda (1986)', fr: 'The Legend of Zelda (1986)', es: 'The Legend of Zelda (1986)' },
            fullTitle: { en: 'The Legend of Zelda (1986)', fr: 'The Legend of Zelda (1986)', es: 'The Legend of Zelda (1986)' },
            era: { en: 'Fallen Hero', fr: 'Héros Déchu', es: 'Héroe Caído' },
            summary: {
                en: "Hyrule is reduced to a barren wilderness. Ganon steals the Triforce of Power. Princess Zelda splits the Triforce of Wisdom into 8 fragments. A young Link collects the pieces, enters Death Mountain, and destroys Ganon.",
                fr: "Hyrule est dévasté. Ganon vole la Triforce de la Force. Zelda brise la Triforce de la Sagesse en 8 morceaux. Le jeune Link les rassemble et élimine Ganon.",
                es: "Hyrule está desolado. Ganon roba la Trifuerza del Poder. Zelda divide la Trifuerza de la Sabiduría en 8 fragmentos. Un joven Link los reúne y destruye a Ganon."
            },
            transition: {
                en: "Though Ganon is destroyed, an ancient Princess Zelda remains asleep under a sleeping spell in the North Palace.",
                fr: "Même si Ganon est détruit, une ancienne Princesse Zelda dort toujours sous un sortilège dans le Palais du Nord.",
                es: "Aunque Ganon es destruido, una ancestral Princesa Zelda continúa bajo un hechizo de sueño en el Palacio del Norte."
            },
            artifacts: ["Triforce of Wisdom", "Triforce of Power"],
            nextGameId: 'zelda2'
        },
        {
            id: 'zelda2', parentId: 'loz1', year: '1987', platform: 'NES', branch: 'fallen',
            badgeColor: 'bg-red-500/20 text-red-300 border-red-500/40', icon: 'fa-khanda',
            x: 200, y: 1340,
            title: { en: 'Zelda II: Adventure of Link', fr: 'Zelda II: Adventure of Link', es: 'Zelda II: Adventure of Link' },
            fullTitle: { en: 'Zelda II: The Adventure of Link', fr: 'Zelda II: The Adventure of Link', es: 'Zelda II: The Adventure of Link' },
            era: { en: 'Fallen Hero', fr: 'Héros Déchu', es: 'Héroe Caído' },
            summary: {
                en: "At age 16, Link gets the crest of the Triforce on his hand. He travels through labyrinth palaces, defeats Dark Link in the Great Palace, obtains the Triforce of Courage, and awakens the sleeping Zelda.",
                fr: "À 16 ans, la marque de la Triforce apparaît sur la main de Link. Il parcourt les labyrinthes, bat Dark Link au Grand Palais et réveille la princesse Zelda.",
                es: "A los 16 años, la marca de la Trifuerza aparece en la mano de Link. Supera los palacios, vence a Dark Link en el Gran Palacio y despierta a Zelda."
            },
            transition: {
                en: "The Triforce is fully reunited, ending the fallen timeline era before fading into ancient legend over tens of thousands of years.",
                fr: "La Triforce est réunifiée, clôturant l'ère déchue qui s'enfonce dans les légendes anciennes sur des millénaires.",
                es: "La Trifuerza se reúne por completo, cerrando la era del Héroe Caído hacia el lejano futuro."
            },
            artifacts: ["Triforce of Courage", "Dark Link"],
            nextGameId: 'botw'
        },

        // BRANCH B: CHILD TIMELINE (MIDDLE BRANCH)
        {
            id: 'mm', parentId: 'oot', year: '2000', platform: 'Nintendo 64', branch: 'child',
            badgeColor: 'bg-blue-500/20 text-blue-300 border-blue-500/40', icon: 'fa-mask',
            x: 700, y: 680,
            title: { en: "Majora's Mask", fr: "Majora's Mask", es: "Majora's Mask" },
            fullTitle: { en: "The Legend of Zelda: Majora's Mask", fr: "The Legend of Zelda: Majora's Mask", es: "The Legend of Zelda: Majora's Mask" },
            era: { en: 'Child Era', fr: 'Ère Enfant', es: 'Era Niño' },
            summary: {
                en: "Sent back to childhood by Zelda, Link searches for Navi and enters Termina, threatened by the crashing Moon within 3 days. Using the Song of Time to loop 72 hours, Link frees the 4 Giants and purges Majora's Mask.",
                fr: "Renvoyé enfant, Link cherche sa fée Navi et arrive à Termina, menacée de destruction par la Lune en 3 jours. Grâce au Chant du Temps, il boucle les 72h et vainc Majora.",
                es: "Enviado a su infancia, Link busca a Navi y llega a Termina, amenazada por la caída de la Luna en 3 días. Usando la Canción del Tiempo, vence a la Máscara de Majora."
            },
            transition: {
                en: "Young Link warns Zelda and the King about Ganondorf's evil intentions before he can attack. Ganondorf is caught and brought to trial by the Ancient Sages.",
                fr: "Grâce aux avertissements de Link enfant, la royauté arrête Ganondorf avant son coup d'état, le conduisant au jugement des Sages.",
                es: "Link advierte a la realeza sobre los planes de Ganondorf, permitiendo capturarlo antes de que ataque Hyrule."
            },
            artifacts: ["Majora's Mask", "Ocarina of Time", "Transformation Masks"],
            nextGameId: 'tp'
        },
        {
            id: 'tp', parentId: 'mm', year: '2006', platform: 'GameCube / Wii', branch: 'child',
            badgeColor: 'bg-blue-500/20 text-blue-300 border-blue-500/40', icon: 'fa-wolf-pack-battalion',
            x: 700, y: 840,
            title: { en: 'Twilight Princess', fr: 'Twilight Princess', es: 'Twilight Princess' },
            fullTitle: { en: 'The Legend of Zelda: Twilight Princess', fr: 'The Legend of Zelda: Twilight Princess', es: 'The Legend of Zelda: Twilight Princess' },
            era: { en: 'Child Era', fr: 'Ère Enfant', es: 'Era Niño' },
            summary: {
                en: "Ganondorf, banished to the Twilight Realm during his failed execution, joins forces with Zant. Link transforms into a wolf in Twilight and teams up with Midna to reclaim Hyrule and slay Ganondorf.",
                fr: "Banni dans le Crépuscule après son exécution ratée, Ganondorf s'allie à Xanto. Link, transformé en loup, fait équipe avec Midona pour libérer Hyrule et tuer Ganondorf.",
                es: "Desterrado al Reino del Crepúsculo, Ganondorf se alía con Zant. Link, transformado en lobo, se une a Midna para recuperar Hyrule y derrotar a Ganondorf."
            },
            transition: {
                en: "Midna destroys the Mirror of Twilight, permanently severing the connection between the World of Light and the Twilight Realm.",
                fr: "Midona brise le Miroir des Ombres, coupant définitivement le passage entre le monde de la Lumière et le Crépuscule.",
                es: "Midna destruye el Espejo del Crepúsculo, rompiendo para siempre el vínculo entre ambos mundos."
            },
            artifacts: ["Mirror of Twilight", "Shadow Crystals", "Master Sword"],
            nextGameId: 'fsa'
        },
        {
            id: 'fsa', parentId: 'tp', year: '2004', platform: 'GameCube', branch: 'child',
            badgeColor: 'bg-blue-500/20 text-blue-300 border-blue-500/40', icon: 'fa-shield-cat',
            x: 700, y: 1000,
            title: { en: 'Four Swords Adventures', fr: 'Four Swords Adventures', es: 'Four Swords Adventures' },
            fullTitle: { en: 'The Legend of Zelda: Four Swords Adventures', fr: 'The Legend of Zelda: Four Swords Adventures', es: 'The Legend of Zelda: Four Swords Adventures' },
            era: { en: 'Child Era', fr: 'Ère Enfant', es: 'Era Niño' },
            summary: {
                en: "Centuries later, a reincarnated Gerudo named Ganon steals the Trident of Power and resurrects Vaati. Link uses the Four Sword to split into four, defeat Vaati, and seal Ganon inside the blade.",
                fr: "Des siècles plus tard, un nouveau Ganon réincarné vole le Trident de la Nuit et libère Vaati. Les quatre Link battent Vaati et scellent Ganon dans l'Épée des Quatre.",
                es: "Siglos después, un nuevo Ganon reencarnado roba el Tridente de la Noche y libera a Vaati. Los cuatro Link vencen a Vaati y sellan a Ganon en la espada."
            },
            transition: {
                en: "Ganon is sealed inside the Four Sword, bringing lasting peace to the Child Timeline until the distant era of the Wild.",
                fr: "Ganon est scellé dans l'Épée des Quatre, apportant une longue paix jusqu'à l'ère de la Sauvagerie.",
                es: "Ganon queda sellado en la Espada de los Cuatro, garantizando paz hasta la lejana era salvaje."
            },
            artifacts: ["Trident of Power", "Four Sword"],
            nextGameId: 'botw'
        },

        // BRANCH C: ADULT TIMELINE (RIGHT BRANCH)
        {
            id: 'tww', parentId: 'oot', year: '2002', platform: 'GameCube', branch: 'adult',
            badgeColor: 'bg-cyan-500/20 text-cyan-300 border-cyan-500/40', icon: 'fa-ship',
            x: 1180, y: 680,
            title: { en: 'The Wind Waker', fr: 'The Wind Waker', es: 'The Wind Waker' },
            fullTitle: { en: 'The Legend of Zelda: The Wind Waker', fr: 'The Legend of Zelda: The Wind Waker', es: 'The Legend of Zelda: The Wind Waker' },
            era: { en: 'Adult Era', fr: 'Ère Adulte', es: 'Era Adulto' },
            summary: {
                en: "Without a Hero when Ganon broke free, the Goddesses flooded Hyrule, sealing it beneath a Great Sea. Centuries later, Link sails with pirate Tetra (Zelda), restores the Master Sword, and turns Ganondorf into stone under the sea.",
                fr: "Privé de héros, Hyrule fut englouti sous un grand océan. Des siècles après, Link navigue avec Tetra (Zelda) et scelle Ganondorf sous forme de statue au fond des mers.",
                es: "Sin un héroe, las Diosas inundaron Hyrule bajo un Gran Mar. Siglos después, Link navega junto a la pirata Tetra (Zelda) y convierte a Ganondorf en piedra bajo el mar."
            },
            transition: {
                en: "Ancient Hyrule is permanently washed away. King Daphnes Nohansen Hyrule wishes on the Triforce to give Link and Tetra a new future to find new land.",
                fr: "L'ancien Hyrule est définitivement englouti. Le roi confie l'avenir à Link et Tetra pour fonder un nouveau royaume sur un nouveau continent.",
                es: "El antiguo Hyrule queda sepultado. El rey confía el futuro a Link y Tetra para encontrar una nueva tierra."
            },
            artifacts: ["Wind Waker", "Master Sword", "Triforce of Courage"],
            nextGameId: 'ph'
        },
        {
            id: 'ph', parentId: 'tww', year: '2007', platform: 'Nintendo DS', branch: 'adult',
            badgeColor: 'bg-cyan-500/20 text-cyan-300 border-cyan-500/40', icon: 'fa-compass',
            x: 1180, y: 840,
            title: { en: 'Phantom Hourglass', fr: 'Phantom Hourglass', es: 'Phantom Hourglass' },
            fullTitle: { en: 'The Legend of Zelda: Phantom Hourglass', fr: 'The Legend of Zelda: Phantom Hourglass', es: 'The Legend of Zelda: Phantom Hourglass' },
            era: { en: 'Adult Era', fr: 'Ère Adulte', es: 'Era Adulto' },
            summary: {
                en: "Tetra is captured by a Ghost Ship. Link teams up with Captain Linebeck and fairy Ciela, using the Phantom Hourglass to explore dungeons and destroy life-draining demon Bellum.",
                fr: "Tetra est enlevée par le Navire Fantôme. Link s'allie au capitaine Linebeck pour explorer la mer et détruire le démon Bellum.",
                es: "Tetra es capturada por el Barco Fantasma. Link se une al capitán Linebeck para explorar el mar y derrotar al demonio Bellum."
            },
            transition: {
                en: "After defeating Bellum, Link and Tetra resume sailing the seas and eventually discover a vast new continent: New Hyrule.",
                fr: "Après la victoire, l'équipage reprend sa navigation et découvre un grand continent inexploré : le Nouvel Hyrule.",
                es: "Tras vencer a Bellum, el equipo continúa su viaje hasta descubrir un vasto continente: Nuevo Hyrule."
            },
            artifacts: ["Phantom Hourglass", "Phantom Sword"],
            nextGameId: 'st'
        },
        {
            id: 'st', parentId: 'ph', year: '2009', platform: 'Nintendo DS', branch: 'adult',
            badgeColor: 'bg-cyan-500/20 text-cyan-300 border-cyan-500/40', icon: 'fa-train',
            x: 1180, y: 1000,
            title: { en: 'Spirit Tracks', fr: 'Spirit Tracks', es: 'Spirit Tracks' },
            fullTitle: { en: 'The Legend of Zelda: Spirit Tracks', fr: 'The Legend of Zelda: Spirit Tracks', es: 'The Legend of Zelda: Spirit Tracks' },
            era: { en: 'Adult Era', fr: 'Ère Adulte', es: 'Era Adulto' },
            summary: {
                en: "100 years later in New Hyrule, Demon King Malladus tries to return by destroying the Spirit Tracks rail network. Engineer Link and spirit-form Zelda pilot an armored train and defeat Malladus.",
                fr: "100 ans plus tard sur le Nouvel Hyrule, le Roi Démon Mallard tente de renaître. Link l'ingénieur et le spectre de Zelda sauvent le continent.",
                es: "100 años después en Nuevo Hyrule, el Rey Demonio Malladus intenta renacer. Link el maquinista y el espíritu de Zelda salvan el continente."
            },
            transition: {
                en: "New Hyrule thrives in an era of train technology and peaceful harmony, closing the Adult Timeline.",
                fr: "Le Nouvel Hyrule prospère sereinement grâce aux Voies Célestes, achevant la ligne adulte.",
                es: "Nuevo Hyrule prospera en paz gracias a las Vías Sagradas, cerrando la era adulta."
            },
            artifacts: ["Spirit Flute", "Spirit Tracks"],
            nextGameId: 'botw'
        },

        // WILD ERA (CONVERGENCE AT BOTTOM)
        {
            id: 'botw', parentId: 'oot', year: '2017', platform: 'Wii U / Switch', branch: 'wild',
            badgeColor: 'bg-emerald-500/20 text-emerald-300 border-emerald-500/40 font-bold', icon: 'fa-mountain-sun',
            x: 700, y: 1520,
            title: { en: 'Breath of the Wild', fr: 'Breath of the Wild', es: 'Breath of the Wild' },
            fullTitle: { en: 'The Legend of Zelda: Breath of the Wild', fr: 'The Legend of Zelda: Breath of the Wild', es: 'The Legend of Zelda: Breath of the Wild' },
            era: { en: 'Era of the Wild', fr: 'Ère Sauvagerie', es: 'Era Salvaje' },
            summary: {
                en: "Tens of thousands of years later, all previous history has faded into myth. Link awakens after 100 years in stasis with no memories. He frees the 4 Divine Beasts and destroys Calamity Ganon, saving Zelda.",
                fr: "Des dizaines de milliers d'années plus tard, les époques passées sont devenues des légendes. Link se réveille après 100 ans, libère les 4 Créatures Divines et détruit le Fléau Ganon.",
                es: "Decenas de miles de años después, el pasado es leyenda. Link despierta tras 100 años de letargo, libera las 4 Bestias Divinas y destruye a Ganon la Calamidad."
            },
            transition: {
                en: "A few years after Calamity Ganon's defeat, Gloom substance begins leaking from deep beneath Hyrule Castle...",
                fr: "Quelques années plus tard, la substance Miasmique commence à fuir depuis les sous-sols du château d'Hyrule...",
                es: "Años después, la sustancia de Miasma comienza a filtrarse desde el subsuelo del Castillo de Hyrule..."
            },
            artifacts: ["Sheikah Slate", "Divine Beasts", "Master Sword"],
            nextGameId: 'totk'
        },
        {
            id: 'totk', parentId: 'botw', year: '2023', platform: 'Nintendo Switch', branch: 'wild',
            badgeColor: 'bg-emerald-500/20 text-emerald-300 border-emerald-500/40 font-bold', icon: 'fa-dragon',
            x: 700, y: 1670,
            title: { en: 'Tears of the Kingdom', fr: 'Tears of the Kingdom', es: 'Tears of the Kingdom' },
            fullTitle: { en: 'The Legend of Zelda: Tears of the Kingdom', fr: 'The Legend of Zelda: Tears of the Kingdom', es: 'The Legend of Zelda: Tears of the Kingdom' },
            era: { en: 'Era of the Wild', fr: 'Ère Sauvagerie', es: 'Era Salvaje' },
            summary: {
                en: "The awakened mummy of Demon King Ganondorf shatters the Master Sword. Zelda travels back in time to the founding era and becomes the Light Dragon to restore the blade. Link unleashes Zonai abilities, awakens the Sages, and slays the Demon Dragon.",
                fr: "La momie de Ganondorf se réveille et brise la Master Sword. Zelda remonte dans le temps et se transforme en Dragon Céleste pour soigner la lame. Link utilise les pouvoirs Soneaux et vainc le Dragon Noir.",
                es: "Ganondorf despierta y destruye la Espada Maestra. Zelda viaja al pasado lejano y se convierte en el Dragón de la Luz para restaurarla. Link utiliza los poderes Sonnan y destruye al Dragón Negro."
            },
            transition: {
                en: "By utterly obliterating Ganondorf, Link and Zelda extinguish Demise's ancient malice, restoring eternal peace across the skies and surface of Hyrule.",
                fr: "En terrassant définitivement Ganondorf, Link et Zelda mettent fin à la malédiction originelle d'Énimath et ramènent une paix durable sur Hyrule.",
                es: "Al derrotar definitivamente a Ganondorf, Link y Zelda eliminan la malicia ancestral de El Heraldo, trayendo paz eterna a Hyrule."
            },
            artifacts: ["Secret Stones", "Rauru's Arm", "Restored Master Sword"],
            nextGameId: null
        }
    ];

    let currentLang = 'en';
    let currentView = 'mindmap';
    let currentBranch = 'all';
    let searchQuery = '';
    let selectedNodeId = null;
    let zoomScale = 0.82;
    let panX = 30;
    let panY = 20;
    let isDragging = false;
    let startX = 0, startY = 0;

    document.addEventListener('DOMContentLoaded', () => {
        // Check browser language or saved language
        const savedLang = localStorage.getItem('zelda_app_lang');
        if (savedLang && translations[savedLang]) {
            currentLang = savedLang;
        }
        document.getElementById('lang-selector').value = currentLang;

        updateLanguageUI();
        renderMindmap();
        renderTimeline();
        renderStoryCard(0);
        setupCanvasPanZoom();

        // Window resize handler
        window.addEventListener('resize', () => {
            if (currentView === 'mindmap') {
                renderMindmap();
            }
        });
    });

    // Language Switcher Function
    function changeLanguage(langKey) {
        if (!translations[langKey]) return;
        currentLang = langKey;
        localStorage.setItem('zelda_app_lang', langKey);
        updateLanguageUI();
        renderMindmap();
        renderTimeline();
        renderStoryCard(currentStoryIdx);
        if (selectedNodeId) {
            selectMindmapNode(selectedNodeId);
        }
    }

    function updateLanguageUI() {
        const t = translations[currentLang];
        document.getElementById('ui-app-title').innerText = t.appTitle;
        document.getElementById('ui-app-subtitle').innerText = t.appSubtitle;
        document.getElementById('ui-tab-mindmap').innerText = t.tabMindmap;
        document.getElementById('ui-tab-timeline').innerText = t.tabTimeline;
        document.getElementById('ui-tab-story').innerText = t.tabStory;
        document.getElementById('ui-filter-label').innerHTML = `<i class="fa-solid fa-filter mr-1"></i> ${t.filterLabel}`;
        document.getElementById('ui-filter-all').innerText = t.filterAll;
        document.getElementById('ui-filter-unified').innerText = t.filterUnified;
        document.getElementById('ui-filter-fallen').innerText = t.filterFallen;
        document.getElementById('ui-filter-child').innerText = t.filterChild;
        document.getElementById('ui-filter-adult').innerText = t.filterAdult;
        document.getElementById('ui-filter-wild').innerText = t.filterWild;
        document.getElementById('ui-btn-reset').innerText = t.btnReset;
        document.getElementById('ui-drawer-summary-title').innerHTML = `<i class="fa-solid fa-book mr-1.5"></i> ${t.drawerSummaryTitle}`;
        document.getElementById('ui-drawer-transition-title').innerHTML = `<i class="fa-solid fa-link mr-1.5"></i> ${t.drawerTransitionTitle}`;
        document.getElementById('ui-drawer-relics-title').innerText = t.drawerRelicsTitle;
        document.getElementById('ui-btn-prev').innerText = t.btnPrev;
        document.getElementById('ui-btn-next').innerText = t.btnNext;
        document.getElementById('ui-legend-title').innerText = t.legendTitle;
        document.getElementById('ui-leg-unified').innerText = t.legUnified;
        document.getElementById('ui-leg-fallen').innerText = t.legFallen;
        document.getElementById('ui-leg-child').innerText = t.legChild;
        document.getElementById('ui-leg-adult').innerText = t.legAdult;
        document.getElementById('ui-leg-wild').innerText = t.legWild;
        document.getElementById('ui-tl-header-title').innerText = t.tlHeaderTitle;
        document.getElementById('ui-tl-header-desc').innerText = t.tlHeaderDesc;
        document.getElementById('ui-story-badge').innerText = t.storyBadge;
        document.getElementById('ui-story-heading').innerText = t.storyHeading;
        document.getElementById('ui-story-prev').innerText = t.storyPrev;
        document.getElementById('ui-story-next').innerText = t.storyNext;
        document.getElementById('search-input').placeholder = t.searchPlaceholder;
    }

    // View Mode Switcher
    function switchView(viewName) {
        currentView = viewName;
        document.getElementById('view-mindmap').classList.add('hidden');
        document.getElementById('view-timeline').classList.add('hidden');
        document.getElementById('view-story').classList.add('hidden');
        document.getElementById('mindmap-controls').classList.add('hidden');

        const inactiveBtnClass = "px-3 py-1.5 rounded-lg text-xs sm:text-sm font-medium transition bg-zelda-card border border-zelda-border text-gray-300 hover:text-zelda-gold font-cinzel";
        const activeBtnClass = "px-3 py-1.5 rounded-lg text-xs sm:text-sm font-medium transition bg-zelda-gold text-black hover:bg-yellow-400 font-cinzel shadow-lg";

        document.getElementById('btn-view-mindmap').className = inactiveBtnClass;
        document.getElementById('btn-view-timeline').className = inactiveBtnClass;
        document.getElementById('btn-view-story').className = inactiveBtnClass;

        if (viewName === 'mindmap') {
            document.getElementById('view-mindmap').classList.remove('hidden');
            document.getElementById('mindmap-controls').classList.remove('hidden');
            document.getElementById('btn-view-mindmap').className = activeBtnClass;
        } else if (viewName === 'timeline') {
            document.getElementById('view-timeline').classList.remove('hidden');
            document.getElementById('btn-view-timeline').className = activeBtnClass;
        } else if (viewName === 'story') {
            document.getElementById('view-story').classList.remove('hidden');
            document.getElementById('btn-view-story').className = activeBtnClass;
        }
    }

    // Branch Filtering & Search Logic
    function filterBranch(branch) {
        currentBranch = branch;
        document.querySelectorAll('.branch-filter-btn').forEach(btn => {
            if (btn.getAttribute('data-branch') === branch) {
                btn.classList.add('bg-zelda-gold/20', 'text-zelda-lightgold', 'border-zelda-gold/50');
                btn.classList.remove('bg-gray-800/80');
            } else {
                btn.classList.remove('bg-zelda-gold/20', 'text-zelda-lightgold', 'border-zelda-gold/50');
                btn.classList.add('bg-gray-800/80');
            }
        });

        renderMindmap();
        renderTimeline();
    }

    function handleSearch(val) {
        searchQuery = val.toLowerCase().trim();
        renderMindmap();
        renderTimeline();
    }

    function renderMindmap() {
        const container = document.getElementById('mindmap-nodes');
        const svg = document.getElementById('mindmap-svg');
        container.innerHTML = '';
        svg.innerHTML = '';

        // Render Root Origin Banner
        const rootNode = document.createElement('div');
        rootNode.className = "absolute text-center bg-amber-950/90 border-2 border-yellow-500 text-yellow-300 font-cinzel font-bold px-5 py-2.5 rounded-2xl shadow-xl z-20";
        rootNode.style.left = `600px`;
        rootNode.style.top = `30px`;
        rootNode.style.width = `200px`;
        rootNode.innerHTML = `<div class="text-[10px] tracking-widest text-yellow-400 uppercase">ORIGINS</div><div class="text-xs sm:text-sm">Creation of Hyrule</div>`;
        container.appendChild(rootNode);

        // Filter games
        const visibleGames = zeldaGamesData.filter(g => {
            const matchesBranch = (currentBranch === 'all') || (g.branch === currentBranch) || (g.branch === 'unified');
            const matchesSearch = !searchQuery || g.title[currentLang].toLowerCase().includes(searchQuery) || g.fullTitle[currentLang].toLowerCase().includes(searchQuery);
            return matchesBranch && matchesSearch;
        });

        // Draw SVG Bezier Curves
        visibleGames.forEach(game => {
            let parentX = 700, parentY = 75;

            if (game.parentId && game.parentId !== 'root') {
                const parent = zeldaGamesData.find(g => g.id === game.parentId);
                if (parent) {
                    parentX = parent.x + 90;
                    parentY = parent.y + 35;
                }
            }

            const childX = game.x + 90;
            const childY = game.y + 5;

            const deltaY = childY - parentY;
            const controlY1 = parentY + deltaY * 0.45;
            const controlY2 = childY - deltaY * 0.45;

            const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
            path.setAttribute('d', `M ${parentX} ${parentY} C ${parentX} ${controlY1}, ${childX} ${controlY2}, ${childX} ${childY}`);
            path.setAttribute('class', `mindmap-connector path-${game.branch}`);
            path.setAttribute('id', `path-${game.id}`);
            svg.appendChild(path);

            // Special convergence line from Zelda2 and Spirit Tracks to BotW
            if (game.id === 'botw' && currentBranch === 'all') {
                ['zelda2', 'st'].forEach(prevId => {
                    const pGame = zeldaGamesData.find(g => g.id === prevId);
                    if (pGame) {
                        const extraPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
                        extraPath.setAttribute('d', `M ${pGame.x + 90} ${pGame.y + 35} C ${pGame.x + 90} ${pGame.y + 100}, ${childX} ${childY - 100}, ${childX} ${childY}`);
                        extraPath.setAttribute('class', `mindmap-connector path-${pGame.branch}`);
                        extraPath.setAttribute('id', `path-extra-${prevId}`);
                        svg.appendChild(extraPath);
                    }
                });
            }
        });

        // Render Node Cards
        visibleGames.forEach(game => {
            const node = document.createElement('div');
            const isSelected = selectedNodeId === game.id;
            
            node.className = `mindmap-node absolute w-48 bg-zelda-card border border-zelda-border rounded-xl p-3 shadow-lg flex flex-col gap-1.5 ${isSelected ? 'selected' : ''}`;
            node.style.left = `${game.x}px`;
            node.style.top = `${game.y}px`;
            
            node.onclick = (e) => {
                e.stopPropagation();
                selectMindmapNode(game.id);
            };

            node.innerHTML = `
                <div class="flex items-center justify-between text-[10px]">
                    <span class="px-2 py-0.5 rounded-full border ${game.badgeColor} font-semibold font-cinzel truncate max-w-[120px]">
                        ${game.era[currentLang]}
                    </span>
                    <span class="text-gray-400 font-mono">${game.year}</span>
                </div>
                <div class="font-cinzel font-bold text-xs text-zelda-lightgold flex items-center gap-1.5 mt-0.5">
                    <i class="fa-solid ${game.icon} text-zelda-gold text-xs shrink-0"></i>
                    <span class="truncate">${game.title[currentLang]}</span>
                </div>
            `;
            container.appendChild(node);
        });

        updateTransform();
    }

    function setupCanvasPanZoom() {
        const viewport = document.getElementById('mindmap-viewport');

        // Mouse pan
        viewport.addEventListener('mousedown', (e) => {
            isDragging = true;
            startX = e.clientX - panX;
            startY = e.clientY - panY;
        });

        window.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            panX = e.clientX - startX;
            panY = e.clientY - startY;
            updateTransform();
        });

        window.addEventListener('mouseup', () => {
            isDragging = false;
        });

        // Touch drag support
        viewport.addEventListener('touchstart', (e) => {
            if (e.touches.length === 1) {
                isDragging = true;
                startX = e.touches[0].clientX - panX;
                startY = e.touches[0].clientY - panY;
            }
        }, { passive: true });

        window.addEventListener('touchmove', (e) => {
            if (!isDragging || e.touches.length !== 1) return;
            panX = e.touches[0].clientX - startX;
            panY = e.touches[0].clientY - startY;
            updateTransform();
        }, { passive: true });

        window.addEventListener('touchend', () => {
            isDragging = false;
        });

        // Wheel zoom
        viewport.addEventListener('wheel', (e) => {
            e.preventDefault();
            const zoomFactor = e.deltaY < 0 ? 1.08 : 0.92;
            zoomScale = Math.min(Math.max(0.35, zoomScale * zoomFactor), 1.8);
            updateTransform();
        }, { passive: false });
    }

    function zoomMindmap(factor) {
        zoomScale = Math.min(Math.max(0.35, zoomScale * factor), 1.8);
        updateTransform();
    }

    function resetMindmapView() {
        zoomScale = 0.82;
        panX = 30;
        panY = 20;
        updateTransform();
    }

    function updateTransform() {
        const container = document.getElementById('mindmap-container');
        if (container) {
            container.style.transform = `translate(${panX}px, ${panY}px) scale(${zoomScale})`;
        }
    }

    function selectMindmapNode(gameId) {
        selectedNodeId = gameId;
        const game = zeldaGamesData.find(g => g.id === gameId);
        if (!game) return;

        // Highlight path
        document.querySelectorAll('.mindmap-connector').forEach(el => el.classList.remove('active'));
        
        let current = game;
        while (current) {
            const pathEl = document.getElementById(`path-${current.id}`);
            if (pathEl) pathEl.classList.add('active');
            current = zeldaGamesData.find(g => g.id === current.parentId);
        }

        // Populate Drawer Details
        document.getElementById('drawer-era').innerText = game.era[currentLang];
        document.getElementById('drawer-title').innerText = game.fullTitle[currentLang];
        document.getElementById('drawer-meta').innerText = `${game.platform} — Released ${game.year}`;
        document.getElementById('drawer-summary').innerText = game.summary[currentLang];
        document.getElementById('drawer-transition').innerText = `"${game.transition[currentLang]}"`;
        
        const artifactsContainer = document.getElementById('drawer-artifacts');
        artifactsContainer.innerHTML = game.artifacts.map(a => 
            `<span class="px-2 py-1 bg-zelda-bg border border-zelda-border rounded text-zelda-lightgold text-[11px]"><i class="fa-solid fa-gem text-zelda-gold mr-1"></i>${a}</span>`
        ).join('');

        // Open Drawer
        const drawer = document.getElementById('mindmap-drawer');
        drawer.classList.remove('translate-x-[110%]');

        renderMindmap();
    }

    function closeDrawer() {
        document.getElementById('mindmap-drawer').classList.add('translate-x-[110%]');
        selectedNodeId = null;
        renderMindmap();
    }

    function navigateMindmapNode(direction) {
        if (!selectedNodeId) {
            selectMindmapNode(zeldaGamesData[0].id);
            return;
        }
        const idx = zeldaGamesData.findIndex(g => g.id === selectedNodeId);
        let nextIdx = idx + direction;
        if (nextIdx < 0) nextIdx = 0;
        if (nextIdx >= zeldaGamesData.length) nextIdx = zeldaGamesData.length - 1;
        selectMindmapNode(zeldaGamesData[nextIdx].id);
    }

    function renderTimeline() {
        const container = document.getElementById('timeline-container');
        container.innerHTML = '';

        const filteredGames = zeldaGamesData.filter(g => {
            const matchesBranch = (currentBranch === 'all') || (g.branch === currentBranch);
            const matchesSearch = !searchQuery || g.title[currentLang].toLowerCase().includes(searchQuery) || g.fullTitle[currentLang].toLowerCase().includes(searchQuery);
            return matchesBranch && matchesSearch;
        });

        document.getElementById('count-all').innerText = zeldaGamesData.length;

        filteredGames.forEach((game) => {
            const card = document.createElement('div');
            card.className = `relative bg-zelda-card border border-zelda-border rounded-xl p-5 fade-in hover:border-zelda-gold/50 transition shadow-lg`;
            
            const marker = document.createElement('div');
            marker.className = "absolute -left-[31px] sm:-left-[47px] top-6 w-8 h-8 rounded-full bg-zelda-bg border-2 border-zelda-gold text-zelda-gold flex items-center justify-center text-xs shadow-lg";
            marker.innerHTML = `<i class="fa-solid ${game.icon}"></i>`;
            card.appendChild(marker);

            card.innerHTML += `
                <div class="flex flex-wrap items-center justify-between gap-2 mb-2">
                    <span class="text-xs px-2.5 py-1 rounded-full border ${game.badgeColor} font-semibold font-cinzel">
                        ${game.era[currentLang]}
                    </span>
                    <div class="text-xs text-gray-400">
                        <span><i class="fa-solid fa-gamepad mr-1"></i>${game.platform} (${game.year})</span>
                    </div>
                </div>
                <h3 class="font-cinzel text-xl font-bold text-zelda-lightgold mb-2">${game.fullTitle[currentLang]}</h3>
                <p class="text-xs sm:text-sm text-gray-300 leading-relaxed mb-4">${game.summary[currentLang]}</p>
                <div class="bg-zelda-bg/80 p-3 rounded-lg border border-zelda-border/60 text-xs">
                    <span class="text-zelda-gold font-bold font-cinzel block mb-1">${translations[currentLang].transitionPrefix}</span>
                    <p class="text-gray-300 italic font-serif">"${game.transition[currentLang]}"</p>
                </div>
            `;
            container.appendChild(card);
        });
    }

    let currentStoryIdx = 0;
    function renderStoryCard(idx) {
        currentStoryIdx = idx;
        const game = zeldaGamesData[currentStoryIdx];
        const display = document.getElementById('story-card-display');
        
        document.getElementById('story-indicator').innerText = `${currentStoryIdx + 1} / ${zeldaGamesData.length}`;
        document.getElementById('story-prev-btn').disabled = currentStoryIdx === 0;
        document.getElementById('story-next-btn').disabled = currentStoryIdx === zeldaGamesData.length - 1;

        display.innerHTML = `
            <div class="flex flex-wrap items-center justify-between gap-2 border-b border-zelda-border pb-4">
                <div>
                    <span class="text-xs px-2.5 py-1 rounded-full border ${game.badgeColor} font-semibold font-cinzel">
                        ${game.era[currentLang]}
                    </span>
                    <h3 class="font-cinzel text-2xl font-bold text-zelda-lightgold mt-2">${game.fullTitle[currentLang]}</h3>
                </div>
                <div class="text-right text-xs text-gray-400">
                    <div class="font-bold text-gray-300">${game.platform}</div>
                    <div>Released: ${game.year}</div>
                </div>
            </div>

            <div class="space-y-4 text-sm text-gray-300 leading-relaxed">
                <div>
                    <h4 class="font-cinzel text-base font-bold text-zelda-gold mb-2"><i class="fa-solid fa-scroll mr-2"></i>${translations[currentLang].drawerSummaryTitle}</h4>
                    <p class="bg-zelda-bg/70 p-4 rounded-xl border border-zelda-border/60">${game.summary[currentLang]}</p>
                </div>

                <div>
                    <h4 class="font-cinzel text-base font-bold text-emerald-400 mb-2"><i class="fa-solid fa-link mr-2"></i>${translations[currentLang].drawerTransitionTitle}</h4>
                    <p class="bg-emerald-950/20 p-4 rounded-xl border border-emerald-500/30 text-emerald-200/90 italic">
                        "${game.transition[currentLang]}"
                    </p>
                </div>
            </div>
        `;
    }

    function navigateStory(direction) {
        let next = currentStoryIdx + direction;
        if (next >= 0 && next < zeldaGamesData.length) {
            renderStoryCard(next);
        }
    }
</script>

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors