{"id":104,"date":"2026-04-14T05:19:14","date_gmt":"2026-04-14T05:19:14","guid":{"rendered":"https:\/\/qubicportal.org\/?page_id=104"},"modified":"2026-04-30T14:12:51","modified_gmt":"2026-04-30T14:12:51","slug":"104-2","status":"publish","type":"page","link":"https:\/\/qubicportal.org\/","title":{"rendered":""},"content":{"rendered":"    <div id=\"imm-mindmap-wrapper\" style=\"position:relative; width:100%; height:auto; min-height:600px; overflow:visible; border:0; background:transparent; isolation:isolate; contain:layout style paint;\">\n        <div id=\"imm-container\" style=\"width:100%; height:80dvh; min-height:600px; position:relative; overflow:hidden; border:0; background:transparent; opacity:0; transition:opacity 0.4s ease;\">\n            <div id=\"imm-loading\" style=\"position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); z-index:10; color:#888; font-family:system-ui,Arial,sans-serif; text-align:center; pointer-events:none;\">\n                <div style=\"margin-bottom:12px;\">Loading mindmap...<\/div>\n                <div style=\"width:40px; height:40px; border:4px solid #ddd; border-top:4px solid #8bf; border-radius:50%; animation:spin 1s linear infinite; margin:0 auto;\"><\/div>\n            <\/div>\n\n            \n            <div id=\"imm-tooltip\" style=\"position:absolute; display:none; background:rgba(0,0,0,0.92); color:#fff; padding:8px 12px; border-radius:6px; font-family:system-ui,Arial,sans-serif; font-size:14px; pointer-events:none; z-index:10002; box-shadow:0 4px 12px rgba(0,0,0,0.5); white-space:nowrap; max-width:280px;\"><\/div>\n\n            <div id=\"imm-youtube-panel\" style=\"display:none; position:fixed; top:50%; left:50%; transform:translate(-50%, -50%); width:512px; height:425px; max-width:92vw; max-height:85vh; background:#000; border:3px solid #fff; border-radius:11px; box-shadow:0 0 30px rgba(0,0,0,0.9); z-index:10001; overflow:hidden;\">\n                <div id=\"imm-video-header\" style=\"background:rgba(0,0,0,0.85); padding:5px 5px; color:#fff; font-weight:bold; cursor:move; user-select:none; display:flex; align-items:center; gap:12px;\">\n                    <button id=\"imm-enter-site-btn\" style=\"background:#8bf; color:white; border:none; padding:3px 10px; border-radius:6px; font-weight:bold; cursor:pointer;\">Enter Site<\/button>\n                    <span style=\"flex:1; text-align:center;\">Video Player<\/span>\n                    <button id=\"imm-close-btn\" style=\"background:none; border:none; color:#fff; font-size:32px; font-weight:bold; cursor:pointer; padding:0 8px;\">\u00d7<\/button>\n                <\/div>\n                <iframe id=\"imm-youtube-iframe\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen style=\"width:100%; height:calc(100% - 52px);\"><\/iframe>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <style>\n    @keyframes spin { to { transform: rotate(360deg); } }\n    #imm-mindmap-wrapper svg,\n    #imm-container svg {\n        position: absolute;\n        top: 0;\n        left: 0;\n        width: 100%;\n        height: 100%;\n        z-index: 3;\n    }\n    .clickable-node { cursor: pointer; }\n\n    .energy-pulse {\n        stroke-width: 2.5;\n        stroke-linecap: round;\n        filter: url(#energy-glow);\n        pointer-events: none;\n        opacity: 0.75;\n    }\n\n    .paid-border {\n        stroke: #fc0;\n        stroke-width: 0;\n        fill: none;\n    }\n\n    .paid-border-pulse {\n        animation: paidBorderPulse 300ms ease-out forwards;\n    }\n    @keyframes paidBorderPulse {\n        0%   { stroke-width: 5; filter: brightness(1.6); }\n        100% { stroke-width: 3; filter: brightness(1); }\n    }\n    <\/style>\n\n    <script>\n    document.addEventListener('DOMContentLoaded', () => {\n        const container = document.getElementById('imm-container');\n        const loadingEl = document.getElementById('imm-loading');\n        const tooltip = document.getElementById('imm-tooltip');\n\n        async function preloadAndMeasureImages(nodes) {\n            const imageUrls = new Set();\n            nodes.forEach(node => {\n                if (node.image_light) imageUrls.add(node.image_light);\n                if (node.image_dark)  imageUrls.add(node.image_dark);\n                if (node.overlay_light) imageUrls.add(node.overlay_light);\n                if (node.overlay_dark)  imageUrls.add(node.overlay_dark);\n            });\n\n            await Promise.all(Array.from(imageUrls).map(src => {\n                return new Promise(resolve => {\n                    if (!src) return resolve();\n                    const img = new Image();\n                    img.onload = () => resolve();\n                    img.onerror = () => resolve();\n                    img.src = src;\n                });\n            }));\n\n            await Promise.all(nodes.map(node => {\n                return new Promise(resolve => {\n                    const src = node.image_light || node.image_dark;\n                    if (!src) {\n                        node.realWidth = 50;\n                        node.realHeight = 50;\n                        return resolve();\n                    }\n                    const img = new Image();\n                    img.onload = () => {\n                        node.realWidth = img.width;\n                        node.realHeight = img.height;\n                        resolve();\n                    };\n                    img.onerror = () => {\n                        node.realWidth = 50;\n                        node.realHeight = 50;\n                        resolve();\n                    };\n                    img.src = src;\n                });\n            }));\n        }\n\n        let rawData = {\"title\":\"Projects\",\"nodes\":[{\"id\":1,\"label\":\"Qubic Portal\",\"url\":\"https:\/\/qubicportal.org\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/logosqw-1.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/logosq.png\",\"youtube_id\":\"\",\"isRoot\":true,\"node_type\":\"newroot\",\"category\":\"\",\"open_behavior\":\"self\",\"overlay_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/centeranim.gif\",\"overlay_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/centeranim.gif\",\"overlay_width\":155,\"overlay_height\":155,\"pulse_rate\":25},{\"id\":2,\"label\":\"Exchanges\",\"url\":\"https:\/\/qubicportal.org\/?page_id=31\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Exchanges2-1.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Exchanges2-1.png\",\"category\":\"Exchanges\",\"youtube_id\":\"\",\"isRoot\":false,\"node_type\":\"newroot\",\"open_behavior\":\"self\",\"overlay_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Exchanges-overlay-2.png\",\"overlay_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Exchanges-overlay-2.png\",\"overlay_width\":80,\"overlay_height\":80},{\"id\":4,\"label\":\"qubic.org\",\"url\":\"https:\/\/\/qubic.org\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qubic.org-light-4.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qubic-dark-.org_-1.png\",\"category\":\"Qubic Resources\",\"youtube_id\":\"\",\"isRoot\":false,\"node_type\":\"leaf\",\"open_behavior\":\"popup\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"pulse_rate\":40},{\"id\":8,\"label\":\"Projects\",\"url\":\"https:\/\/qubicportal.org\/?page_id=33\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/projects2-1.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/projects2-1.png\",\"category\":\"Projects\",\"youtube_id\":\"\",\"isRoot\":false,\"node_type\":\"newroot\",\"open_behavior\":\"self\",\"overlay_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Projects-overlay-2.png\",\"overlay_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Projects-overlay-2.png\",\"overlay_width\":80,\"overlay_height\":80,\"pulse_rate\":120},{\"id\":6,\"label\":\"Portal DEX\",\"url\":\"https:\/\/app.qubicportal.org\/\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/portal-app-3-1.webp\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/portal-app-dark-3-1.webp\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Exchanges\",\"youtube_id\":\"\",\"open_behavior\":\"popup\",\"node_type\":\"leaf\",\"pulse_rate\":40,\"isRoot\":false},{\"id\":5,\"label\":\"qswap\",\"url\":\"https:\/\/swap.qubicportal.org\/swap\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qswap-1.webp\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qswap-1.webp\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Exchanges\",\"youtube_id\":\"\",\"open_behavior\":\"popup\",\"node_type\":\"leaf\",\"pulse_rate\":40,\"isRoot\":false},{\"id\":10,\"label\":\"Resources\",\"url\":\"https:\/\/qubicportal.org\/?page_id=35\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Resources2-2.webp\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Resources2-2.webp\",\"overlay_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Resource-overlay-2.webp\",\"overlay_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Resource-overlay-2.webp\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Qubic Resources\",\"youtube_id\":\"\",\"isRoot\":false,\"node_type\":\"newroot\",\"open_behavior\":\"self\",\"pulse_rate\":40},{\"id\":11,\"label\":\"Contact\",\"url\":\"https:\/\/qubicportal.org\/?page_id=30\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/05\/RENTMEFC0BLACK.gif\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/05\/RENTMEfc0.gif\",\"overlay_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/05\/RENTMEfc0.gif\",\"overlay_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/05\/RENTMEFC0BLACK.gif\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Contact\",\"youtube_id\":\"\",\"isRoot\":false,\"node_type\":\"paid\",\"open_behavior\":\"self\",\"pulse_rate\":40},{\"id\":12,\"label\":\"Qmine\",\"url\":\"https:\/\/qmine.io\/\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qmine-3.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qmine-3.png\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Mining\",\"youtube_id\":\"DFhffPqaS30\",\"isRoot\":false,\"node_type\":\"leaf\",\"open_behavior\":\"popup\",\"pulse_rate\":40},{\"id\":13,\"label\":\"Qubic Capital\",\"url\":\"https:\/\/qubic.org\/ecosystem\/qubic-capital\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/QCaplight-1.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/QCapdark.png\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Projects\",\"youtube_id\":\"\",\"open_behavior\":\"popup\",\"node_type\":\"leaf\",\"pulse_rate\":40,\"isRoot\":false},{\"id\":14,\"label\":\"Garth\",\"url\":\"https:\/\/garthonqubic.com\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Garth.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Garth.png\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Projects\",\"youtube_id\":\"\",\"open_behavior\":\"popup\",\"node_type\":\"leaf\",\"pulse_rate\":40,\"isRoot\":false},{\"id\":15,\"label\":\"qDoge\",\"url\":\"https:\/\/www.qdogeonqubic.com\/\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qDoge-2.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qDoge-2.png\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Projects\",\"youtube_id\":\"DT0wuhaOnz0\",\"isRoot\":false,\"node_type\":\"leaf\",\"open_behavior\":\"popup\",\"pulse_rate\":40},{\"id\":20,\"label\":\"Random Lottery\",\"url\":\"https:\/\/qubicrl.org\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qubicRL-1.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qubicRL-1.png\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Projects\",\"youtube_id\":\"\",\"open_behavior\":\"popup\",\"node_type\":\"leaf\",\"pulse_rate\":40,\"isRoot\":false},{\"id\":19,\"label\":\"QRaffle\",\"url\":\"https:\/\/qubic.org\/ecosystem\/qraffle\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qraffle-1.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/qraffle-1.png\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Projects\",\"youtube_id\":\"\",\"open_behavior\":\"popup\",\"node_type\":\"leaf\",\"pulse_rate\":40,\"isRoot\":false},{\"id\":18,\"label\":\"Ncodemus - Design \/ Video \/ Animation \/ AI\",\"url\":\"http:\/\/www.ncodemus.com\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Transparent-N.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Transparent-N.png\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Talent\",\"youtube_id\":\"VZHekB8f_PA\",\"open_behavior\":\"popup\",\"node_type\":\"leaf\",\"pulse_rate\":40,\"isRoot\":false},{\"id\":21,\"label\":\"Mining Resources\",\"url\":\"https:\/\/qubicportal.org\/?page_id=373\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/mining.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/mining.png\",\"overlay_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Mining-overlay.png\",\"overlay_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Mining-overlay.png\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Mining\",\"youtube_id\":\"\",\"isRoot\":false,\"node_type\":\"newroot\",\"open_behavior\":\"self\",\"pulse_rate\":40},{\"id\":22,\"label\":\"My Last Match\",\"url\":\"https:\/\/mylastmatch.net\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/mlm-2.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/mlm-2.png\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Projects\",\"youtube_id\":\"4vnJBtV_7vk\",\"open_behavior\":\"popup\",\"node_type\":\"leaf\",\"pulse_rate\":40,\"isRoot\":false},{\"id\":29,\"label\":\"Wolf Pack - My Last Match\",\"url\":\"https:\/\/mlmwp.com\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/05\/wpblackanim-4.gif\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/05\/wpblackanim-4.gif\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Projects\",\"youtube_id\":\"4vnJBtV_7vk\",\"isRoot\":false,\"node_type\":\"paid\",\"open_behavior\":\"popup\",\"pulse_rate\":40},{\"id\":31,\"label\":\"Qubic Bay\",\"url\":\"https:\/\/qubicbay.io\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Qubic-Bay-1.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/Qubic-Bay-1.png\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Projects\",\"youtube_id\":\"\",\"open_behavior\":\"popup\",\"node_type\":\"leaf\",\"pulse_rate\":40,\"isRoot\":false},{\"id\":32,\"label\":\"Qubic BLOG\",\"url\":\"https:\/\/qubic.org\/blog-grid\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/blog.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/blog.png\",\"overlay_light\":\"\",\"overlay_dark\":\"\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Qubic Resources\",\"youtube_id\":\"\",\"isRoot\":false,\"node_type\":\"leaf\",\"open_behavior\":\"popup\",\"pulse_rate\":40},{\"id\":33,\"label\":\"Science\",\"url\":\"https:\/\/qubicportal.org\/?page_id=516\",\"image_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/science.png\",\"image_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/science.png\",\"overlay_light\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/science-overlay.png\",\"overlay_dark\":\"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/science-overlay.png\",\"overlay_width\":80,\"overlay_height\":80,\"category\":\"Qubic Resources\",\"youtube_id\":\"\",\"isRoot\":false,\"node_type\":\"newroot\",\"open_behavior\":\"popup\"}],\"links\":[{\"source\":1,\"target\":2},{\"source\":1,\"target\":10},{\"source\":1,\"target\":16},{\"source\":1,\"target\":17},{\"source\":1,\"target\":5},{\"source\":1,\"target\":21},{\"source\":1,\"target\":8},{\"source\":1,\"target\":23},{\"source\":1,\"target\":24},{\"source\":1,\"target\":25},{\"source\":1,\"target\":26},{\"source\":1,\"target\":27},{\"source\":1,\"target\":28},{\"source\":1,\"target\":12},{\"source\":1,\"target\":15},{\"source\":1,\"target\":30},{\"source\":1,\"target\":4},{\"source\":1,\"target\":14},{\"source\":1,\"target\":6},{\"source\":1,\"target\":32},{\"source\":1,\"target\":20},{\"source\":1,\"target\":31},{\"source\":1,\"target\":13},{\"source\":1,\"target\":18},{\"source\":1,\"target\":19},{\"source\":1,\"target\":22},{\"source\":1,\"target\":29},{\"source\":1,\"target\":11},{\"source\":1,\"target\":33}],\"custom_layout\":{\"1\":{\"x\":0.5,\"y\":0.5,\"scale\":1},\"2\":{\"x\":0.525465200276613,\"y\":0.28142857142857136,\"scale\":1},\"4\":{\"x\":0.6413191089734521,\"y\":0.1643453755299263,\"scale\":1},\"5\":{\"x\":0.8662672500501932,\"y\":0.4568708353437093,\"scale\":1},\"6\":{\"x\":0.7909865374118015,\"y\":0.2742857142857143,\"scale\":1.0666666666666667},\"8\":{\"x\":0.7065877726310777,\"y\":0.39318882590050264,\"scale\":1},\"10\":{\"x\":0.38811400698655846,\"y\":0.6445577360848621,\"scale\":1},\"11\":{\"x\":0.6524850741605476,\"y\":0.5328571428571429,\"scale\":0.7},\"12\":{\"x\":0.8290710129056018,\"y\":0.652525459813783,\"scale\":1},\"13\":{\"x\":0.7106187827123526,\"y\":0.7670831523021051,\"scale\":1},\"14\":{\"x\":0.5576426058196301,\"y\":0.8114285714285714,\"scale\":1},\"15\":{\"x\":0.40599671094551637,\"y\":0.7885117673272166,\"scale\":1},\"18\":{\"x\":0.07496684912589788,\"y\":0.4988434503705763,\"scale\":1},\"19\":{\"x\":0.16085090008814612,\"y\":0.6114285714285715,\"scale\":1},\"20\":{\"x\":0.2616653064469942,\"y\":0.7110968883852117,\"scale\":1},\"21\":{\"x\":0.2895797549313776,\"y\":0.3925851210579953,\"scale\":1},\"22\":{\"x\":0.2969465042669197,\"y\":0.17571428571428568,\"scale\":1},\"29\":{\"x\":0.39213672513733866,\"y\":0.2931888259005026,\"scale\":1},\"31\":{\"x\":0.5049193059331967,\"y\":0.11863108981564058,\"scale\":1},\"32\":{\"x\":0.15282145386380294,\"y\":0.29040178571428577,\"scale\":1},\"33\":{\"x\":0.5614835641180012,\"y\":0.6697285714285713,\"scale\":1}},\"custom_layout_portrait\":{\"1\":{\"x\":0.10434272321089523,\"y\":0.4863392857142857,\"scale\":1},\"2\":{\"x\":0.11988744598330672,\"y\":0.270625,\"scale\":1},\"4\":{\"x\":0.7441363903169329,\"y\":0.3949107142857143,\"scale\":1},\"5\":{\"x\":0.8117348833715753,\"y\":0.5091964285714285,\"scale\":1},\"6\":{\"x\":0.6933232731229788,\"y\":0.27919642857142857,\"scale\":1},\"8\":{\"x\":0.42128475785551445,\"y\":0.32919642857142856,\"scale\":1},\"10\":{\"x\":0.42075464990572303,\"y\":0.6734821428571428,\"scale\":1},\"11\":{\"x\":0.3498711087317273,\"y\":0.8091964285714286,\"scale\":1},\"12\":{\"x\":0.1087569181398453,\"y\":0.07205357142857143,\"scale\":1},\"13\":{\"x\":0.6319723002007614,\"y\":0.19633928571428572,\"scale\":1},\"14\":{\"x\":0.506945839028073,\"y\":0.8934821428571429,\"scale\":1},\"15\":{\"x\":0.3091536590343117,\"y\":0.9249107142857143,\"scale\":1},\"18\":{\"x\":0.10845803320879362,\"y\":0.9034821428571429,\"scale\":1},\"19\":{\"x\":0.6849665546975703,\"y\":0.7463392857142858,\"scale\":1.6400000000000001},\"20\":{\"x\":0.7311160191941948,\"y\":0.6463392857142857,\"scale\":1.79},\"21\":{\"x\":0.12553053061011724,\"y\":0.6991964285714286,\"scale\":1},\"22\":{\"x\":0.513450967610476,\"y\":0.080625,\"scale\":1},\"29\":{\"x\":0.36505852500073277,\"y\":0.15776785714285715,\"scale\":1},\"31\":{\"x\":0.3137630775871752,\"y\":0.07348214285714286,\"scale\":1},\"32\":{\"x\":0.6292705380332125,\"y\":0.8204017857142857,\"scale\":1},\"33\":{\"x\":0.5434221553281156,\"y\":0.4925857142857142,\"scale\":1}}};\n        let nodesData = rawData.nodes.map(d => ({...d}));\n        let linksData = rawData.links.map(d => ({...d}));\n\n        preloadAndMeasureImages(nodesData).then(() => {\n            loadingEl.style.opacity = '0';\n            setTimeout(() => {\n                loadingEl.style.display = 'none';\n                container.style.opacity = '1';\n                initMindmap();\n            }, 300);\n        });\n\n        function initMindmap() {\n            let svg, g, defs, lineGroup, nodeGroup, lines, nodeElements, currentNodes, currentLinks, energyGroup;\n            let currentIsDark = isDarkTheme();\n            let width = container.clientWidth;\n            let height = container.clientHeight;\n\n            let activePulses = new Map();\n            let currentOpenNodeId = null;\n\n            function isDarkTheme() {\n                const bodyTheme = document.body.getAttribute('data-theme');\n                if (bodyTheme === 'dark') return true;\n                if (bodyTheme === 'light') return false;\n                return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;\n            }\n\n            function getPulseColor() {\n                return currentIsDark ? '#fff' : '#8cf';\n            }\n\n            function getBorderColor() {\n                return currentIsDark ? '#fff' : '#8cf';\n            }\n\n            function getHoverBorderColor() {\n                return currentIsDark ? '#fff' : '#000';\n            }\n\n            function getHighlightColor() {\n                return currentIsDark ? '#fff' : '#333333';\n            }\n\n            function getLinkColor(link) {\n                const isPaidLink = (link.source && link.source.node_type === 'paid') ||\n                                   (link.target && link.target.node_type === 'paid');\n                return isPaidLink ? '#fc0' : (currentIsDark ? '#2ff' : '#88ccff');\n            }\n\n            function isPortrait() {\n                return window.innerHeight > window.innerWidth * 1.05;\n            }\n\n            function isTablet() {\n                const w = window.innerWidth;\n                return w >= 768 && w <= 1024;\n            }\n\n            function cleanupPulses() {\n                if (energyGroup) energyGroup.selectAll('.energy-pulse').interrupt().remove();\n                activePulses.clear();\n            }\n\n            function createMindmapElements() {\n                if (svg) svg.remove();\n\n                svg = d3.select('#imm-container').append('svg')\n                    .attr('width', width)\n                    .attr('height', height)\n                    .attr('viewBox', `0 0 ${width} ${height}`)\n                    .attr('preserveAspectRatio', 'xMidYMid meet');\n\n                g = svg.append('g');\n                defs = svg.append('defs');\n\n                const glowFilter = defs.append('filter').attr('id', 'root-glow')\n                    .attr('x', '-50%').attr('y', '-50%')\n                    .attr('width', '200%').attr('height', '200%');\n                glowFilter.append('feGaussianBlur').attr('stdDeviation', '10').attr('result', 'coloredBlur');\n                const feMerge = glowFilter.append('feMerge');\n                feMerge.append('feMergeNode').attr('in', 'coloredBlur');\n                feMerge.append('feMergeNode').attr('in', 'SourceGraphic');\n\n                const energyGlow = defs.append('filter').attr('id', 'energy-glow')\n                    .attr('x', '-200%').attr('y', '-200%')\n                    .attr('width', '400%').attr('height', '400%');\n                energyGlow.append('feGaussianBlur').attr('stdDeviation', '6.5').attr('result', 'blur');\n                const energyMerge = energyGlow.append('feMerge');\n                energyMerge.append('feMergeNode').attr('in', 'blur');\n                energyMerge.append('feMergeNode').attr('in', 'SourceGraphic');\n\n                const hexPath = \"M 0,-1 L 0.866,-0.5 L 0.866,0.5 L 0,1 L -0.866,0.5 L -0.866,-0.5 Z\";\n\n                let root = nodesData.find(n => n.isRoot) || nodesData[0];\n                const rootId = root.id;\n\n                const filteredNodes = nodesData.filter(n => n.isRoot || linksData.some(l => (l.target.id || l.target) === n.id && (l.source.id || l.source) === rootId));\n                const filteredLinks = linksData.filter(l => {\n                    const s = l.source.id || l.source;\n                    const t = l.target.id || l.target;\n                    return filteredNodes.some(n => n.id === s) && filteredNodes.some(n => n.id === t);\n                });\n\n                currentNodes = filteredNodes.map(d => ({...d}));\n                currentLinks = filteredLinks.map(l => ({\n                    source: currentNodes.find(n => n.id === (l.source.id || l.source)),\n                    target: currentNodes.find(n => n.id === (l.target.id || l.target))\n                })).filter(l => l.source && l.target);\n\n                lineGroup = g.append('g');\n                nodeGroup = g.append('g');\n                energyGroup = lineGroup.append('g').attr('class', 'energy-group');\n\n                const portrait = isPortrait();\n                const tablet   = isTablet();\n                let baseScale = 0.8;\n                let rootScale = 0.8;\n                if (tablet) {\n                    baseScale = 0.75;\n                    rootScale = 0.75;\n                } else if (portrait) {\n                    baseScale = 0.60;\n                    rootScale = 0.5;\n                }\n\n                lines = lineGroup.selectAll('line')\n                    .data(currentLinks)\n                    .join('line')\n                    .attr('stroke', d => getLinkColor(d))\n                    .attr('stroke-width', d => ((d.source && d.source.node_type === 'paid') || (d.target && d.target.node_type === 'paid')) ? 5 : 1)\n                    .attr('data-source', d => d.source.id)\n                    .attr('data-target', d => d.target.id);\n\n                nodeElements = nodeGroup.selectAll('g')\n                    .data(currentNodes)\n                    .join('g')\n                    .attr('class', 'clickable-node');\n\n                nodeElements.each(function(d) {\n                    const gNode = d3.select(this);\n                    let nodeScale = d.isRoot ? rootScale : baseScale;\n                    nodeScale *= (d.custom_scale || 1.15);\n\n                    if (d.isRoot) {\n                        const mainSrc = currentIsDark ? (d.image_dark || d.image_light) : d.image_light;\n                        const rootImg = gNode.append('image')\n                            .attr('class', 'root-main-image')\n                            .attr('xlink:href', mainSrc)\n                            .attr('height', 155 * nodeScale)\n                            .attr('preserveAspectRatio', 'xMidYMid meet');\n\n                        rootImg.on('load', function() {\n                            const bbox = this.getBBox();\n                            d3.select(this).attr('x', -bbox.width \/ 2).attr('y', -bbox.height \/ 2);\n                        });\n\n                        const overlayUrl = currentIsDark ? (d.overlay_dark || d.overlay_light) : d.overlay_light;\n                        if (overlayUrl) {\n                            const overlayW = (parseFloat(d.overlay_width) || 80) * nodeScale;\n                            const overlayH = (parseFloat(d.overlay_height) || 80) * nodeScale;\n                            gNode.append('image')\n                                .attr('class', 'root-overlay')\n                                .attr('xlink:href', overlayUrl)\n                                .attr('width', overlayW)\n                                .attr('height', overlayH)\n                                .attr('x', -overlayW \/ 2)\n                                .attr('y', -overlayH \/ 2)\n                                .attr('preserveAspectRatio', 'xMidYMid meet');\n                        }\n                    } \n                    else if (d.node_type === 'paid') {\n                        const radius = 55 * nodeScale;\n\n                        const border = gNode.append('circle')\n                            .attr('class', 'paid-border')\n                            .attr('r', radius + 1)\n                            .attr('stroke', getBorderColor())\n                            .attr('stroke-width', 0)\n                            .attr('fill', 'none');\n\n                        border.attr('data-original-width', '0')\n                              .attr('data-original-color', getBorderColor());\n\n                        const clipId = 'clip-paid-' + d.id;\n                        defs.append('clipPath').attr('id', clipId)\n                            .append('circle').attr('r', radius);\n\n                        const mainImageUrl = currentIsDark ? (d.image_dark || d.image_light) : d.image_light;\n                        const mainImg = gNode.append('image')\n                            .attr('xlink:href', mainImageUrl)\n                            .attr('width', radius * 2)\n                            .attr('height', radius * 2)\n                            .attr('x', -radius)\n                            .attr('y', -radius)\n                            .attr('preserveAspectRatio', 'xMidYMid cover');\n\n                        const overlayUrl = currentIsDark ? (d.overlay_dark || d.overlay_light) : d.overlay_light;\n                        if (overlayUrl) {\n                            mainImg.on('mouseover', function() {\n                                d3.select(this).attr('xlink:href', overlayUrl);\n                            }).on('mouseout', function() {\n                                d3.select(this).attr('xlink:href', mainImageUrl);\n                            });\n                        }\n                    } \n                    else if (d.node_type === 'newroot') {\n                        const radius = 50 * nodeScale;\n                        const clipId = 'clip-hex-' + d.id;\n                        if (!defs.select('#' + clipId).empty()) defs.select('#' + clipId).remove();\n\n                        defs.append('clipPath').attr('id', clipId)\n                            .append('path').attr('d', hexPath).attr('transform', `scale(${radius})`);\n\n                        const hexBorder = gNode.append('path')\n                            .attr('class', 'hex-border')\n                            .attr('d', hexPath)\n                            .attr('transform', `scale(${radius})`)\n                            .attr('fill','none')\n                            .attr('stroke', '#2ff')\n                            .attr('stroke-width', 0.10);\n\n                        hexBorder.attr('data-original-width', '0.10')\n                                 .attr('data-original-color', '#2ff');\n\n                        const mainImageUrl = currentIsDark ? (d.image_dark || d.image_light) : d.image_light;\n                        const mainImg = gNode.append('image')\n                            .attr('xlink:href', mainImageUrl)\n                            .attr('width', 90 * nodeScale)\n                            .attr('height', 90 * nodeScale)\n                            .attr('x', -45 * nodeScale)\n                            .attr('y', -45 * nodeScale)\n                            .attr('clip-path', `url(#${clipId})`)\n                            .attr('preserveAspectRatio', 'xMidYMid slice');\n\n                        const overlayUrl = currentIsDark ? (d.overlay_dark || d.overlay_light) : d.overlay_light;\n                        if (overlayUrl) {\n                            mainImg.on('mouseover', function() {\n                                d3.select(this).attr('xlink:href', overlayUrl);\n                            }).on('mouseout', function() {\n                                d3.select(this).attr('xlink:href', mainImageUrl);\n                            });\n                        }\n                    } \n                    else if (d.node_type === 'square') {\n                        const size = 90 * nodeScale;\n                        const mainImageUrl = currentIsDark ? (d.image_dark || d.image_light) : d.image_light;\n                        const overlayUrl = currentIsDark ? (d.overlay_dark || d.overlay_light) : d.overlay_light;\n\n                        const mainImg = gNode.append('image')\n                            .attr('xlink:href', mainImageUrl)\n                            .attr('x', -size\/2 + 6)\n                            .attr('y', -size\/2 + 6)\n                            .attr('width', size - 12)\n                            .attr('height', size - 12)\n                            .attr('preserveAspectRatio', 'xMidYMid meet');\n\n                        if (overlayUrl) {\n                            mainImg.on('mouseover', function() {\n                                d3.select(this).attr('xlink:href', overlayUrl);\n                            }).on('mouseout', function() {\n                                d3.select(this).attr('xlink:href', mainImageUrl);\n                            });\n                        }\n                    } \n                    else {\n                        const baseHeight = 60 * nodeScale;\n                        const realW = d.realWidth  || 52;\n                        const realH = d.realHeight || 52;\n\n                        const heightScale = baseHeight \/ realH;\n                        const renderW = realW * heightScale;\n                        const renderH = baseHeight;\n\n                        const borderCircle = gNode.append('circle')\n                            .attr('r', 37 * nodeScale)\n                            .attr('fill','none')\n                            .attr('stroke', 'none')\n                            .attr('stroke-width', 0);\n\n                        borderCircle.attr('data-original-width', '0')\n                                    .attr('data-original-color', 'none');\n\n                        const mainImageUrl = currentIsDark ? (d.image_dark || d.image_light) : d.image_light;\n                        const overlayUrl = currentIsDark ? (d.overlay_dark || d.overlay_light) : d.overlay_light;\n\n                        const mainImg = gNode.append('image')\n                            .attr('xlink:href', mainImageUrl)\n                            .attr('width', renderW)\n                            .attr('height', renderH)\n                            .attr('x', -renderW \/ 2)\n                            .attr('y', -renderH \/ 2)\n                            .attr('preserveAspectRatio', 'xMidYMid meet');\n\n                        if (overlayUrl) {\n                            mainImg.on('mouseover', function() {\n                                d3.select(this).attr('xlink:href', overlayUrl);\n                            }).on('mouseout', function() {\n                                d3.select(this).attr('xlink:href', mainImageUrl);\n                            });\n                        }\n                    }\n\n                    gNode.on('click', function(event) {\n                        event.stopImmediatePropagation();\n                        const hasVideo = !!(d.youtube_id && d.youtube_id.trim());\n\n                        if (hasVideo) {\n                            let videoId = d.youtube_id.trim();\n                            if (videoId.includes('youtube.com') || videoId.includes('youtu.be')) {\n                                const match = videoId.match(\/(?:v=|youtu\\.be\\\/)([^&\\n?#]+)\/);\n                                if (match) videoId = match[1];\n                            }\n                            currentOpenNodeId = d.id;\n\n                            const youtubePanel = document.getElementById('imm-youtube-panel');\n                            const youtubeIframe = document.getElementById('imm-youtube-iframe');\n\n                            if (youtubePanel.style.display === 'block') {\n                                youtubePanel.style.display = 'none';\n                                youtubeIframe.src = '';\n                                currentOpenNodeId = null;\n                            } else {\n                                youtubeIframe.src = `https:\/\/www.youtube.com\/embed\/${videoId}?autoplay=1&loop=1&playlist=${videoId}&mute=0&controls=1`;\n                                youtubePanel.style.display = 'block';\n                            }\n                            return;\n                        }\n\n                        if (d.url) {\n                            const url = d.url.trim();\n                            const behavior = d.open_behavior || 'popup';\n                            if (behavior === 'popup') {\n                                let w = Math.min(920, window.innerWidth * 0.92);\n                                let h = Math.min(720, window.innerHeight * 0.85);\n                                const left = (screen.width - w) \/ 2;\n                                const top = (screen.height - h) \/ 2 - 40;\n                                window.open(url, '_blank', `width=${Math.round(w)},height=${Math.round(h)},top=${Math.round(top)},left=${Math.round(left)},resizable=yes,scrollbars=yes`);\n                            } else if (behavior === 'self') {\n                                window.location.href = url;\n                            } else {\n                                window.open(url, '_blank');\n                            }\n                        }\n                    });\n                });\n\n                return { lines, nodeElements, energyGroup, currentNodes };\n            }\n\n            function layoutNodes() {\n                width = container.clientWidth;\n                height = container.clientHeight;\n\n                const portraitMode = isPortrait();\n                const layoutKey = portraitMode ? 'custom_layout_portrait' : 'custom_layout';\n\n                if (rawData[layoutKey] && Object.keys(rawData[layoutKey]).length > 0) {\n                    currentNodes.forEach(node => {\n                        const saved = rawData[layoutKey][node.id];\n                        if (saved) {\n                            node.x = saved.x * width;\n                            node.y = saved.y * height;\n                            node.custom_scale = saved.scale || 1.0;\n                        }\n                    });\n                    return;\n                }\n\n                let root = currentNodes.find(n => n.isRoot);\n                root.x = portraitMode ? 45 : width \/ 2;\n                root.y = height \/ 2;\n\n                let children = currentNodes.filter(n => !n.isRoot);\n                if (children.length === 0) return;\n\n                children.sort((a, b) => a.id - b.id);\n\n                const hexes   = children.filter(n => n.node_type === 'newroot');\n                const squares = children.filter(n => n.node_type === 'square');\n                const paids   = children.filter(n => n.node_type === 'paid');\n                const leaves  = children.filter(n => n.node_type === 'leaf' || !n.node_type);\n\n                const baseMaxDist = Math.min(width, height) * 0.42;\n                const leafMaxDistLandscape = width * 0.43;\n\n                const angleSpan = portraitMode ? Math.PI : Math.PI * 2;\n\n                let hexDistFactor    = 0.43;\n                let squareDistFactor = 0.87;\n                let paidDistFactor   = 0.70;\n                let leafDistFactor   = 1.3;\n\n                if (portraitMode) {\n                    hexDistFactor    = 5\/13;\n                    squareDistFactor = 12\/13;\n                    paidDistFactor   = 9\/13;\n                    leafDistFactor   = 19\/13;\n                }\n\n                function placeType(group, radiusFraction, typeOffset = 0) {\n                    const count = group.length;\n                    if (count === 0) return;\n                    let startAngle = portraitMode ? -Math.PI \/ 2 : -Math.PI \/ 2 + 0.2;\n                    startAngle += typeOffset * (angleSpan \/ 20);\n                    const step = angleSpan \/ Math.max(count, 1);\n                    group.forEach((node, i) => {\n                        const angle = startAngle + i * step;\n                        const dist = baseMaxDist * radiusFraction;\n                        node.x = root.x + Math.cos(angle) * dist;\n                        node.y = root.y + Math.sin(angle) * dist;\n                    });\n                }\n\n                placeType(hexes,   hexDistFactor,    -0.7);\n                placeType(squares, squareDistFactor,  0.0);\n                placeType(paids,   paidDistFactor,    0.3);\n                placeType(leaves,  leafDistFactor,    0.6);\n\n                children.forEach(node => {\n                    node.angle = Math.atan2(node.y - root.y, node.x - root.x);\n                    if (node.angle < 0) node.angle += Math.PI * 2;\n                });\n                children.sort((a, b) => a.angle - b.angle);\n\n                const globalStep = angleSpan \/ children.length;\n                const startAngle = portraitMode ? -Math.PI \/ 2 : -Math.PI \/ 2 + 0.1;\n\n                children.forEach((node, i) => {\n                    const targetAngle = startAngle + i * globalStep;\n                    const currentDist = Math.hypot(node.x - root.x, node.y - root.y);\n                    node.x = root.x + Math.cos(targetAngle) * currentDist;\n                    node.y = root.y + Math.sin(targetAngle) * currentDist;\n                });\n\n                for (let iter = 0; iter < 10; iter++) {\n                    let moved = false;\n                    for (let a = 0; a < children.length; a++) {\n                        for (let b = a + 1; b < children.length; b++) {\n                            const A = children[a], B = children[b];\n                            if (A.node_type !== 'leaf' || B.node_type !== 'leaf') continue;\n                            const dx = Math.abs(A.x - B.x);\n                            const dy = A.y - B.y;\n                            if (dx > 150) continue;\n                            const minSep = Math.max(A.realHeight || 50, B.realHeight || 50) * 1.05 + 25;\n                            if (Math.abs(dy) < minSep) {\n                                moved = true;\n                                const push = (minSep - Math.abs(dy)) \/ 2 * 0.85;\n                                if (dy >= 0) { A.y += push; B.y -= push; } else { A.y -= push; B.y += push; }\n                            }\n                        }\n                    }\n                    if (!moved) break;\n                }\n\n                staggerSameTypeDistances(squares, root, baseMaxDist);\n                applyEvenAngularSpacing(children, root, baseMaxDist, portraitMode, angleSpan);\n                separateOverlappingLeaves(children, height);\n\n                const portraitLeaves = children.filter(n => n.node_type === 'leaf' || !n.node_type);\n                if (portraitMode && portraitLeaves.length >= 3) {\n                    const degToRad = Math.PI \/ 180;\n                    const topAngle    = -Math.PI \/ 2 + 1 * degToRad;\n                    const bottomAngle =  Math.PI \/ 2 - 1 * degToRad;\n                    const leafSpan    = bottomAngle - topAngle;\n                    const leafStep    = leafSpan \/ (portraitLeaves.length - 1);\n\n                    portraitLeaves.sort((a, b) => a.id - b.id);\n\n                    portraitLeaves.forEach((node, i) => {\n                        const angle = topAngle + i * leafStep;\n                        const dist  = baseMaxDist * leafDistFactor;\n                        node.x = root.x + Math.cos(angle) * dist;\n                        node.y = root.y + Math.sin(angle) * dist;\n                    });\n                }\n\n                if (!portraitMode) {\n                    portraitLeaves.forEach(node => {\n                        const currentDist = Math.hypot(node.x - root.x, node.y - root.y);\n                        if (currentDist > leafMaxDistLandscape) {\n                            const angle = Math.atan2(node.y - root.y, node.x - root.x);\n                            node.x = root.x + Math.cos(angle) * leafMaxDistLandscape;\n                            node.y = root.y + Math.sin(angle) * leafMaxDistLandscape;\n                        }\n                    });\n                }\n\n                const padding = 55;\n                children.forEach(node => {\n                    node.x = Math.max(padding, Math.min(width - padding, node.x));\n                    node.y = Math.max(padding, Math.min(height - padding, node.y));\n                });\n\n                staggerLeavesAfterClamp(portraitLeaves, root, baseMaxDist);\n            }\n\n            function staggerSameTypeDistances(group, root, maxDist) {\n                if (group.length < 2) return;\n                const staggerAmount = maxDist \/ 11;\n                group.forEach((node, i) => {\n                    const currentDist = Math.hypot(node.x - root.x, node.y - root.y);\n                    const sign = (i % 2 === 0) ? -1 : -2.5;\n                    const newDist = Math.max(maxDist * 0.5, currentDist + sign * staggerAmount);\n                    const angle = Math.atan2(node.y - root.y, node.x - root.x);\n                    node.x = root.x + Math.cos(angle) * newDist;\n                    node.y = root.y + Math.sin(angle) * newDist;\n                });\n            }\n\n            function staggerLeavesAfterClamp(leaves, root, maxDist) {\n                if (leaves.length < 2) return;\n                const staggerAmount = maxDist \/ 13;\n                leaves.sort((a, b) => a.angle - b.angle);\n                leaves.forEach((node, i) => {\n                    const currentDist = Math.hypot(node.x - root.x, node.y - root.y);\n                    const sign = (i % 2 === 0) ? 1 : -1.5;\n                    const newDist = Math.max(maxDist * 0.55, currentDist + sign * staggerAmount);\n                    const angle = Math.atan2(node.y - root.y, node.x - root.x);\n                    node.x = root.x + Math.cos(angle) * newDist;\n                    node.y = root.y + Math.sin(angle) * newDist;\n                });\n            }\n\n            function applyEvenAngularSpacing(children, root, maxDist, portrait, angleSpan) {\n                if (children.length < 2) return;\n                const startAngle = portrait ? -Math.PI \/ 2 : -Math.PI \/ 2 + 0.1;\n                const step = angleSpan \/ children.length;\n                children.forEach((node, i) => {\n                    const idealAngle = startAngle + i * step;\n                    const dist = Math.hypot(node.x - root.x, node.y - root.y);\n                    node.x = root.x + Math.cos(idealAngle) * dist;\n                    node.y = root.y + Math.sin(idealAngle) * dist;\n                });\n            }\n\n            function separateOverlappingLeaves(children, screenHeight) {\n                const leaves = children.filter(n => n.node_type === 'leaf' || !n.node_type);\n                if (leaves.length < 2) return;\n                const centerY = screenHeight \/ 2;\n                const minGap = 5;\n                for (let iter = 0; iter < 12; iter++) {\n                    let moved = false;\n                    for (let i = 0; i < leaves.length; i++) {\n                        for (let j = i + 1; j < leaves.length; j++) {\n                            const A = leaves[i];\n                            const B = leaves[j];\n                            const dx = Math.abs(A.x - B.x);\n                            if (dx > 120) continue;\n                            const halfA = (A.realHeight || 50) \/ 2;\n                            const halfB = (B.realHeight || 50) \/ 2;\n                            const minSep = halfA + halfB + minGap;\n                            const dy = A.y - B.y;\n                            if (Math.abs(dy) < minSep) {\n                                moved = true;\n                                const push = (minSep - Math.abs(dy)) * 0.2;\n                                const distA = Math.abs(A.y - centerY);\n                                const distB = Math.abs(B.y - centerY);\n                                if (distA < distB) {\n                                    if (dy > 0) A.y += push; else A.y -= push;\n                                } else {\n                                    if (dy > 0) B.y -= push; else B.y += push;\n                                }\n                            }\n                        }\n                    }\n                    if (!moved) break;\n                }\n            }\n\n            function updatePositions(linesSel, nodesSel) {\n                linesSel.each(function(d) {\n                    d3.select(this)\n                        .attr('x1', d.source.x).attr('y1', d.source.y)\n                        .attr('x2', d.target.x).attr('y2', d.target.y);\n                });\n                nodesSel.attr('transform', d => `translate(${d.x}, ${d.y})`);\n            }\n\n            function updateAllImages() {\n                const isDark = isDarkTheme();\n                if (isDark === currentIsDark) return;\n                currentIsDark = isDark;\n\n                nodeElements.each(function(d) {\n                    const gNode = d3.select(this);\n                    if (d.isRoot) {\n                        const mainSrc = isDark ? (d.image_dark || d.image_light) : d.image_light;\n                        gNode.select('.root-main-image').attr('xlink:href', mainSrc);\n                        const overlaySrc = isDark ? (d.overlay_dark || d.overlay_light) : d.overlay_light;\n                        const overlayImg = gNode.select('.root-overlay');\n                        if (!overlayImg.empty() && overlaySrc) overlayImg.attr('xlink:href', overlaySrc);\n                    } else {\n                        const mainSrc = isDark ? (d.image_dark || d.image_light) : d.image_light;\n                        gNode.select('image').filter(function() {\n                            return !d3.select(this).classed('root-overlay');\n                        }).attr('xlink:href', mainSrc);\n                    }\n                });\n\n                lines.attr('stroke', d => getLinkColor(d));\n            }\n\n            function createPulseOnLink(link, baseDelay = 0) {\n                const linkId = `${link.source.id}-${link.target.id}`;\n                if (activePulses.has(linkId)) return;\n\n                const dx = link.target.x - link.source.x;\n                const dy = link.target.y - link.source.y;\n                const length = Math.sqrt(dx*dx + dy*dy);\n                if (length < 20) return;\n\n                const pulseColor = getPulseColor();\n                const pulse = energyGroup.append('path')\n                    .attr('class', 'energy-pulse')\n                    .attr('stroke', pulseColor)\n                    .attr('d', `M ${link.source.x} ${link.source.y} L ${link.target.x} ${link.target.y}`)\n                    .attr('stroke-dasharray', '23, ' + (length - 23))\n                    .attr('stroke-dashoffset', length);\n\n                activePulses.set(linkId, true);\n\n                const extraRandomDelay = Math.random() * 650;\n                const duration = 750 + Math.random() * 950;\n\n                pulse.transition()\n                    .delay(baseDelay + extraRandomDelay)\n                    .duration(duration)\n                    .ease(d3.easeLinear)\n                    .attr('stroke-dashoffset', 0)\n                    .on('end', () => {\n                        pulse.remove();\n                        activePulses.delete(linkId);\n                    });\n            }\n\n            function startEnergyFlow() {\n                const rootLinks = currentLinks.filter(l => l.source.isRoot);\n                const otherLinks = currentLinks.filter(l => !l.source.isRoot);\n                rootLinks.forEach((link, i) => createPulseOnLink(link, i * 180));\n                otherLinks.forEach(link => createPulseOnLink(link, Math.random() * 400));\n                setTimeout(startEnergyFlow, 900 + Math.random() * 1100);\n            }\n\n            let result = createMindmapElements();\n            lines = result.lines;\n            nodeElements = result.nodeElements;\n            energyGroup = result.energyGroup;\n            currentNodes = result.currentNodes;\n\n            layoutNodes();\n            updatePositions(lines, nodeElements);\n\n            \/\/ Hover handlers (placed AFTER layout & positioning so transforms are correct)\n            nodeElements.filter(d => !d.isRoot).each(function(d) {\n                const nodeSel = d3.select(this);\n                const nodeId = d.id;\n\n                const connectedLine = lines.filter(l => \n                    (l.source && l.source.id === nodeId) || (l.target && l.target.id === nodeId)\n                );\n\n                let borderEl = null;\n                if (d.node_type === 'paid') {\n                    borderEl = nodeSel.select('.paid-border');\n                } else if (d.node_type === 'newroot') {\n                    borderEl = nodeSel.select('.hex-border');\n                } else {\n                    borderEl = nodeSel.select('circle[stroke]');\n                }\n\n                const originalLineWidth = connectedLine.attr('stroke-width') || 1;\n\n                nodeSel.on('mouseover', function() {\n                    \/\/ Scale around center (content is already at local (0,0))\n                    const tx = d.x || 0;\n                    const ty = d.y || 0;\n                    nodeSel.transition().duration(120)\n                        .attr('transform', `translate(${tx}, ${ty}) scale(1.05)`);\n\n                    if (borderEl && borderEl.node()) {\n                        const origW = parseFloat(borderEl.attr('data-original-width') || 0);\n                        if (origW > 0) {\n                            borderEl\n                                .attr('stroke', getHoverBorderColor())\n                                .attr('stroke-width', origW * 1.5);\n                        }\n                    }\n\n                    if (connectedLine.node()) {\n                        connectedLine\n                            .attr('stroke', getHoverBorderColor())\n                            .attr('stroke-width', 5);\n                    }\n                })\n                .on('mouseout', function() {\n                    const tx = d.x || 0;\n                    const ty = d.y || 0;\n                    nodeSel.transition().duration(120)\n                        .attr('transform', `translate(${tx}, ${ty})`);\n\n                    if (borderEl && borderEl.node()) {\n                        const origW = parseFloat(borderEl.attr('data-original-width') || 0);\n                        const origColor = borderEl.attr('data-original-color') || getBorderColor();\n                        if (origW > 0) {\n                            borderEl\n                                .attr('stroke', origColor)\n                                .attr('stroke-width', origW);\n                        }\n                    }\n\n                    if (connectedLine.node()) {\n                        connectedLine\n                            .attr('stroke', getLinkColor(connectedLine.datum()))\n                            .attr('stroke-width', originalLineWidth);\n                    }\n                });\n            });\n\n            setTimeout(() => startEnergyFlow(), 150);\n\n            const observer = new MutationObserver(updateAllImages);\n            observer.observe(document.body, { attributes: true, attributeFilter: ['data-theme'] });\n            if (window.matchMedia) window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateAllImages);\n\n            let resizeTimeout;\n            function resizeAndUpdate() {\n                clearTimeout(resizeTimeout);\n                resizeTimeout = setTimeout(() => {\n                    cleanupPulses();\n                    width = container.clientWidth;\n                    height = container.clientHeight;\n\n                    result = createMindmapElements();\n                    lines = result.lines;\n                    nodeElements = result.nodeElements;\n                    energyGroup = result.energyGroup;\n                    currentNodes = result.currentNodes;\n\n                    layoutNodes();\n                    updatePositions(lines, nodeElements);\n\n                    svg.attr('width', width).attr('height', height).attr('viewBox', `0 0 ${width} ${height}`);\n\n                    setTimeout(() => startEnergyFlow(), 80);\n                }, 180);\n            }\n\n            window.addEventListener('resize', resizeAndUpdate);\n            window.addEventListener('orientationchange', () => setTimeout(resizeAndUpdate, 150));\n\n            const youtubePanel = document.getElementById('imm-youtube-panel');\n            const youtubeIframe = document.getElementById('imm-youtube-iframe');\n            const closeBtn = document.getElementById('imm-close-btn');\n            const enterSiteBtn = document.getElementById('imm-enter-site-btn');\n\n            function makeDraggable(panel, handle) {\n                let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;\n                handle.onmousedown = function(e) {\n                    if (e.target.tagName === 'BUTTON') return;\n                    e.preventDefault();\n                    pos3 = e.clientX; pos4 = e.clientY;\n                    document.onmouseup = () => { document.onmouseup = document.onmousemove = null; };\n                    document.onmousemove = function(ev) {\n                        ev.preventDefault();\n                        pos1 = pos3 - ev.clientX;\n                        pos2 = pos4 - ev.clientY;\n                        pos3 = ev.clientX; pos4 = ev.clientY;\n                        panel.style.top = (panel.offsetTop - pos2) + \"px\";\n                        panel.style.left = (panel.offsetLeft - pos1) + \"px\";\n                        panel.style.transform = \"none\";\n                    };\n                };\n            }\n            makeDraggable(youtubePanel, document.getElementById('imm-video-header'));\n\n            closeBtn.addEventListener('click', () => {\n                youtubePanel.style.display = 'none';\n                youtubeIframe.src = '';\n                currentOpenNodeId = null;\n            });\n\n            enterSiteBtn.addEventListener('click', () => {\n                if (!currentOpenNodeId) return;\n                const openNode = currentNodes.find(n => n.id === currentOpenNodeId);\n                if (openNode && openNode.url && openNode.url.trim() !== '') {\n                    youtubePanel.style.display = 'none';\n                    youtubeIframe.src = '';\n                    const url = openNode.url.trim();\n                    let w = Math.min(920, window.innerWidth * 0.92);\n                    let h = Math.min(720, window.innerHeight * 0.85);\n                    const left = (screen.width - w) \/ 2;\n                    const top = (screen.height - h) \/ 2 - 40;\n                    window.open(url, '_blank', `width=${Math.round(w)},height=${Math.round(h)},top=${Math.round(top)},left=${Math.round(left)},resizable=yes,scrollbars=yes`);\n                    currentOpenNodeId = null;\n                }\n            });\n        }\n    });\n    <\/script>\n    \n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_angie_page":false,"footnotes":""},"class_list":["post-104","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>- qubicportal.org<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/qubicportal.org\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"- qubicportal.org\" \/>\n<meta property=\"og:url\" content=\"https:\/\/qubicportal.org\/\" \/>\n<meta property=\"og:site_name\" content=\"qubicportal.org\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-30T14:12:51+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/qubicportal.org\\\/\",\"url\":\"https:\\\/\\\/qubicportal.org\\\/\",\"name\":\"- qubicportal.org\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/qubicportal.org\\\/#website\"},\"datePublished\":\"2026-04-14T05:19:14+00:00\",\"dateModified\":\"2026-04-30T14:12:51+00:00\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/qubicportal.org\\\/\"]}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/qubicportal.org\\\/#website\",\"url\":\"https:\\\/\\\/qubicportal.org\\\/\",\"name\":\"qubicportal.org\",\"description\":\"the gateway to the qubic ecosystem\",\"publisher\":{\"@id\":\"https:\\\/\\\/qubicportal.org\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/qubicportal.org\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/qubicportal.org\\\/#organization\",\"name\":\"qubicportal.org\",\"url\":\"https:\\\/\\\/qubicportal.org\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/qubicportal.org\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/qubicportal.org\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/newclearQP-ezgif.com-gif-maker-1.png\",\"contentUrl\":\"https:\\\/\\\/qubicportal.org\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/newclearQP-ezgif.com-gif-maker-1.png\",\"width\":824,\"height\":190,\"caption\":\"qubicportal.org\"},\"image\":{\"@id\":\"https:\\\/\\\/qubicportal.org\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"- qubicportal.org","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/qubicportal.org\/","og_locale":"en_US","og_type":"article","og_title":"- qubicportal.org","og_url":"https:\/\/qubicportal.org\/","og_site_name":"qubicportal.org","article_modified_time":"2026-04-30T14:12:51+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/qubicportal.org\/","url":"https:\/\/qubicportal.org\/","name":"- qubicportal.org","isPartOf":{"@id":"https:\/\/qubicportal.org\/#website"},"datePublished":"2026-04-14T05:19:14+00:00","dateModified":"2026-04-30T14:12:51+00:00","inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/qubicportal.org\/"]}]},{"@type":"WebSite","@id":"https:\/\/qubicportal.org\/#website","url":"https:\/\/qubicportal.org\/","name":"qubicportal.org","description":"the gateway to the qubic ecosystem","publisher":{"@id":"https:\/\/qubicportal.org\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/qubicportal.org\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/qubicportal.org\/#organization","name":"qubicportal.org","url":"https:\/\/qubicportal.org\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/qubicportal.org\/#\/schema\/logo\/image\/","url":"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/newclearQP-ezgif.com-gif-maker-1.png","contentUrl":"https:\/\/qubicportal.org\/wp-content\/uploads\/2026\/04\/newclearQP-ezgif.com-gif-maker-1.png","width":824,"height":190,"caption":"qubicportal.org"},"image":{"@id":"https:\/\/qubicportal.org\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/qubicportal.org\/index.php?rest_route=\/wp\/v2\/pages\/104","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qubicportal.org\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/qubicportal.org\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/qubicportal.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/qubicportal.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=104"}],"version-history":[{"count":5,"href":"https:\/\/qubicportal.org\/index.php?rest_route=\/wp\/v2\/pages\/104\/revisions"}],"predecessor-version":[{"id":646,"href":"https:\/\/qubicportal.org\/index.php?rest_route=\/wp\/v2\/pages\/104\/revisions\/646"}],"wp:attachment":[{"href":"https:\/\/qubicportal.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=104"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}