{% extends "base.html.j2" %}
{% from "decor.html.j2" import badge_circle, motif_divider, page_badge %}

{% block title %}{{ t.enc_title | default(site.encyclopedia_label) }}{% endblock %}

{% block extra_styles %}
.enc-welcome {
    padding: 1.5rem 0 1rem;
    margin-bottom: 1.5rem;
    position: relative;
    padding-left: 1.5rem;
}
.enc-welcome::before {
    content: '';
    position: absolute;
    left: 0;
    top: 1.5rem;
    width: 4px;
    height: 48px;
    background: var(--td-color-primary);
    border-radius: 2px;
}
.enc-welcome h1 {
    color: var(--td-color-primary);
    margin: 0 0 0.5rem 0;
    font-size: 2rem;
}
.enc-welcome-text {
    max-width: 600px;
    font-size: 0.95rem;
    line-height: 1.6;
    color: var(--td-color-text-secondary);
}
.enc-stat {
    font-size: 0.85rem;
    color: var(--td-color-text-muted);
    margin-top: 0.5rem;
}
.portal-section {
    background: var(--td-color-surface);
    border: 1px solid var(--td-color-border);
    border-radius: var(--td-radius);
    padding: 1.5rem;
    margin-bottom: 1.5rem;
    box-shadow: var(--td-shadow-sm);
}
.portal-section h2 {
    margin: 0 0 1rem 0;
    font-size: 1.1rem;
    color: var(--td-color-primary);
    padding-bottom: 0.5rem;
    border-bottom: none;
    position: relative;
    padding-left: 1rem;
}
.portal-section h2::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.15em;
    width: 3px;
    height: 1.1em;
    background: var(--td-color-primary);
    border-radius: 2px;
}
.portal-columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.5rem;
}
.portal-columns .portal-section {
    margin-bottom: 0;
}
.featured-card {
    display: flex;
    gap: 1.25rem;
}
.featured-image {
    flex-shrink: 0;
    width: 200px;
}
.featured-image img {
    width: 100%;
    border-radius: var(--td-radius);
    object-fit: cover;
}
.featured-image .credit {
    font-size: 0.75rem;
    color: var(--td-color-text-muted);
    margin-top: 0.25rem;
}
.featured-text h3 {
    margin: 0 0 0.5rem 0;
    font-size: 1.2rem;
}
.featured-text h3 a {
    color: var(--td-color-primary);
    text-decoration: none;
}
.featured-text h3 a:hover {
    color: var(--td-color-accent);
}
.featured-abstract {
    font-style: italic;
    color: var(--td-color-text-secondary);
    margin: 0 0 0.5rem 0;
    font-size: 0.9rem;
    line-height: 1.5;
}
.featured-excerpt {
    color: var(--td-color-text-secondary);
    font-size: 0.9rem;
    line-height: 1.6;
    margin: 0 0 0.5rem 0;
}
.featured-readmore {
    display: inline-block;
    background: var(--td-color-primary);
    color: white;
    padding: 0.4rem 1.25rem;
    border-radius: 999px;
    text-decoration: none;
    font-size: 0.85rem;
    font-weight: 600;
    transition: background 0.2s;
}
.featured-readmore:hover {
    background: var(--td-color-accent-muted);
    text-decoration: none;
}
.cat-list {
    list-style: none;
    padding: 0;
    margin: 0;
    max-height: calc(100vh - 16rem);
    overflow-y: auto;
}
.cat-list > li {
    padding: 0.3rem 0;
}
.cat-list a {
    color: var(--td-color-primary);
    text-decoration: none;
    font-size: 0.95rem;
}
.cat-list a:hover {
    text-decoration: underline;
}
.cat-count {
    font-size: 0.8rem;
    color: var(--td-color-text-muted);
}
.cat-list ul {
    list-style: none;
    padding-left: 1rem;
    margin: 0.15rem 0;
}
.cat-list ul li {
    padding: 0.15rem 0;
}
.cat-list ul a {
    font-size: 0.85rem;
}
.highlights-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
    gap: 0.75rem;
}
.highlight-card {
    text-decoration: none;
    color: inherit;
    text-align: center;
    transition: transform 0.2s;
}
.highlight-card:hover {
    transform: translateY(-2px);
}
.highlight-card img {
    width: 100%;
    height: 80px;
    object-fit: cover;
    border-radius: var(--td-radius);
}
.highlight-card span {
    font-size: 0.8rem;
    color: var(--td-color-primary);
    display: block;
    margin-top: 0.25rem;
}
.highlight-card:hover span {
    color: var(--td-color-accent);
}
.entry {
    transition: background 0.2s;
}
.entry:hover {
    background: var(--td-color-bg-light);
}
.entry-header {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: 0.25rem;
}
.entry-word {
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--td-color-primary);
}
.entry-word a {
    color: inherit;
    text-decoration: none;
}
.entry-word a:hover {
    color: var(--td-color-accent);
    text-decoration: underline;
}
.entry-type-tag {
    font-size: 0.7rem;
    background: var(--td-color-accent-light);
    color: var(--td-color-accent-muted);
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
}
.entry-summary {
    color: var(--td-color-text-secondary);
    font-size: 0.95rem;
    margin: 0;
    line-height: 1.5;
}
.entry-categories {
    margin-top: 0.35rem;
    display: flex;
    gap: 0.25rem;
    flex-wrap: wrap;
}
.entry-cat {
    font-size: 0.72rem;
    background: var(--td-color-accent-light);
    color: var(--td-color-accent-muted);
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.15s;
}
.entry-cat:hover {
    background: var(--td-color-border-dark);
    color: white;
}
.entry-variants {
    font-size: 0.85rem;
    color: var(--td-color-text-muted);
    margin-top: 0.2rem;
}
.variant-tag {
    background: var(--td-color-border);
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    margin-right: 0.3rem;
    font-style: italic;
}
.enc-skeleton {
    padding: 1.5rem;
}
@media (max-width: 900px) and (min-width: 701px) {
    .portal-columns {
        gap: 1rem;
    }
    .featured-card {
        gap: 1rem;
    }
    .featured-image {
        width: 140px;
        min-width: 120px;
    }
}
@media (max-width: 700px) {
    .portal-columns {
        grid-template-columns: 1fr;
    }
    .featured-card {
        flex-direction: column;
    }
    .featured-image {
        width: 100%;
    }
}

/* ── Knowledge graph ── */
.graph-section {
    background: var(--td-color-surface);
    border: 1px solid var(--td-color-border);
    border-radius: var(--td-radius);
    padding: 1.5rem;
    margin-bottom: 1.5rem;
    box-shadow: var(--td-shadow-sm);
}
.graph-section h2 {
    margin: 0 0 0.75rem 0;
    font-size: 1.1rem;
    color: var(--td-color-primary);
    position: relative;
    padding-left: 1rem;
}
.graph-section h2::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.15em;
    width: 3px;
    height: 1.1em;
    background: var(--td-color-primary);
    border-radius: 2px;
}
.graph-container {
    position: relative;
    width: 100%;
    height: 520px;
    border: 1px solid var(--td-color-border);
    border-radius: calc(var(--td-radius) - 4px);
    overflow: hidden;
    background: var(--td-color-bg);
}
.graph-container svg {
    width: 100%;
    height: 100%;
    display: block;
}
.graph-controls {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
    flex-wrap: wrap;
    align-items: center;
}
.graph-btn {
    padding: 0.3rem 0.8rem;
    border: 1px solid var(--td-color-border);
    background: var(--td-color-bg);
    border-radius: 999px;
    font-size: 0.8rem;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
    font-family: var(--td-font-family);
    color: var(--td-color-text);
}
.graph-btn:hover {
    background: var(--td-color-bg-light);
    border-color: var(--td-color-border-dark);
}
.graph-btn.active {
    background: var(--td-color-primary);
    color: white;
    border-color: var(--td-color-primary);
}
.graph-legend {
    display: flex;
    gap: 1rem;
    font-size: 0.75rem;
    color: var(--td-color-text-muted);
    margin-top: 0.5rem;
    flex-wrap: wrap;
}
.graph-legend-item {
    display: flex;
    align-items: center;
    gap: 0.3rem;
}
.graph-legend-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    display: inline-block;
}
.graph-tooltip {
    position: absolute;
    pointer-events: none;
    background: var(--td-color-surface);
    border: 1px solid var(--td-color-border-dark);
    border-radius: 8px;
    padding: 0.75rem 1rem;
    font-size: 0.85rem;
    max-width: 280px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    z-index: 10;
    opacity: 0;
    transition: opacity 0.15s;
}
.graph-tooltip.visible {
    opacity: 1;
    pointer-events: auto;
}
.graph-tooltip-title {
    font-weight: 700;
    font-family: var(--td-font-headings);
    color: var(--td-color-primary);
    margin-bottom: 0.3rem;
}
.graph-tooltip-abstract {
    font-size: 0.8rem;
    color: var(--td-color-text-secondary);
    line-height: 1.4;
    margin-bottom: 0.4rem;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.graph-tooltip-link {
    font-size: 0.75rem;
    color: var(--td-color-accent);
    text-decoration: none;
}
.graph-tooltip-link:hover {
    text-decoration: underline;
}
.graph-tooltip-cats {
    font-size: 0.72rem;
    color: var(--td-color-text-muted);
    margin-bottom: 0.3rem;
}
@media (max-width: 700px) {
    .graph-container {
        height: 380px;
    }
}
{% endblock %}

{% block content %}
    <div class="enc-welcome">
        <h1>{{ t.enc_title | default(site.encyclopedia_label) }} {{ page_badge() }}</h1>
        <p class="enc-welcome-text">{{ t.enc_welcome_text | default('') }}</p>
        <p class="enc-stat">{{ badge_circle(label=meta.record_count|string, intensity=intensity) }} {{ meta.record_count }} {{ t.articles_label | default('verbetes') }}</p>
    </div>

    <div class="no-js-only results-container">
        {% for entry in data %}
        <div class="entry">
            <div class="entry-header">
                <span class="entry-word"><a href="encyclopedia/{{ entry.id }}.html">{{ entry.title }}</a></span>
                {% if entry.entry_type %}<span class="entry-type-tag">{{ entry.entry_type }}</span>{% endif %}
            </div>
            {% if entry.abstract %}<p class="entry-summary">{{ entry.abstract }}</p>{% endif %}
        </div>
        {% endfor %}
    </div>

    <div class="js-only">
        <div class="search-container">
            <div class="search-row">
                <input type="text" id="search-input" class="search-input"
                       placeholder="{{ t.enc_search_placeholder | default('Pesquisar...') }}"
                       autocomplete="off"
                       aria-label="{{ t.search_label | default('Pesquisar') }}">
                <select id="keyword-filter" class="filter-select"
                        aria-label="{{ t.filter_label | default('Filtrar resultados') }}">
                    <option value="">{{ t.enc_filter_all | default('Todas as categorias') }}</option>
                    <option value="__uncategorized__">{{ t.enc_filter_uncategorized | default('Sem categoria') }}</option>
                </select>
            </div>
            <div class="stats" id="stats" aria-live="polite"></div>
        </div>

        {{ motif_divider(intensity=intensity) }}

        <div id="portal">
            <div id="featured-section" class="portal-section" style="display:none;"></div>
            <div class="portal-columns">
                <div id="categories-section" class="portal-section" style="display:none;"></div>
                <div id="highlights-section" class="portal-section" style="display:none;"></div>
            </div>
        </div>

        <div id="graph-section" class="graph-section js-only" style="display:none;">
            <h2>{{ t.enc_graph_title | default('Mapa de conexões') }}</h2>
            <div class="graph-controls">
                <button class="graph-btn active" data-graph-filter="all">{{ t.enc_graph_all | default('Todos') }}</button>
            </div>
            <div class="graph-container" id="graph-container">
                <div class="graph-tooltip" id="graph-tooltip">
                    <div class="graph-tooltip-title" id="tooltip-title"></div>
                    <div class="graph-tooltip-cats" id="tooltip-cats"></div>
                    <div class="graph-tooltip-abstract" id="tooltip-abstract"></div>
                    <a class="graph-tooltip-link" id="tooltip-link" href="#">{{ t.enc_read_more | default('Ler mais') }} &rarr;</a>
                </div>
            </div>
            <div class="graph-legend" id="graph-legend"></div>
        </div>

        <div class="results-container" id="results" style="display:none;">
            <div class="enc-skeleton" id="loading">
                <div class="skeleton skeleton-block"></div>
                <div class="skeleton skeleton-line"></div>
                <div class="skeleton skeleton-line"></div>
                <div class="skeleton skeleton-line" style="width:60%"></div>
            </div>
        </div>
    </div>
{% endblock %}

{% block scripts %}
    <script>
    var T = {{ t | tojson }};
    var BASE_PATH = '{{ base_path | default("../") }}';

    function hasCategory(entry, value) {
        if (value === '__uncategorized__') return !entry.categories || entry.categories.length === 0;
        return entry.categories && entry.categories.some(function(cat) {
            return cat === value || cat.indexOf(value + '/') === 0;
        });
    }

    function attachCategoryFilters() {
        document.querySelectorAll('.entry-cat').forEach(function(el) {
            el.addEventListener('click', function(e) {
                e.stopPropagation();
                var cat = this.getAttribute('data-category');
                if (cat) {
                    var kw = document.getElementById('keyword-filter');
                    var found = false;
                    for (var i = 0; i < kw.options.length; i++) {
                        if (kw.options[i].value === cat) { found = true; break; }
                    }
                    if (!found) {
                        var opt = document.createElement('option');
                        opt.value = cat;
                        opt.textContent = cat;
                        kw.appendChild(opt);
                    }
                    kw.value = cat;
                    kw.dispatchEvent(new Event('change'));
                }
            });
        });
    }

    function addFilterOption(kwFilter, value) {
        for (var i = 0; i < kwFilter.options.length; i++) {
            if (kwFilter.options[i].value === value) return;
        }
        var opt = document.createElement('option');
        opt.value = value;
        opt.textContent = value;
        kwFilter.appendChild(opt);
    }

    var json = {% if client_index %}{{ client_index | tojson }}{% else %}{
        data: {{ data | tojson }},
        category_tree: {},
        featured: null,
        highlights: []
    }{% endif %};
    (function() {
            var kwFilter = document.getElementById('keyword-filter');
            var searchInput = document.getElementById('search-input');
            var portal = document.getElementById('portal');
            var resultsEl = document.getElementById('results');

            // --- Render Featured Article ---
            if (json.featured) {
                var f = json.featured;
                var html = '<h2>' + escapeHtml(T.enc_featured_title) + '</h2>';
                html += '<div class="featured-card">';
                if (f.image) {
                    html += '<div class="featured-image">';
                    html += '<a href="encyclopedia/' + encodeURIComponent(f.id) + '.html">';
                    html += '<img src="' + BASE_PATH + escapeHtml(f.image.url) + '" alt="' + escapeHtml(f.image.alt || f.title) + '">';
                    html += '</a>';
                    if (f.image.credit) html += '<div class="credit">' + escapeHtml(f.image.credit) + '</div>';
                    html += '</div>';
                }
                html += '<div class="featured-text">';
                html += '<h3><a href="encyclopedia/' + encodeURIComponent(f.id) + '.html">' + escapeHtml(f.title) + '</a></h3>';
                if (f.abstract) html += '<p class="featured-abstract">' + escapeHtml(f.abstract) + '</p>';
                if (f.content_excerpt) html += '<p class="featured-excerpt">' + escapeHtml(f.content_excerpt) + '</p>';
                html += '<a href="encyclopedia/' + encodeURIComponent(f.id) + '.html" class="featured-readmore">' + escapeHtml(T.enc_read_more) + ' &rarr;</a>';
                html += '</div></div>';
                document.getElementById('featured-section').innerHTML = html;
                document.getElementById('featured-section').style.display = '';
            }

            // --- Render Category Tree (top two levels) ---
            if (json.category_tree) {
                var topKeys = Object.keys(json.category_tree).filter(function(k) { return k !== '_count'; }).sort();
                if (topKeys.length > 0) {
                    var catHtml = '<h2>' + escapeHtml(T.enc_browse_categories) + '</h2>';
                    catHtml += '<ul class="cat-list">';
                    topKeys.forEach(function(key) {
                        var node = json.category_tree[key];
                        var count = node._count || 0;
                        catHtml += '<li><a href="#" data-cat-filter="' + escapeHtml(key) + '">' + escapeHtml(key) + '</a> <span class="cat-count">(' + count + ')</span>';
                        var subKeys = Object.keys(node).filter(function(k) { return k !== '_count'; }).sort();
                        if (subKeys.length > 0) {
                            catHtml += '<ul>';
                            subKeys.forEach(function(sub) {
                                var subCount = (node[sub] || {})._count || 0;
                                catHtml += '<li><a href="#" data-cat-filter="' + escapeHtml(key + '/' + sub) + '">' + escapeHtml(sub) + '</a> <span class="cat-count">(' + subCount + ')</span></li>';
                            });
                            catHtml += '</ul>';
                        }
                        catHtml += '</li>';
                    });
                    catHtml += '</ul>';
                    document.getElementById('categories-section').innerHTML = catHtml;
                    document.getElementById('categories-section').style.display = '';
                }
            }

            // --- Render Highlights (entries with images) ---
            if (json.highlights && json.highlights.length > 0) {
                var hlHtml = '<h2>' + escapeHtml(T.enc_highlights_title) + '</h2>';
                hlHtml += '<div class="highlights-grid">';
                json.highlights.forEach(function(h) {
                    hlHtml += '<a href="encyclopedia/' + encodeURIComponent(h.id) + '.html" class="highlight-card">';
                    if (h.image) {
                        hlHtml += '<img src="' + BASE_PATH + escapeHtml(h.image.url) + '" alt="' + escapeHtml(h.image.alt || h.title) + '" loading="lazy">';
                    }
                    hlHtml += '<span>' + escapeHtml(h.title) + '</span>';
                    hlHtml += '</a>';
                });
                hlHtml += '</div>';
                document.getElementById('highlights-section').innerHTML = hlHtml;
                document.getElementById('highlights-section').style.display = '';
            }

            // --- Populate filter dropdown ---
            var categories = {};
            json.data.forEach(function(item) {
                if (item.categories) item.categories.forEach(function(cat) { categories[cat] = true; });
            });
            Object.keys(categories).sort().forEach(function(cat) {
                addFilterOption(kwFilter, cat);
            });

            // --- Setup search ---
            initSearchPage({
                data: json.data,
                fuseKeys: [
                    { name: 'title', weight: 2 },
                    { name: 'abstract', weight: 1.5 },
                    { name: 'categories', weight: 1 },
                    { name: 'variants', weight: 0.5 }
                ],
                filterField: null,
                filterElementId: 'keyword-filter',
                filterFn: hasCategory,
                renderEntry: function(entry, matchMap) {
                    var html = '<div class="entry">';
                    html += '<div class="entry-header">';
                    html += '<span class="entry-word"><a href="encyclopedia/' + encodeURIComponent(entry.id) + '.html">' + highlightMatches(entry.title, matchMap['title']) + '</a></span>';
                    if (entry.entry_type) {
                        html += '<span class="entry-type-tag">' + escapeHtml(entry.entry_type) + '</span>';
                    }
                    html += '</div>';
                    if (entry.abstract) {
                        html += '<p class="entry-summary">' + highlightMatches(entry.abstract, matchMap['abstract']) + '</p>';
                    }
                    if (entry.variants && entry.variants.length > 0) {
                        html += '<div class="entry-variants">';
                        entry.variants.forEach(function(v) {
                            html += '<span class="variant-tag">' + escapeHtml(v) + '</span>';
                        });
                        html += '</div>';
                    }
                    if (entry.categories && entry.categories.length > 0) {
                        html += '<div class="entry-categories">';
                        entry.categories.forEach(function(cat) {
                            html += '<span class="entry-cat" data-category="' + escapeHtml(cat) + '">' + escapeHtml(cat) + '</span>';
                        });
                        html += '</div>';
                    }
                    html += '</div>';
                    return html;
                },
                onResults: attachCategoryFilters,
                labels: {
                    results: function(n) { return n !== 1 ? T.enc_result_plural : T.enc_result_singular; },
                    emptyTitle: T.enc_empty_title,
                    emptyText: T.enc_empty_text,
                    noResults: T.enc_no_results,
                    noResultsHint: T.enc_no_results_hint,
                    overflow: function(n) { return T.enc_overflow.replace('{n}', n); }
                }
            });

            // --- Toggle portal/results visibility ---
            function updateView() {
                var q = searchInput.value.trim();
                var f = kwFilter.value;
                if (q.length >= 2 || f) {
                    portal.style.display = 'none';
                    resultsEl.style.display = '';
                } else {
                    portal.style.display = '';
                    resultsEl.style.display = 'none';
                }
            }

            searchInput.addEventListener('input', debounce(updateView, 210));
            kwFilter.addEventListener('change', function() { setTimeout(updateView, 10); });

            // --- Category tree click handlers ---
            document.querySelectorAll('[data-cat-filter]').forEach(function(el) {
                el.addEventListener('click', function(e) {
                    e.preventDefault();
                    var cat = this.getAttribute('data-cat-filter');
                    addFilterOption(kwFilter, cat);
                    kwFilter.value = cat;
                    kwFilter.dispatchEvent(new Event('change'));
                });
            });

            // --- Handle ?category= URL param ---
            var urlParams = new URLSearchParams(window.location.search);
            var catParam = urlParams.get('category');
            if (catParam) {
                addFilterOption(kwFilter, catParam);
                kwFilter.value = catParam;
                kwFilter.dispatchEvent(new Event('change'));
            }

            // Ensure correct initial visibility
            updateView();
    })();
    </script>
    <script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
    <script>
    (function() {
        var GRAPH_DATA = {% if graph_data %}{{ graph_data | tojson }}{% else %}null{% endif %};
        var CATEGORY_COLORS = {
            'natureza': '#5B8C5A',
            'cosmologia': '#7B5EA7',
            'ritual': '#C81F2D',
            'cultura-material': '#B7522C',
            'sociedade': '#2E6B8A',
            'linguística': '#8B7355',
            'organização-social': '#D4A03A',
            'nomes-próprios': '#6B8E6B',
            'geografia': '#4A90A4',
            'música': '#C06090',
            'corpo': '#D2691E',
            'etnografia': '#708090',
            'saúde': '#3CB371',
            'território': '#8FBC8F',
            'tradição-oral': '#CD853F'
        };
        var DEFAULT_COLOR = '#999';

        function catColor(cat) {
            if (!cat) return DEFAULT_COLOR;
            var top = cat.replace('cat:', '');
            return CATEGORY_COLORS[top] || DEFAULT_COLOR;
        }

        function nodeColor(d) {
            if (d.type === 'category') return catColor(d.id);
            if (d.categories && d.categories.length > 0) {
                return catColor(d.categories[0].split('/')[0]);
            }
            return DEFAULT_COLOR;
        }

        if (GRAPH_DATA && GRAPH_DATA.nodes) { initGraph(GRAPH_DATA); }

        function initGraph(graph) {
            var container = document.getElementById('graph-container');
            var section = document.getElementById('graph-section');
            if (!container || !section) return;
            section.style.display = '';

            var width = container.clientWidth;
            var height = container.clientHeight;

            // Build lookup maps
            var nodeMap = {};
            graph.nodes.forEach(function(n) { nodeMap[n.id] = n; });

            // Build adjacency
            var adj = {};
            graph.edges.forEach(function(e) {
                if (!adj[e.source]) adj[e.source] = [];
                if (!adj[e.target]) adj[e.target] = [];
                adj[e.source].push(e);
                adj[e.target].push(e);
            });

            // State: which nodes are visible
            var visibleNodes = new Set();
            var visibleEdges = [];
            var expandedCats = new Set();

            // Start with category nodes only
            graph.categories.forEach(function(c) { visibleNodes.add('cat:' + c); });

            // Build filter buttons
            var controlsEl = container.parentElement.querySelector('.graph-controls');
            graph.categories.forEach(function(cat) {
                var btn = document.createElement('button');
                btn.className = 'graph-btn';
                btn.setAttribute('data-graph-filter', cat);
                btn.textContent = cat;
                btn.style.borderColor = catColor(cat);
                btn.addEventListener('click', function() {
                    toggleCategory(cat);
                });
                controlsEl.appendChild(btn);
            });

            // Legend
            var legendEl = document.getElementById('graph-legend');
            var legendItems = [
                { label: T.enc_graph_legend_category || 'Categoria', size: 14, color: 'var(--td-color-primary)' },
                { label: T.enc_graph_legend_entry || 'Verbete', size: 8, color: 'var(--td-color-text-muted)' },
            ];
            legendItems.forEach(function(item) {
                var el = document.createElement('span');
                el.className = 'graph-legend-item';
                el.innerHTML = '<span class="graph-legend-dot" style="width:' + item.size + 'px;height:' + item.size + 'px;background:' + item.color + ';"></span>' + item.label;
                legendEl.appendChild(el);
            });

            // SVG setup
            var svg = d3.select(container).append('svg')
                .attr('width', width)
                .attr('height', height);

            var g = svg.append('g');

            // Zoom
            var zoom = d3.zoom()
                .scaleExtent([0.3, 4])
                .on('zoom', function(event) {
                    g.attr('transform', event.transform);
                });
            svg.call(zoom);

            var linkGroup = g.append('g').attr('class', 'links');
            var nodeGroup = g.append('g').attr('class', 'nodes');

            // Tooltip
            var tooltip = document.getElementById('graph-tooltip');
            var tooltipTitle = document.getElementById('tooltip-title');
            var tooltipCats = document.getElementById('tooltip-cats');
            var tooltipAbstract = document.getElementById('tooltip-abstract');
            var tooltipLink = document.getElementById('tooltip-link');

            // Force simulation
            var simulation = d3.forceSimulation()
                .force('link', d3.forceLink().id(function(d) { return d.id; }).distance(function(d) {
                    if (d.source.type === 'category' || d.target.type === 'category') return 120;
                    return 60;
                }))
                .force('charge', d3.forceManyBody().strength(function(d) {
                    return d.type === 'category' ? -400 : -80;
                }))
                .force('center', d3.forceCenter(width / 2, height / 2))
                .force('collision', d3.forceCollide().radius(function(d) {
                    return d.type === 'category' ? 30 : 8;
                }))
                .on('tick', ticked);

            function getVisibleData() {
                var nodes = [];
                var nodeSet = new Set();
                visibleNodes.forEach(function(id) {
                    if (nodeMap[id]) {
                        nodes.push(nodeMap[id]);
                        nodeSet.add(id);
                    }
                });

                var edges = [];
                graph.edges.forEach(function(e) {
                    var sid = typeof e.source === 'object' ? e.source.id : e.source;
                    var tid = typeof e.target === 'object' ? e.target.id : e.target;
                    if (nodeSet.has(sid) && nodeSet.has(tid)) {
                        edges.push({ source: sid, target: tid, type: e.type });
                    }
                });

                return { nodes: nodes, edges: edges };
            }

            function update() {
                var data = getVisibleData();

                // Links
                var link = linkGroup.selectAll('line').data(data.edges, function(d) {
                    return (d.source.id || d.source) + '-' + (d.target.id || d.target);
                });
                link.exit().remove();
                var linkEnter = link.enter().append('line')
                    .attr('stroke', function(d) {
                        if (d.type === 'see_also') return 'var(--td-color-accent)';
                        if (d.type === 'wikilink') return 'var(--td-color-gold)';
                        return 'var(--td-color-border-dark)';
                    })
                    .attr('stroke-opacity', function(d) {
                        return d.type === 'category' ? 0.3 : 0.6;
                    })
                    .attr('stroke-width', function(d) {
                        return d.type === 'category' ? 1 : 1.5;
                    });
                link = linkEnter.merge(link);

                // Nodes
                var node = nodeGroup.selectAll('g.node').data(data.nodes, function(d) { return d.id; });
                node.exit().remove();

                var nodeEnter = node.enter().append('g')
                    .attr('class', 'node')
                    .style('cursor', 'pointer')
                    .call(d3.drag()
                        .on('start', dragstarted)
                        .on('drag', dragged)
                        .on('end', dragended));

                nodeEnter.append('circle')
                    .attr('r', function(d) { return d.type === 'category' ? 18 : 5; })
                    .attr('fill', function(d) { return nodeColor(d); })
                    .attr('stroke', function(d) { return d.type === 'category' ? 'white' : 'none'; })
                    .attr('stroke-width', function(d) { return d.type === 'category' ? 2 : 0; })
                    .attr('opacity', function(d) { return d.type === 'category' ? 1 : 0.85; });

                nodeEnter.filter(function(d) { return d.type === 'category'; })
                    .append('text')
                    .text(function(d) { return d.title; })
                    .attr('text-anchor', 'middle')
                    .attr('dy', '0.35em')
                    .attr('font-size', '7px')
                    .attr('font-weight', '700')
                    .attr('font-family', 'var(--td-font-headings)')
                    .attr('fill', 'white')
                    .attr('pointer-events', 'none');

                nodeEnter.on('click', function(event, d) {
                    event.stopPropagation();
                    if (d.type === 'category') {
                        toggleCategory(d.title);
                    } else {
                        showTooltip(d, event);
                    }
                });

                nodeEnter.on('mouseenter', function(event, d) {
                    if (d.type === 'entry') {
                        showTooltip(d, event);
                    }
                });

                node = nodeEnter.merge(node);

                // Restart simulation
                simulation.nodes(data.nodes);
                simulation.force('link').links(data.edges);
                simulation.alpha(0.6).restart();
            }

            function ticked() {
                linkGroup.selectAll('line')
                    .attr('x1', function(d) { return d.source.x; })
                    .attr('y1', function(d) { return d.source.y; })
                    .attr('x2', function(d) { return d.target.x; })
                    .attr('y2', function(d) { return d.target.y; });

                nodeGroup.selectAll('g.node')
                    .attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; });
            }

            function toggleCategory(cat) {
                var catId = 'cat:' + cat;
                if (expandedCats.has(cat)) {
                    // Collapse: remove entry nodes that belong only to this category
                    expandedCats.delete(cat);
                    graph.edges.forEach(function(e) {
                        var sid = typeof e.source === 'object' ? e.source.id : e.source;
                        var tid = typeof e.target === 'object' ? e.target.id : e.target;
                        if (e.type === 'category' && tid === catId) {
                            // Check if this entry belongs to another expanded category
                            var keepEntry = false;
                            var entryNode = nodeMap[sid];
                            if (entryNode && entryNode.categories) {
                                entryNode.categories.forEach(function(c) {
                                    if (expandedCats.has(c.split('/')[0])) keepEntry = true;
                                });
                            }
                            if (!keepEntry) visibleNodes.delete(sid);
                        }
                    });
                    // Update button
                    var btns = controlsEl.querySelectorAll('[data-graph-filter="' + cat + '"]');
                    btns.forEach(function(b) { b.classList.remove('active'); });
                } else {
                    // Expand: add entry nodes
                    expandedCats.add(cat);
                    graph.edges.forEach(function(e) {
                        var sid = typeof e.source === 'object' ? e.source.id : e.source;
                        var tid = typeof e.target === 'object' ? e.target.id : e.target;
                        if (e.type === 'category' && tid === catId) {
                            visibleNodes.add(sid);
                        }
                    });
                    // Also add see_also/wikilink targets that are visible
                    var newEntries = new Set();
                    visibleNodes.forEach(function(id) { newEntries.add(id); });
                    graph.edges.forEach(function(e) {
                        var sid = typeof e.source === 'object' ? e.source.id : e.source;
                        var tid = typeof e.target === 'object' ? e.target.id : e.target;
                        if (e.type !== 'category') {
                            if (newEntries.has(sid)) visibleNodes.add(tid);
                            if (newEntries.has(tid)) visibleNodes.add(sid);
                        }
                    });
                    // Update button
                    var btns = controlsEl.querySelectorAll('[data-graph-filter="' + cat + '"]');
                    btns.forEach(function(b) { b.classList.add('active'); });
                }

                // Update 'all' button
                var allBtn = controlsEl.querySelector('[data-graph-filter="all"]');
                if (expandedCats.size === graph.categories.length) {
                    allBtn.classList.add('active');
                } else if (expandedCats.size === 0) {
                    allBtn.classList.add('active');
                } else {
                    allBtn.classList.remove('active');
                }

                update();
            }

            // 'All' button handler
            controlsEl.querySelector('[data-graph-filter="all"]').addEventListener('click', function() {
                if (expandedCats.size === graph.categories.length) {
                    // Collapse all
                    expandedCats.clear();
                    visibleNodes.clear();
                    graph.categories.forEach(function(c) { visibleNodes.add('cat:' + c); });
                    controlsEl.querySelectorAll('.graph-btn').forEach(function(b) { b.classList.remove('active'); });
                    controlsEl.querySelector('[data-graph-filter="all"]').classList.add('active');
                } else {
                    // Expand all
                    graph.categories.forEach(function(cat) {
                        if (!expandedCats.has(cat)) {
                            expandedCats.add(cat);
                            graph.edges.forEach(function(e) {
                                var sid = typeof e.source === 'object' ? e.source.id : e.source;
                                var tid = typeof e.target === 'object' ? e.target.id : e.target;
                                if (e.type === 'category' && tid === 'cat:' + cat) {
                                    visibleNodes.add(sid);
                                }
                            });
                        }
                    });
                    // Add see_also/wikilink connections
                    var allEntries = new Set(visibleNodes);
                    graph.edges.forEach(function(e) {
                        var sid = typeof e.source === 'object' ? e.source.id : e.source;
                        var tid = typeof e.target === 'object' ? e.target.id : e.target;
                        if (e.type !== 'category') {
                            if (allEntries.has(sid)) visibleNodes.add(tid);
                            if (allEntries.has(tid)) visibleNodes.add(sid);
                        }
                    });
                    controlsEl.querySelectorAll('.graph-btn').forEach(function(b) { b.classList.add('active'); });
                }
                update();
            });

            function showTooltip(d, event) {
                tooltipTitle.textContent = d.title || d.id;
                tooltipCats.textContent = (d.categories || []).join(' · ');
                tooltipAbstract.textContent = d.abstract || '';
                tooltipAbstract.style.display = d.abstract ? '' : 'none';

                if (d.has_content) {
                    tooltipLink.href = 'encyclopedia/' + encodeURIComponent(d.id) + '.html';
                    tooltipLink.style.display = '';
                } else {
                    tooltipLink.style.display = 'none';
                }

                tooltip.classList.add('visible');

                // Position
                var rect = container.getBoundingClientRect();
                var x = event.clientX - rect.left + 15;
                var y = event.clientY - rect.top - 10;

                // Prevent overflow
                if (x + 290 > rect.width) x = x - 310;
                if (y + 150 > rect.height) y = y - 150;
                if (x < 0) x = 10;
                if (y < 0) y = 10;

                tooltip.style.left = x + 'px';
                tooltip.style.top = y + 'px';
            }

            // Hide tooltip on click outside
            svg.on('click', function() {
                tooltip.classList.remove('visible');
            });

            function dragstarted(event, d) {
                if (!event.active) simulation.alphaTarget(0.3).restart();
                d.fx = d.x;
                d.fy = d.y;
            }

            function dragged(event, d) {
                d.fx = event.x;
                d.fy = event.y;
            }

            function dragended(event, d) {
                if (!event.active) simulation.alphaTarget(0);
                d.fx = null;
                d.fy = null;
            }

            // Handle resize
            window.addEventListener('resize', function() {
                width = container.clientWidth;
                height = container.clientHeight;
                svg.attr('width', width).attr('height', height);
                simulation.force('center', d3.forceCenter(width / 2, height / 2));
                simulation.alpha(0.3).restart();
            });

            // Initial render
            update();
        }
    })();
    </script>
{% endblock %}
