{% extends "base.html" %}
{% set active_page = "authors" %}

{% block title %}Author Network — IETF Draft Analyzer{% endblock %}

{% block extra_head %}
<script src="/static/js/plotly.min.js"></script>
<script src="/static/js/d3.v7.min.js"></script>
<style>
    #networkSvg {
        width: 100%;
        height: 600px;
        cursor: grab;
    }
    #networkSvg:active { cursor: grabbing; }
    #networkSvg .node { cursor: pointer; }
    #networkSvg .node circle { stroke: rgba(255,255,255,0.15); stroke-width: 1.5px; transition: r 0.2s; }
    #networkSvg .node:hover circle { stroke: #60a5fa; stroke-width: 2.5px; }
    #networkSvg .node text { pointer-events: none; }
    #networkSvg .link { stroke-opacity: 0.25; }
    #networkSvg .link:hover { stroke-opacity: 0.7; }
    .tooltip-card {
        position: absolute; pointer-events: none; z-index: 50;
        background: #1e293b; border: 1px solid #334155; border-radius: 8px;
        padding: 10px 14px; font-size: 12px; color: #e2e8f0;
        box-shadow: 0 8px 24px rgba(0,0,0,0.4);
        max-width: 280px; opacity: 0; transition: opacity 0.15s;
    }
    .tooltip-card.visible { opacity: 1; }
    .legend-swatch { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
    .cluster-card { transition: all 0.2s; }
    .cluster-card:hover { border-color: #3b82f6 !important; }
    .filter-btn { transition: all 0.15s; }
    .filter-btn:hover { background: rgba(59, 130, 246, 0.2); }
    .filter-btn.active { background: rgba(59, 130, 246, 0.3); border-color: #3b82f6; color: #60a5fa; }
</style>
{% endblock %}

{% block content %}
<div class="mb-6">
    <h1 class="text-2xl font-bold text-white">Author Network</h1>
    <p class="text-slate-400 text-sm mt-1">Interactive collaboration graph of {{ network.nodes | length }} authors across {{ orgs | length }} organizations. Authors are connected when they co-authored 2+ drafts together. Node size reflects number of drafts authored; color represents organization. Clusters are detected via connected-component analysis (BFS) — authors in the same cluster share direct or indirect co-authorship links.</p>
</div>

<!-- Summary stats -->
<div class="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6">
    <div class="stat-card rounded-xl border border-slate-800 p-4 relative overflow-hidden">
        <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-blue-500 to-blue-400"></div>
        <div class="text-xs text-slate-500 uppercase tracking-wider">Authors Shown</div>
        <div class="text-2xl font-bold text-white mt-1">{{ network.nodes | length }}</div>
    </div>
    <div class="stat-card rounded-xl border border-slate-800 p-4 relative overflow-hidden">
        <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-purple-500 to-purple-400"></div>
        <div class="text-xs text-slate-500 uppercase tracking-wider">Organizations</div>
        <div class="text-2xl font-bold text-white mt-1">{{ orgs | length }}</div>
    </div>
    <div class="stat-card rounded-xl border border-slate-800 p-4 relative overflow-hidden">
        <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-emerald-500 to-emerald-400"></div>
        <div class="text-xs text-slate-500 uppercase tracking-wider">Co-Author Links</div>
        <div class="text-2xl font-bold text-white mt-1">{{ network.edges | length }}</div>
    </div>
    <div class="stat-card rounded-xl border border-slate-800 p-4 relative overflow-hidden">
        <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-amber-500 to-amber-400"></div>
        <div class="text-xs text-slate-500 uppercase tracking-wider">Clusters</div>
        <div class="text-2xl font-bold text-white mt-1">{{ network.clusters | length }}</div>
    </div>
    <div class="stat-card rounded-xl border border-slate-800 p-4 relative overflow-hidden">
        <div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-rose-500 to-rose-400"></div>
        <div class="text-xs text-slate-500 uppercase tracking-wider">Multi-Draft</div>
        <div class="text-2xl font-bold text-white mt-1">{{ authors | selectattr('draft_count', 'gt', 1) | list | length }}</div>
    </div>
</div>

<!-- D3 Force-directed Network Graph -->
<div class="bg-slate-900 rounded-xl border border-slate-800 p-5 mb-6 relative">
    <div class="flex flex-wrap items-center justify-between gap-3 mb-3">
        <div>
            <h2 class="text-sm font-semibold text-slate-300">Co-Authorship Network</h2>
            <p class="text-xs text-slate-500 mt-0.5">Node size = draft count. Color = organization. Edge thickness = shared drafts. Drag nodes to rearrange. Scroll to zoom.</p>
        </div>
        <div class="flex gap-2 items-center">
            <button id="resetZoom" class="text-xs px-3 py-1.5 rounded-lg border border-slate-700 text-slate-400 hover:text-white hover:border-slate-500 transition">Reset View</button>
            <select id="highlightOrg" class="text-xs px-3 py-1.5 rounded-lg border border-slate-700 bg-slate-800 text-slate-300 focus:outline-none focus:border-blue-500">
                <option value="">All Organizations</option>
            </select>
        </div>
    </div>
    <div class="relative">
        <svg id="networkSvg"></svg>
        <div id="tooltip" class="tooltip-card"></div>
    </div>
    <!-- Legend -->
    <div id="legend" class="flex flex-wrap gap-3 mt-3 pt-3 border-t border-slate-800"></div>
</div>

<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
    <!-- Organization bar chart -->
    <div class="bg-slate-900 rounded-xl border border-slate-800 p-5">
        <h2 class="text-sm font-semibold text-slate-300 mb-1">Organizations by Draft Count</h2>
        <p class="text-xs text-slate-500 mb-3">Color intensity = number of authors from that org.</p>
        <div id="orgChart" style="height: 500px;"></div>
    </div>

    <!-- Cross-org collaboration -->
    <div class="bg-slate-900 rounded-xl border border-slate-800 p-5">
        <h2 class="text-sm font-semibold text-slate-300 mb-1">Cross-Organization Collaboration</h2>
        <p class="text-xs text-slate-500 mb-3">Organizations co-authoring drafts together.</p>
        <div id="crossOrgChart" style="height: 500px;"></div>
    </div>
</div>

<!-- Collaboration Clusters -->
{% if network.clusters %}
<div class="bg-slate-900 rounded-xl border border-slate-800 p-5 mb-6">
    <h2 class="text-sm font-semibold text-slate-300 mb-1">Collaboration Clusters</h2>
    <p class="text-xs text-slate-500 mb-4">Clusters are formed by connected-component analysis of the co-authorship graph: authors who share 2+ drafts are linked, and all authors reachable through such links form a cluster. This reveals research teams and institutional collaboration patterns — a cluster of 20 authors from 3 organizations means those groups actively co-author across org boundaries. Click a cluster to highlight it in the graph above.</p>
    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" id="clusterGrid">
        {% for c in network.clusters[:12] %}
        <div class="cluster-card bg-slate-800/50 rounded-lg border border-slate-700/50 p-4 cursor-pointer" data-cluster-id="{{ c.id }}" onclick="highlightCluster({{ c.id }})">
            <div class="flex items-center justify-between mb-2">
                <span class="text-sm font-semibold text-white">Cluster #{{ c.id + 1 }}</span>
                <div class="flex gap-1.5">
                    <span class="text-xs px-2 py-0.5 rounded-full bg-blue-500/20 text-blue-400">{{ c.size }} authors</span>
                    <span class="text-xs px-2 py-0.5 rounded-full bg-emerald-500/20 text-emerald-400">{{ c.draft_count }} drafts</span>
                </div>
            </div>
            <div class="flex flex-wrap gap-1 mb-2">
                {% for org, count in c.org_mix.items() %}
                <span class="text-xs px-2 py-0.5 rounded-full bg-slate-700 text-slate-300">{{ org }} ({{ count }})</span>
                {% endfor %}
            </div>
            <div class="text-xs text-slate-500 mb-2 truncate" title="{{ c.members | join(', ') }}">
                {{ c.members[:5] | join(', ') }}{% if c.members | length > 5 %} +{{ c.members | length - 5 }} more{% endif %}
            </div>
            {% if c.drafts %}
            <div class="border-t border-slate-700/50 pt-2 mt-2">
                {% for d in c.drafts[:5] %}
                <div class="text-xs truncate mb-0.5" title="{{ d.name }}: {{ d.title }}">
                    <a href="/drafts/{{ d.name }}" class="text-blue-400/70 hover:text-blue-300 transition" onclick="event.stopPropagation()">{{ d.title }}</a>
                </div>
                {% endfor %}
                {% if c.draft_count > 5 %}
                <div class="text-xs text-slate-600 mt-1">+{{ c.draft_count - 5 }} more drafts</div>
                {% endif %}
            </div>
            {% endif %}
        </div>
        {% endfor %}
    </div>
</div>
{% endif %}

<!-- Top Authors Table and Org Stats side by side -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
    <!-- Top authors table -->
    <div class="lg:col-span-2 bg-slate-900 rounded-xl border border-slate-800 overflow-hidden">
        <div class="p-4 border-b border-slate-800 flex items-center justify-between">
            <h2 class="text-sm font-semibold text-slate-300">Top Authors</h2>
            <span class="text-xs text-slate-500">Showing top {{ authors | length }}</span>
        </div>
        <div class="overflow-x-auto max-h-[600px] overflow-y-auto">
            <table class="w-full text-sm" id="authorsTable">
                <thead class="sticky top-0 z-10">
                    <tr class="border-b border-slate-800 bg-slate-900">
                        <th class="px-4 py-2.5 text-left text-xs font-medium text-slate-400 cursor-pointer hover:text-slate-200" data-sort="index">#</th>
                        <th class="px-4 py-2.5 text-left text-xs font-medium text-slate-400 cursor-pointer hover:text-slate-200" data-sort="name">Author</th>
                        <th class="px-4 py-2.5 text-left text-xs font-medium text-slate-400 cursor-pointer hover:text-slate-200" data-sort="org">Organization</th>
                        <th class="px-4 py-2.5 text-right text-xs font-medium text-slate-400 cursor-pointer hover:text-slate-200" data-sort="drafts">Drafts</th>
                    </tr>
                </thead>
                <tbody class="divide-y divide-slate-800/50" id="authorsBody">
                    {% for a in authors %}
                    <tr class="hover:bg-slate-800/50 transition author-row" data-name="{{ a.name }}" data-org="{{ a.affiliation }}" data-count="{{ a.draft_count }}">
                        <td class="px-4 py-2.5 text-slate-500 text-xs">{{ loop.index }}</td>
                        <td class="px-4 py-2.5">
                            <a href="/drafts?q={{ a.name | urlencode }}" class="text-blue-400 hover:text-blue-300 transition">{{ a.name }}</a>
                        </td>
                        <td class="px-4 py-2.5 text-slate-500 text-xs truncate max-w-[200px]" title="{{ a.affiliation }}">{{ a.affiliation }}</td>
                        <td class="px-4 py-2.5 text-right">
                            <span class="px-2 py-0.5 rounded-full text-xs font-medium
                                {% if a.draft_count >= 5 %}bg-green-500/20 text-green-400
                                {% elif a.draft_count >= 3 %}bg-blue-500/20 text-blue-400
                                {% else %}bg-slate-700/50 text-slate-400{% endif %}">
                                {{ a.draft_count }}
                            </span>
                        </td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>

    <!-- Organization stats cards -->
    <div class="bg-slate-900 rounded-xl border border-slate-800 overflow-hidden">
        <div class="p-4 border-b border-slate-800">
            <h2 class="text-sm font-semibold text-slate-300">Organization Stats</h2>
        </div>
        <div class="max-h-[600px] overflow-y-auto divide-y divide-slate-800/50">
            {% for o in orgs %}
            <div class="px-4 py-3 hover:bg-slate-800/30 transition cursor-pointer org-card" data-org="{{ o.org }}" onclick="filterByOrg('{{ o.org | e }}')">
                <div class="flex items-center justify-between mb-1">
                    <span class="text-sm text-slate-200 font-medium truncate max-w-[180px]" title="{{ o.org }}">{{ o.org }}</span>
                    <span class="text-xs px-2 py-0.5 rounded-full bg-blue-500/20 text-blue-400">{{ o.draft_count }} drafts</span>
                </div>
                <div class="flex items-center gap-3 text-xs text-slate-500">
                    <span>{{ o.author_count }} author{{ 's' if o.author_count != 1 }}</span>
                    <span class="text-slate-700">|</span>
                    <span>{{ (o.draft_count / o.author_count) | round(1) }} drafts/author</span>
                </div>
                <div class="mt-1.5 w-full bg-slate-800 rounded-full h-1.5">
                    <div class="bg-blue-500/60 h-1.5 rounded-full" style="width: {{ (o.draft_count / orgs[0].draft_count * 100) | round }}%"></div>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
</div>
{% endblock %}

{% block extra_scripts %}
<script>
// --- Shared Plotly config ---
const PLOTLY_LAYOUT = {
    paper_bgcolor: 'transparent',
    plot_bgcolor: 'transparent',
    font: { color: '#94a3b8', family: 'Inter, system-ui, sans-serif', size: 12 },
    margin: { t: 20, r: 20, b: 40, l: 50 },
    xaxis: { gridcolor: '#1e293b', zerolinecolor: '#334155' },
    yaxis: { gridcolor: '#1e293b', zerolinecolor: '#334155' },
};
const CFG = { responsive: true, displayModeBar: false };

const PALETTE = [
    '#3b82f6', '#ef4444', '#22c55e', '#a855f7', '#f59e0b',
    '#06b6d4', '#ec4899', '#84cc16', '#f97316', '#8b5cf6',
    '#14b8a6', '#e11d48', '#64748b', '#eab308', '#6366f1',
    '#fb923c', '#34d399', '#c084fc', '#38bdf8', '#fbbf24',
];

// --- Data from server ---
const network = {{ network | tojson }};

// ===========================================================
//  D3.js Force-Directed Co-Authorship Network
// ===========================================================
(function() {
    if (network.nodes.length === 0) {
        document.getElementById('networkSvg').outerHTML =
            '<p class="text-slate-500 text-sm text-center py-20">No co-authorship data available</p>';
        return;
    }

    const svg = d3.select('#networkSvg');
    const container = svg.node().parentElement;
    const width = container.clientWidth;
    const height = 600;
    svg.attr('viewBox', [0, 0, width, height]);

    // Build org color map (top orgs by frequency)
    const orgCounts = {};
    network.nodes.forEach(n => {
        if (n.org) orgCounts[n.org] = (orgCounts[n.org] || 0) + 1;
    });
    const orgsSorted = Object.entries(orgCounts).sort((a,b) => b[1] - a[1]);
    const orgColor = {};
    orgsSorted.forEach(([org], i) => {
        orgColor[org] = i < PALETTE.length ? PALETTE[i] : '#475569';
    });

    // Populate org dropdown
    const orgSelect = document.getElementById('highlightOrg');
    orgsSorted.slice(0, 30).forEach(([org, cnt]) => {
        const opt = document.createElement('option');
        opt.value = org;
        opt.textContent = `${org} (${cnt})`;
        orgSelect.appendChild(opt);
    });

    // Populate legend
    const legendEl = document.getElementById('legend');
    orgsSorted.slice(0, 12).forEach(([org]) => {
        const item = document.createElement('div');
        item.className = 'flex items-center gap-1.5 text-xs text-slate-400';
        item.innerHTML = `<span class="legend-swatch" style="background:${orgColor[org]}"></span>${org}`;
        legendEl.appendChild(item);
    });

    // Build cluster lookup
    const clusterOf = {};
    (network.clusters || []).forEach(c => {
        c.members.forEach(m => { clusterOf[m] = c.id; });
    });

    // Prepare simulation data (deep copy + initial positions to prevent explosion)
    const nodes = network.nodes.map((n, i) => ({
        ...n,
        x: width / 2 + (Math.cos(i * 2 * Math.PI / network.nodes.length) * Math.min(width, height) * 0.35),
        y: height / 2 + (Math.sin(i * 2 * Math.PI / network.nodes.length) * Math.min(width, height) * 0.35),
    }));
    const links = network.edges.map(e => ({
        source: e.source,
        target: e.target,
        weight: e.weight,
    }));

    // Size scale
    const maxDrafts = d3.max(nodes, n => n.draft_count) || 1;
    const rScale = d3.scaleSqrt().domain([1, maxDrafts]).range([4, 22]);

    // Force simulation — tuned for large graphs (498 nodes, 1142 edges)
    const nNodes = nodes.length;
    const chargeStrength = nNodes > 300 ? -60 : nNodes > 100 ? -100 : -120;

    const simulation = d3.forceSimulation(nodes)
        .force('link', d3.forceLink(links)
            .id(d => d.id)
            .distance(d => 40 + 60 / Math.sqrt(d.weight))
            .strength(d => Math.min(0.15, 0.05 * d.weight))
        )
        .force('charge', d3.forceManyBody().strength(chargeStrength).distanceMax(400))
        .force('center', d3.forceCenter(width / 2, height / 2))
        .force('collision', d3.forceCollide().radius(d => rScale(d.draft_count) + 2).strength(0.7))
        .force('x', d3.forceX(width / 2).strength(0.02))
        .force('y', d3.forceY(height / 2).strength(0.02))
        .alphaDecay(0.02)
        .velocityDecay(0.4);

    // Zoom behavior
    const g = svg.append('g');
    const zoom = d3.zoom()
        .scaleExtent([0.2, 5])
        .on('zoom', (event) => g.attr('transform', event.transform));
    svg.call(zoom);

    document.getElementById('resetZoom').addEventListener('click', () => {
        svg.transition().duration(500).call(zoom.transform, d3.zoomIdentity);
    });

    // Draw edges
    const linkGroup = g.append('g').attr('class', 'links');
    const link = linkGroup.selectAll('line')
        .data(links)
        .join('line')
        .attr('class', 'link')
        .attr('stroke', '#475569')
        .attr('stroke-width', d => Math.max(1, d.weight * 1.5));

    // Draw nodes
    const nodeGroup = g.append('g').attr('class', 'nodes');
    const node = nodeGroup.selectAll('g')
        .data(nodes)
        .join('g')
        .attr('class', 'node')
        .call(d3.drag()
            .on('start', dragStarted)
            .on('drag', dragged)
            .on('end', dragEnded)
        );

    node.append('circle')
        .attr('r', d => rScale(d.draft_count))
        .attr('fill', d => orgColor[d.org] || '#475569')
        .attr('opacity', 0.85);

    // Labels for nodes with 3+ drafts
    node.filter(d => d.draft_count >= 3)
        .append('text')
        .text(d => {
            const parts = d.name.split(' ');
            return parts[parts.length - 1];
        })
        .attr('dy', d => -(rScale(d.draft_count) + 4))
        .attr('text-anchor', 'middle')
        .attr('fill', '#94a3b8')
        .attr('font-size', '9px')
        .attr('font-family', 'Inter, system-ui, sans-serif');

    // Tooltip
    const tooltip = document.getElementById('tooltip');

    node.on('mouseover', function(event, d) {
        const draftList = (d.drafts || []).slice(0, 5).map(dn => {
            const short = dn.replace(/^draft-/, '');
            return `<div class="truncate text-slate-400">${short}</div>`;
        }).join('');
        const moreCount = (d.drafts || []).length > 5 ? `<div class="text-slate-500 mt-1">+${d.drafts.length - 5} more</div>` : '';

        tooltip.innerHTML = `
            <div class="font-semibold text-white mb-1">${d.name}</div>
            <div class="text-slate-400 text-xs mb-2">${d.org || 'Unknown org'}</div>
            <div class="flex gap-4 text-xs mb-2">
                <span><span class="text-blue-400 font-medium">${d.draft_count}</span> drafts</span>
                <span>avg <span class="text-emerald-400 font-medium">${d.avg_score}</span></span>
            </div>
            <div class="text-xs">${draftList}${moreCount}</div>
        `;
        tooltip.classList.add('visible');

        // Highlight connected
        const connected = new Set();
        links.forEach(l => {
            const sid = typeof l.source === 'object' ? l.source.id : l.source;
            const tid = typeof l.target === 'object' ? l.target.id : l.target;
            if (sid === d.id) connected.add(tid);
            if (tid === d.id) connected.add(sid);
        });
        connected.add(d.id);

        node.select('circle')
            .attr('opacity', n => connected.has(n.id) ? 1 : 0.15);
        node.selectAll('text')
            .attr('opacity', n => connected.has(n.id) ? 1 : 0.15);
        link
            .attr('stroke-opacity', l => {
                const sid = typeof l.source === 'object' ? l.source.id : l.source;
                const tid = typeof l.target === 'object' ? l.target.id : l.target;
                return (sid === d.id || tid === d.id) ? 0.7 : 0.03;
            });
    })
    .on('mousemove', function(event) {
        const rect = container.getBoundingClientRect();
        tooltip.style.left = (event.clientX - rect.left + 15) + 'px';
        tooltip.style.top = (event.clientY - rect.top - 10) + 'px';
    })
    .on('mouseout', function() {
        tooltip.classList.remove('visible');
        node.select('circle').attr('opacity', 0.85);
        node.selectAll('text').attr('opacity', 1);
        link.attr('stroke-opacity', 0.25);
    })
    .on('click', function(event, d) {
        // Navigate to drafts search for this author
        window.open(`/drafts?q=${encodeURIComponent(d.name)}`, '_blank');
    });

    // Tick handler
    simulation.on('tick', () => {
        link
            .attr('x1', d => d.source.x)
            .attr('y1', d => d.source.y)
            .attr('x2', d => d.target.x)
            .attr('y2', d => d.target.y);
        node.attr('transform', d => `translate(${d.x},${d.y})`);
    });

    // Drag handlers
    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;
    }

    // Org filter dropdown
    orgSelect.addEventListener('change', function() {
        const org = this.value;
        if (!org) {
            node.select('circle').attr('opacity', 0.85);
            node.selectAll('text').attr('opacity', 1);
            link.attr('stroke-opacity', 0.25);
            return;
        }
        const inOrg = new Set(nodes.filter(n => n.org === org).map(n => n.id));
        node.select('circle')
            .attr('opacity', n => inOrg.has(n.id) ? 1 : 0.08);
        node.selectAll('text')
            .attr('opacity', n => inOrg.has(n.id) ? 1 : 0.08);
        link.attr('stroke-opacity', l => {
            const sid = typeof l.source === 'object' ? l.source.id : l.source;
            const tid = typeof l.target === 'object' ? l.target.id : l.target;
            return (inOrg.has(sid) && inOrg.has(tid)) ? 0.6 : 0.02;
        });
    });

    // Expose cluster highlighting globally
    window.highlightCluster = function(clusterId) {
        const cluster = (network.clusters || []).find(c => c.id === clusterId);
        if (!cluster) return;
        const members = new Set(cluster.members);

        // Reset org dropdown
        orgSelect.value = '';

        // Highlight matching nodes and dim others
        node.select('circle')
            .transition().duration(300)
            .attr('opacity', n => members.has(n.id) || members.has(n.name) ? 1 : 0.08);
        node.selectAll('text')
            .transition().duration(300)
            .attr('opacity', n => members.has(n.id) || members.has(n.name) ? 1 : 0.08);
        link.transition().duration(300)
            .attr('stroke-opacity', l => {
                const sid = typeof l.source === 'object' ? l.source.id : l.source;
                const tid = typeof l.target === 'object' ? l.target.id : l.target;
                return (members.has(sid) && members.has(tid)) ? 0.7 : 0.02;
            });

        // Highlight cluster card
        document.querySelectorAll('.cluster-card').forEach(c => {
            const isActive = c.dataset.clusterId == clusterId;
            c.style.borderColor = isActive ? '#3b82f6' : '';
        });

        // Zoom to fit cluster members (only if simulation has settled enough)
        const clusterNodes = nodes.filter(n =>
            (members.has(n.id) || members.has(n.name)) && isFinite(n.x) && isFinite(n.y));
        if (clusterNodes.length > 1) {
            const xs = clusterNodes.map(n => n.x);
            const ys = clusterNodes.map(n => n.y);
            const pad = 80;
            const x0 = Math.min(...xs) - pad, x1 = Math.max(...xs) + pad;
            const y0 = Math.min(...ys) - pad, y1 = Math.max(...ys) + pad;
            const dx = x1 - x0, dy = y1 - y0;
            if (dx > 0 && dy > 0) {
                const scale = Math.min(width / dx, height / dy, 3);
                const cx = (x0 + x1) / 2, cy = (y0 + y1) / 2;
                svg.transition().duration(750).call(
                    zoom.transform,
                    d3.zoomIdentity.translate(width/2, height/2).scale(scale).translate(-cx, -cy)
                );
            }
        }
    };

    // Filter by org (called from org stats cards)
    window.filterByOrg = function(org) {
        orgSelect.value = org;
        orgSelect.dispatchEvent(new Event('change'));
    };
})();

// ===========================================================
//  Organization Bar Chart (Plotly)
// ===========================================================
const orgsData = {{ orgs_data | tojson }};
const orgNames = orgsData.map(o => o.org).reverse();
const orgDrafts = orgsData.map(o => o.draft_count).reverse();
const orgAuthors = orgsData.map(o => o.author_count).reverse();

Plotly.newPlot('orgChart', [{
    y: orgNames, x: orgDrafts,
    type: 'bar', orientation: 'h',
    marker: {
        color: orgAuthors,
        colorscale: [[0, '#1e3a5f'], [0.5, '#3b82f6'], [1, '#60a5fa']],
        showscale: true,
        colorbar: {
            title: { text: 'Authors', font: { color: '#94a3b8', size: 10 } },
            tickfont: { color: '#94a3b8', size: 10 },
            thickness: 12, len: 0.5,
        },
    },
    text: orgDrafts.map((d, i) => `${d} drafts, ${orgAuthors[i]} authors`),
    textposition: 'none',
    hovertemplate: '<b>%{y}</b><br>%{text}<extra></extra>',
}], {
    ...PLOTLY_LAYOUT,
    margin: { t: 10, r: 80, b: 40, l: 180 },
    xaxis: { ...PLOTLY_LAYOUT.xaxis, title: { text: 'Draft Count', font: { size: 11 } } },
}, CFG);

// ===========================================================
//  Cross-Org Collaboration Chart (Plotly)
// ===========================================================
const crossOrg = {{ cross_org | tojson }};
if (crossOrg.length > 0) {
    const coLabels = crossOrg.map(c => `${c.org_a} + ${c.org_b}`).reverse();
    const coValues = crossOrg.map(c => c.shared_drafts).reverse();

    Plotly.newPlot('crossOrgChart', [{
        y: coLabels, x: coValues,
        type: 'bar', orientation: 'h',
        marker: {
            color: coValues.map((v, i) => {
                const pct = i / Math.max(coValues.length - 1, 1);
                return `hsl(${160 + pct * 60}, 65%, 50%)`;
            }),
        },
        hovertemplate: '<b>%{y}</b><br>%{x} shared draft(s)<extra></extra>',
    }], {
        ...PLOTLY_LAYOUT,
        margin: { t: 10, r: 40, b: 40, l: 240 },
        xaxis: { ...PLOTLY_LAYOUT.xaxis, title: { text: 'Shared Drafts', font: { size: 11 } }, dtick: 1 },
        yaxis: { ...PLOTLY_LAYOUT.yaxis, automargin: true, tickfont: { size: 10 } },
    }, CFG);
} else {
    document.getElementById('crossOrgChart').innerHTML =
        '<p class="text-slate-500 text-sm text-center mt-20">No cross-org data available</p>';
}

// ===========================================================
//  Sortable Authors Table
// ===========================================================
(function() {
    const table = document.getElementById('authorsTable');
    const tbody = document.getElementById('authorsBody');
    let sortCol = null, sortAsc = true;

    table.querySelectorAll('th[data-sort]').forEach(th => {
        th.addEventListener('click', () => {
            const col = th.dataset.sort;
            if (sortCol === col) { sortAsc = !sortAsc; } else { sortCol = col; sortAsc = true; }

            table.querySelectorAll('th[data-sort]').forEach(h =>
                h.textContent = h.textContent.replace(/ [▲▼]/, ''));
            th.textContent += sortAsc ? ' ▲' : ' ▼';

            const rows = Array.from(tbody.querySelectorAll('tr'));
            rows.sort((a, b) => {
                let va, vb;
                if (col === 'name') { va = a.dataset.name.toLowerCase(); vb = b.dataset.name.toLowerCase(); }
                else if (col === 'org') { va = a.dataset.org.toLowerCase(); vb = b.dataset.org.toLowerCase(); }
                else if (col === 'drafts') { va = parseInt(a.dataset.count); vb = parseInt(b.dataset.count); }
                else { va = parseInt(a.cells[0].textContent); vb = parseInt(b.cells[0].textContent); }

                if (typeof va === 'number') return sortAsc ? va - vb : vb - va;
                return sortAsc ? va.localeCompare(vb) : vb.localeCompare(va);
            });
            rows.forEach(r => tbody.appendChild(r));
        });
    });
})();
</script>
{% endblock %}
