(function () {
  const NS = 'http://www.w3.org/2000/svg';
  const TIER_ROWS = [4, 5, 7];
  const MIN_NODE_GAP = 148;
  const VIEW_W = 1200;
  const VIEW_H = 860;

  /** Korte rol-context per agent (NL) */
  const AGENT_HINTS = {
    marketing: { short: 'Campagnes', domain: 'Groei' },
    retail: { short: 'Supermarkten', domain: 'Groei' },
    bizdev: { short: 'Deals', domain: 'Groei' },
    research: { short: 'Marktintel', domain: 'Groei' },
    browser: { short: 'Web research', domain: 'Groei' },
    webbuilder: { short: 'Websites', domain: 'Product' },
    product: { short: 'Assortiment', domain: 'Product' },
    packaging: { short: 'Verpakking', domain: 'Product' },
    design: { short: 'Visuals', domain: 'Product' },
    halal: { short: 'Compliance', domain: 'Product' },
    sourcing: { short: 'Inkoop', domain: 'Product' },
    finance: { short: 'Cijfers', domain: 'Operatie' },
    hr: { short: 'Team', domain: 'Operatie' },
    knowledge: { short: 'Kennisbank', domain: 'Operatie' },
    email: { short: 'Inbox', domain: 'Operatie' },
    sysops: { short: 'Infra', domain: 'Operatie' },
  };

  const CLUSTERS = {
    growth: {
      label: 'Groei',
      icon: '📈',
      color: 'rgba(0, 229, 255, 0.14)',
      stroke: 'rgba(0, 229, 255, 0.35)',
      agents: ['marketing', 'retail', 'bizdev', 'research', 'browser'],
    },
    product: {
      label: 'Product',
      icon: '📦',
      color: 'rgba(168, 85, 247, 0.12)',
      stroke: 'rgba(168, 85, 247, 0.35)',
      agents: ['product', 'packaging', 'design', 'halal', 'sourcing', 'webbuilder'],
    },
    ops: {
      label: 'Operatie',
      icon: '⚙️',
      color: 'rgba(34, 197, 94, 0.1)',
      stroke: 'rgba(34, 197, 94, 0.32)',
      agents: ['finance', 'hr', 'knowledge', 'email', 'sysops'],
    },
  };

  /** @type {Record<string, object>} */
  const COMM_MODES = {
    pyramid: {
      id: 'pyramid',
      label: 'Piramide',
      subtitle: 'Herman als hub',
      icon: '◆',
      desc: 'Rapport ↑ en delegatie ↓. Alleen live handoffs tussen agents.',
      layout: 'pyramid',
      showExecutive: true,
      showReport: true,
      showDelegate: true,
      showLivePeers: true,
      showStaticPeers: false,
      activeOnly: false,
      edgeBend: 0.42,
    },
    mesh: {
      id: 'mesh',
      label: 'Vol netwerk',
      subtitle: 'Alle partners',
      icon: '⬡',
      desc: 'Volledige samenwerkingsmatrix — wie met wie kan werken.',
      layout: 'pyramid',
      showExecutive: true,
      showReport: true,
      showDelegate: true,
      showLivePeers: true,
      showStaticPeers: true,
      activeOnly: false,
      staticPeerOpacity: 0.35,
      edgeBend: 0.38,
    },
    org: {
      id: 'org',
      label: 'Organigram',
      subtitle: 'Pure hiërarchie',
      icon: '▤',
      desc: 'CEO → Herman → agents. Geen peer-lijnen, overzichtelijk.',
      layout: 'pyramid',
      showExecutive: true,
      showReport: true,
      showDelegate: true,
      showLivePeers: false,
      showStaticPeers: false,
      activeOnly: false,
      edgeBend: 0.12,
      straightExec: true,
    },
    pulse: {
      id: 'pulse',
      label: 'Live stream',
      subtitle: 'Alleen actief',
      icon: '◎',
      desc: 'Geen stille lijnen — alleen wat nu beweegt (pulse).',
      layout: 'pyramid',
      showExecutive: true,
      showReport: true,
      showDelegate: true,
      showLivePeers: true,
      showStaticPeers: false,
      activeOnly: true,
      edgeBend: 0.45,
    },
    clusters: {
      id: 'clusters',
      label: 'Domeinen',
      subtitle: 'Per afdeling',
      icon: '◉',
      desc: 'Groei · Product · Operatie — agents gegroepeerd rond Herman.',
      layout: 'clusters',
      showExecutive: true,
      showReport: true,
      showDelegate: true,
      showLivePeers: true,
      showStaticPeers: true,
      activeOnly: false,
      staticPeerOpacity: 0.2,
      edgeBend: 0.5,
      intraClusterOnly: false,
    },
  };

  const STORAGE_KEY = 'foodlinkk_mesh_comm_mode';
  let cachedData = null;
  let pickerBound = false;
  let onModeChangeCb = null;
  let lastMountOpts = {};

  function make(tag, attrs) {
    attrs = attrs || {};
    const el = document.createElementNS(NS, tag);
    Object.entries(attrs).forEach(function (pair) {
      el.setAttribute(pair[0], String(pair[1]));
    });
    return el;
  }

  function clear(el) {
    while (el.firstChild) el.removeChild(el.firstChild);
  }

  function curvePath(x1, y1, x2, y2, bend) {
    if (bend != null && bend < 0.18) {
      return 'M ' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2;
    }
    const midY = y1 + (y2 - y1) * (bend != null ? bend : 0.42);
    return 'M ' + x1 + ' ' + y1 + ' C ' + x1 + ' ' + midY + ', ' + x2 + ' ' + midY + ', ' + x2 + ' ' + y2;
  }

  function clusterOf(key) {
    const k = (key || '').toLowerCase();
    return (
      Object.keys(CLUSTERS).find(function (cid) {
        return CLUSTERS[cid].agents.indexOf(k) >= 0;
      }) || null
    );
  }

  function spreadTier(nodes, width, minGap) {
    if (nodes.length <= 1) return;
    nodes.sort(function (a, b) {
      return a._x - b._x;
    });
    for (let i = 1; i < nodes.length; i++) {
      const gap = nodes[i]._x - nodes[i - 1]._x;
      if (gap < minGap) {
        const shift = minGap - gap;
        for (let j = i; j < nodes.length; j++) nodes[j]._x += shift;
      }
    }
    const tierCx = (nodes[0]._x + nodes[nodes.length - 1]._x) / 2;
    const offset = width / 2 - tierCx;
    nodes.forEach(function (n) {
      n._x += offset;
    });
  }

  function deoverlapLayout(souls, width) {
    const tiers = {};
    souls.forEach(function (n) {
      const y = n._y || 0;
      if (!tiers[y]) tiers[y] = [];
      tiers[y].push(n);
    });
    Object.keys(tiers).forEach(function (y) {
      spreadTier(tiers[y], width, MIN_NODE_GAP);
    });
  }

  function agentSubtitle(node) {
    const hint = AGENT_HINTS[(node.agent_key || '').toLowerCase()];
    const task = (node.current_task || node.last_event_title || '').trim();
    if (task) return task.length > 32 ? task.slice(0, 30) + '…' : task;
    if (hint) return hint.short;
    return (node.role_title || '').split('·')[0].trim().slice(0, 24);
  }

  function agentStatus(node) {
    if (node.is_active) return 'live';
    const h = node.health || 'idle';
    if (h === 'warn') return 'warn';
    if (h === 'healthy') return 'ready';
    return 'idle';
  }

  function addRotateAnim(group, dur) {
    const anim = document.createElementNS(NS, 'animateTransform');
    anim.setAttribute('attributeName', 'transform');
    anim.setAttribute('type', 'rotate');
    anim.setAttribute('from', '0 0 0');
    anim.setAttribute('to', '360 0 0');
    anim.setAttribute('dur', dur || '12s');
    anim.setAttribute('repeatCount', 'indefinite');
    group.appendChild(anim);
  }

  function layoutPyramid(souls, width, positions) {
    const cx = width / 2;
    const hermanY = positions.hermanY;
    const execY = positions.execY;
    const tierYs = positions.tierYs;
    const minGap = MIN_NODE_GAP;
    let idx = 0;
    TIER_ROWS.forEach(function (count, tierIdx) {
      const y = tierYs[tierIdx] || 580;
      const slice = souls.slice(idx, idx + count);
      idx += count;
      const n = slice.length;
      if (!n) return;
      const span = Math.max(minGap * Math.max(n - 1, 1), minGap);
      const startX = cx - span / 2;
      slice.forEach(function (node, i) {
        node._x = n === 1 ? cx : startX + (span * i) / Math.max(1, n - 1);
        node._y = y;
        node._tier = tierIdx;
        node._cluster = clusterOf(node.agent_key);
      });
    });
    while (idx < souls.length) {
      const y = 660;
      const rest = souls.slice(idx);
      const n = rest.length;
      const span = Math.max(minGap * Math.max(n - 1, 1), minGap);
      const startX = cx - span / 2;
      rest.forEach(function (node, i) {
        node._x = n === 1 ? cx : startX + (span * i) / Math.max(1, n - 1);
        node._y = y;
        node._cluster = clusterOf(node.agent_key);
      });
      idx = souls.length;
    }
    deoverlapLayout(souls, width);
    return { cx: cx, hermanY: hermanY, execY: execY };
  }

  function layoutClusters(souls, width, height) {
    const cx = width / 2;
    const hermanY = height * 0.42;
    const execY = 58;
    const clusterCenters = {
      growth: { x: cx - 280, y: height * 0.72, r: 118 },
      product: { x: cx, y: height * 0.78, r: 118 },
      ops: { x: cx + 280, y: height * 0.72, r: 118 },
    };

    souls.forEach(function (node) {
      const key = (node.agent_key || '').toLowerCase();
      const cid = clusterOf(key) || 'product';
      const center = clusterCenters[cid];
      const members = CLUSTERS[cid].agents;
      const idx = members.indexOf(key);
      const n = members.length;
      const angleStart = -Math.PI * 0.72;
      const angleEnd = -Math.PI * 0.28;
      const angle =
        n <= 1 ? (angleStart + angleEnd) / 2 : angleStart + ((angleEnd - angleStart) * idx) / (n - 1);
      const dist = center.r * 0.72;
      node._x = center.x + Math.cos(angle) * dist;
      node._y = center.y + Math.sin(angle) * dist * 0.55;
      node._cluster = cid;
      node._clusterCx = center.x;
      node._clusterCy = center.y;
    });

    return { cx: cx, hermanY: hermanY, execY: execY, clusterCenters: clusterCenters };
  }

  function isSvgVisible(svg) {
    if (!svg) return false;
    const r = svg.getBoundingClientRect();
    return r.width > 8 && r.height > 8;
  }

  function showMeshLoading(svg, text) {
    clear(svg);
    const g = make('g', { class: 'mesh-loading' });
    const t = make('text', {
      x: 600,
      y: 360,
      'text-anchor': 'middle',
      fill: '#94a3b8',
      'font-size': 14,
    });
    t.textContent = text || 'Netwerk laden…';
    g.appendChild(t);
    svg.appendChild(g);
  }

  function showMeshError(svg, text) {
    clear(svg);
    const t = make('text', { x: 32, y: 40, fill: '#ef4444', 'font-size': 14 });
    t.textContent = text || 'Mesh kon niet geladen worden';
    svg.appendChild(t);
  }

  function whenSvgVisible(svg, fn) {
    if (!svg) return;
    if (isSvgVisible(svg)) {
      fn();
      return;
    }
    let tries = 0;
    const tick = function () {
      tries += 1;
      if (isSvgVisible(svg)) {
        fn();
        return;
      }
      if (tries < 60) {
        requestAnimationFrame(tick);
      } else {
        fn();
      }
    };
    requestAnimationFrame(tick);
  }

  function watchSvgResize(svg, renderFn) {
    if (!svg || svg._meshResizeObs || typeof ResizeObserver === 'undefined') return;
    const wrap = svg.closest('.mesh-wrap') || svg.parentElement;
    if (!wrap) return;
    let hadSize = isSvgVisible(svg);
    svg._meshResizeObs = new ResizeObserver(function () {
      const vis = isSvgVisible(svg);
      if (vis && !hadSize) {
        hadSize = true;
        renderFn();
      } else if (vis) {
        hadSize = true;
      }
    });
    svg._meshResizeObs.observe(wrap);
  }

  function renderMesh(svg, opts) {
    if (!svg || !cachedData) return;
    const drawOpts = Object.assign({}, lastMountOpts, opts || {}, {
      commMode: (opts && opts.commMode) || getSavedMode(),
    });
    const doDraw = function () {
      try {
        drawMesh(svg, cachedData, drawOpts);
      } catch (e) {
        console.error('drawMesh failed', e);
        showMeshError(svg, 'Diagram fout: ' + (e.message || 'onbekend'));
      }
    };
    doDraw();
    watchSvgResize(svg, doDraw);
  }

  async function loadMeshData() {
    const r = await fetch('/api/agents/mesh');
    if (!r.ok) throw new Error('Mesh API unavailable');
    return r.json();
  }

  async function ensureMeshData() {
    if (cachedData) return cachedData;
    cachedData = await loadMeshData();
    return cachedData;
  }

  let pendingRender = false;

  function scheduleRender(opts) {
    const svg = document.getElementById('agents-mesh-canvas');
    if (!svg) return;
    if (opts) lastMountOpts = Object.assign({}, lastMountOpts, opts);
    if (pendingRender) return;
    pendingRender = true;
    whenSvgVisible(svg, function () {
      pendingRender = false;
      renderMesh(svg, opts);
    });
  }

  function nodePos(nodesByKey, key) {
    const n = nodesByKey[key];
    if (n && n._x != null) return { x: n._x, y: n._y };
    return null;
  }

  function resolveMode(opts) {
    const id = (opts && opts.commMode) || localStorage.getItem(STORAGE_KEY) || 'pyramid';
    return COMM_MODES[id] || COMM_MODES.pyramid;
  }

  function drawMesh(svg, data, opts) {
    opts = opts || {};
    const mode = resolveMode(opts);
    const onNodeClick = opts.onNodeClick;

    clear(svg);
    svg.setAttribute('data-mesh-mode', mode.id);

    const vb = svg.viewBox.baseVal;
    const width = vb && vb.width ? vb.width : VIEW_W;
    const height = vb && vb.height ? vb.height : VIEW_H;
    if (!vb || vb.width !== VIEW_W || vb.height !== VIEW_H) {
      svg.setAttribute('viewBox', '0 0 ' + VIEW_W + ' ' + VIEW_H);
    }

    const positions = {
      execY: 72,
      hermanY: mode.layout === 'clusters' ? height * 0.38 : 228,
      tierYs: [400, 540, 690],
    };

    const souls = (data.nodes || []).filter(function (n) {
      const key = (n.agent_key || '').toLowerCase();
      return key && key !== 'herman';
    });

    const layoutMeta =
      mode.layout === 'clusters'
        ? layoutClusters(souls, width, height)
        : layoutPyramid(souls, width, positions);

    const cx = layoutMeta.cx;
    const hermanY = layoutMeta.hermanY;
    const execY = layoutMeta.execY;

    const nodesByKey = {};
    souls.forEach(function (n) {
      nodesByKey[(n.agent_key || '').toLowerCase()] = n;
    });

    const ceo = { x: cx - 200, y: execY, label: 'CEO', sub: 'Aissa' };
    const cto = { x: cx + 200, y: execY, label: 'CTO', sub: 'Platform' };

    const bgLayer = make('g', { class: 'mesh-bg' });
    const clusterLayer = make('g', { class: 'mesh-clusters' });
    const edgesLayer = make('g', { class: 'mesh-edges' });
    const nodesLayer = make('g', { class: 'mesh-nodes' });
    svg.appendChild(bgLayer);
    if (mode.layout === 'clusters') svg.appendChild(clusterLayer);
    svg.appendChild(edgesLayer);
    svg.appendChild(nodesLayer);

    if (mode.layout === 'pyramid') {
      positions.tierYs.forEach(function (y, i) {
        bgLayer.appendChild(
          make('rect', {
            x: 40,
            y: y - 42,
            width: width - 80,
            height: 84,
            rx: 12,
            class: 'mesh-tier-band mesh-tier-' + i,
          })
        );
      });
    }

    if (mode.layout === 'clusters' && layoutMeta.clusterCenters) {
      Object.keys(CLUSTERS).forEach(function (cid) {
        const c = CLUSTERS[cid];
        const center = layoutMeta.clusterCenters[cid];
        if (!center) return;
        const g = make('g', { class: 'mesh-cluster-blob mesh-cluster-' + cid });
        g.appendChild(
          make('ellipse', {
            cx: center.x,
            cy: center.y,
            rx: center.r + 28,
            ry: center.r * 0.62,
            fill: c.color,
            stroke: c.stroke,
            'stroke-width': 1.2,
          })
        );
        const lbl = make('text', {
          x: center.x,
          y: center.y - center.r * 0.35,
          class: 'mesh-cluster-label',
          'text-anchor': 'middle',
        });
        lbl.textContent = c.icon + ' ' + c.label;
        g.appendChild(lbl);
        clusterLayer.appendChild(g);
      });
    }

    function addEdge(x1, y1, x2, y2, cls, pulse, extraAttrs) {
      const d = curvePath(x1, y1, x2, y2, mode.edgeBend);
      const attrs = { d: d, class: 'mesh-edge ' + cls };
      if (extraAttrs) Object.assign(attrs, extraAttrs);
      edgesLayer.appendChild(make('path', attrs));
      if (pulse) {
        const p = make('path', { d: d, class: 'mesh-pulse mesh-pulse-active ' + cls });
        p.style.animationDuration = (typeof pulse === 'number' ? pulse : 2.6) + 's';
        edgesLayer.appendChild(p);
      }
    }

    if (mode.showExecutive) {
      const execCls = mode.straightExec ? 'mesh-edge-executive mesh-edge-straight' : 'mesh-edge-executive';
      addEdge(ceo.x, ceo.y + 30, cx, hermanY - 46, execCls, false);
      addEdge(cto.x, cto.y + 30, cx, hermanY - 46, execCls, false);
    }

    (data.peer_edges || []).forEach(function (e) {
      const live = e.type === 'live';
      if (live && !mode.showLivePeers) return;
      if (!live && !mode.showStaticPeers) return;
      if (mode.activeOnly && !live) return;

      const a = nodePos(nodesByKey, e.source);
      const b = nodePos(nodesByKey, e.target);
      if (!a || !b) return;

      const srcNode = nodesByKey[e.source];
      const tgtNode = nodesByKey[e.target];
      const sameCluster =
        srcNode &&
        tgtNode &&
        srcNode._cluster &&
        srcNode._cluster === tgtNode._cluster;

      if (mode.intraClusterOnly && !sameCluster) return;

      const cls = live ? 'mesh-edge-peer-live' : 'mesh-edge-peer';
      const pulse = live ? 2.2 : false;
      const extra = {};
      if (!live && mode.staticPeerOpacity != null) {
        extra['stroke-opacity'] = mode.staticPeerOpacity;
      }
      if (sameCluster && mode.layout === 'clusters') {
        extra['stroke-opacity'] = live ? 0.75 : mode.staticPeerOpacity || 0.25;
      }

      addEdge(a.x, a.y, b.x, b.y, cls, pulse, extra);
    });

    if (mode.showReport) {
      (data.report_edges || []).forEach(function (e) {
        if (mode.activeOnly && !e.active) return;
        const a = nodePos(nodesByKey, e.source);
        if (!a) return;
        addEdge(a.x, a.y - 24, cx, hermanY + 42, 'mesh-edge-report', e.active ? 2.4 : false);
      });
    }

    if (mode.showDelegate) {
      (data.delegate_edges || []).forEach(function (e) {
        if (mode.activeOnly && !e.active) return;
        const b = nodePos(nodesByKey, e.target);
        if (!b) return;
        addEdge(cx, hermanY + 42, b.x, b.y - 24, 'mesh-edge-delegate', e.active ? 2.0 : false);
      });
    }

    function drawExec(node, cls) {
      const g = make('g', { class: 'mesh-node mesh-exec ' + cls, transform: 'translate(' + node.x + ',' + node.y + ')' });
      g.appendChild(make('circle', { cx: 0, cy: 0, r: 30, class: 'main' }));
      const t = make('text', { x: 0, y: -42, class: 'mesh-label mesh-label-exec' });
      t.textContent = node.label;
      g.appendChild(t);
      const sub = make('text', { x: 0, y: -26, class: 'mesh-sublabel' });
      sub.textContent = node.sub;
      g.appendChild(sub);
      nodesLayer.appendChild(g);
    }

    drawExec(ceo, 'mesh-ceo');
    drawExec(cto, 'mesh-cto');

    const hermanActive =
      (data.delegate_edges || []).some(function (e) {
        return e.active;
      }) ||
      (data.report_edges || []).some(function (e) {
        return e.active;
      });

    const hermanG = make('g', {
      class: 'mesh-node mesh-herman' + (hermanActive ? ' active' : ''),
      transform: 'translate(' + cx + ',' + hermanY + ')',
    });
    if (hermanActive) {
      const ringG = make('g', { class: 'mesh-herman-ring' });
      ringG.appendChild(make('circle', { class: 'ring', cx: 0, cy: 0, r: 54 }));
      addRotateAnim(ringG, '14s');
      hermanG.appendChild(ringG);
    }
    hermanG.appendChild(make('circle', { class: 'main', cx: 0, cy: 0, r: 40 }));
    const crown = make('text', { x: 0, y: 6, class: 'mesh-herman-crown', 'text-anchor': 'middle' });
    crown.textContent = '👑';
    hermanG.appendChild(crown);
    const hLabel = make('text', { x: 0, y: 64, class: 'mesh-label mesh-label-herman' });
    hLabel.textContent = 'Herman';
    hermanG.appendChild(hLabel);
    const hSub = make('text', { x: 0, y: 80, class: 'mesh-sublabel mesh-sublabel-herman' });
    hSub.textContent = 'Co-CEO · takenverdeler';
    hermanG.appendChild(hSub);
    const hTask = make('text', { x: 0, y: 94, class: 'mesh-context' });
    const hermanNode = (data.nodes || []).find(function (n) {
      return (n.agent_key || '').toLowerCase() === 'herman';
    });
    hTask.textContent = hermanNode ? agentSubtitle(hermanNode) : 'Coördineert alle agents';
    hermanG.appendChild(hTask);
    nodesLayer.appendChild(hermanG);

    souls.forEach(function (node, idx) {
      const key = (node.agent_key || '').toLowerCase();
      const live = !!node.is_active;
      const status = agentStatus(node);
      const hint = AGENT_HINTS[key];
      const clusterCls = node._cluster ? ' mesh-agent-cluster-' + node._cluster : '';
      const labelAbove = typeof node._tier === 'number' ? node._tier % 2 === 1 : idx % 2 === 1;
      const group = make('g', {
        class:
          'mesh-node mesh-agent mesh-status-' +
          status +
          ' ' +
          (node.health || 'idle') +
          (live ? ' mesh-agent-live' : '') +
          clusterCls,
        transform: 'translate(' + node._x + ',' + node._y + ')',
      });
      if (live) {
        const pulseG = make('g', { class: 'mesh-pulse-wrap' });
        pulseG.appendChild(make('circle', { cx: 0, cy: 0, r: 28, class: 'pulse-ring' }));
        group.appendChild(pulseG);
      }
      group.appendChild(make('circle', { cx: 0, cy: 0, r: 22, class: 'main' }));
      const emoji = make('text', { x: 0, y: 5, class: 'mesh-emoji', 'text-anchor': 'middle' });
      emoji.textContent = node.avatar_emoji || (hint && hint.short ? hint.short.charAt(0) : '') || '●';
      group.appendChild(emoji);
      const displayName = (node.display_name || key || '?').split(' ')[0];
      const nameY = labelAbove ? -30 : 36;
      const ctxY = labelAbove ? -16 : 52;
      const domY = labelAbove ? -2 : 66;
      const t = make('text', { x: 0, y: nameY, class: 'mesh-label mesh-label-agent' });
      t.textContent = displayName;
      group.appendChild(t);
      const ctx = make('text', { x: 0, y: ctxY, class: 'mesh-context' });
      ctx.textContent = agentSubtitle(node);
      group.appendChild(ctx);
      if (hint && mode.layout !== 'clusters') {
        const dom = make('text', { x: 0, y: domY, class: 'mesh-domain' });
        dom.textContent = hint.domain;
        group.appendChild(dom);
      }
      const tip =
        (node.role_title || '') +
        (node.responsibilities ? '\n' + String(node.responsibilities).slice(0, 120) : '') +
        (node.current_task ? '\nNu: ' + node.current_task : '');
      group.setAttribute('title', tip.slice(0, 200));
      group.addEventListener('click', function () {
        if (onNodeClick) onNodeClick(node);
      });
      nodesLayer.appendChild(group);
    });
  }

  function updatePickerUI(modeId) {
    document.querySelectorAll('[data-mesh-mode]').forEach(function (btn) {
      const active = btn.getAttribute('data-mesh-mode') === modeId;
      btn.classList.toggle('is-active', active);
      btn.setAttribute('aria-pressed', active ? 'true' : 'false');
    });
    const wrap = document.querySelector('.mesh-wrap');
    if (wrap) {
      wrap.className = 'mesh-wrap mesh-wrap-mode-' + modeId;
      wrap.setAttribute('data-mode', modeId);
    }
    const badge = document.getElementById('mesh-mode-badge');
    if (badge && COMM_MODES[modeId]) {
      badge.textContent = COMM_MODES[modeId].label;
    }
  }

  function pickMode(modeId, opts, silent) {
    const mode = COMM_MODES[modeId] ? modeId : 'pyramid';
    const drawOpts = Object.assign({}, lastMountOpts, opts || {}, { commMode: mode });
    if (!silent) saveMode(mode);
    updatePickerUI(mode);
    if (onModeChangeCb) onModeChangeCb(mode);
    const svg = document.getElementById('agents-mesh-canvas');
    if (!svg) return mode;
    showMeshLoading(svg, 'Stijl: ' + COMM_MODES[mode].label + '…');
    ensureMeshData()
      .then(function () {
        whenSvgVisible(svg, function () {
          renderMesh(svg, drawOpts);
        });
      })
      .catch(function (e) {
        showMeshError(svg, 'Mesh laden mislukt — ' + (e.message || ''));
      });
    return mode;
  }

  function bindModePicker(opts) {
    lastMountOpts = Object.assign({}, lastMountOpts, opts || {});
    if (pickerBound) return;
    pickerBound = true;
    document.addEventListener('click', function (e) {
      const btn = e.target.closest('[data-mesh-mode]');
      if (!btn) return;
      e.preventDefault();
      e.stopPropagation();
      const id = btn.getAttribute('data-mesh-mode');
      pickMode(id);
    });
  }

  function setModeChangeCallback(fn) {
    onModeChangeCb = fn;
  }

  async function mount(targetId, opts) {
    opts = opts || {};
    lastMountOpts = opts;
    const svg = document.getElementById(targetId);
    if (!svg) return null;
    bindModePicker(opts);
    const modeObj = resolveMode(opts);
    updatePickerUI(modeObj.id);
    showMeshLoading(svg, 'Netwerk laden…');
    try {
      await ensureMeshData();
      const drawOpts = Object.assign({}, opts, { commMode: modeObj.id });
      whenSvgVisible(svg, function () {
        renderMesh(svg, drawOpts);
      });
      return cachedData;
    } catch (e) {
      showMeshError(svg, 'Mesh laden mislukt — ' + (e.message || ''));
      return null;
    }
  }

  async function refresh(targetId, opts) {
    cachedData = null;
    return mount(targetId, opts);
  }

  function redraw(targetId, opts) {
    const svg = document.getElementById(targetId);
    if (!svg || !cachedData) return;
    whenSvgVisible(svg, function () {
      renderMesh(svg, opts || {});
    });
  }

  function saveMode(modeId) {
    if (COMM_MODES[modeId]) localStorage.setItem(STORAGE_KEY, modeId);
  }

  function getSavedMode() {
    const id = localStorage.getItem(STORAGE_KEY) || 'pyramid';
    return COMM_MODES[id] ? id : 'pyramid';
  }

  window.AgentsMesh = {
    mount,
    refresh,
    redraw,
    pickMode,
    bindModePicker,
    setModeChangeCallback,
    loadMeshData,
    drawMesh,
    COMM_MODES,
    CLUSTERS,
    saveMode,
    getSavedMode,
  };

  function bootPicker() {
    bindModePicker({});
    const saved = getSavedMode();
    updatePickerUI(saved);
  }
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', bootPicker);
  } else {
    bootPicker();
  }

  if (window.location.pathname.indexOf('/agents') >= 0) {
    ensureMeshData().catch(function () {});
  }
})();
