{"id":35061,"date":"2021-08-16T13:09:28","date_gmt":"2021-08-16T11:09:28","guid":{"rendered":"https:\/\/worldofstudents.org\/test-tim\/"},"modified":"2025-12-15T09:04:45","modified_gmt":"2025-12-15T08:04:45","slug":"test-tim","status":"publish","type":"page","link":"https:\/\/worldofstudents.org\/en\/test-tim\/","title":{"rendered":"Test Tim"},"content":{"rendered":"<p>[et_pb_section fb_built=&#8221;1&#8243; fullwidth=&#8221;on&#8221; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; background_color=&#8221;#005499&#8243; global_colors_info=&#8221;{}&#8221;][et_pb_fullwidth_code disabled_on=&#8221;on|on|on&#8221; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; hover_enabled=&#8221;0&#8243; global_colors_info=&#8221;{}&#8221; sticky_enabled=&#8221;0&#8243; admin_label=&#8221;12-12-2025&#8243; disabled=&#8221;on&#8221;]<script src=\"https:\/\/d3js.org\/d3.v7.min.js\"><\/script><!-- [et_pb_line_break_holder] --><script src=\"https:\/\/cdn.jsdelivr.net\/npm\/topojson-client@3\/dist\/topojson-client.min.js\"><\/script><!-- [et_pb_line_break_holder] --><\/p>\n<style><!-- [et_pb_line_break_holder] -->    html, body {<!-- [et_pb_line_break_holder] -->        margin: 0;<!-- [et_pb_line_break_holder] -->        padding: 0;<!-- [et_pb_line_break_holder] -->        height: 100%;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/* Wrapper defines how long the sticky canvas stays fixed *\/<!-- [et_pb_line_break_holder] -->    .earth-sticky-wrapper {<!-- [et_pb_line_break_holder] -->        position: relative;<!-- [et_pb_line_break_holder] -->        height: 600vh; \/* Extended for continent tours *\/<!-- [et_pb_line_break_holder] -->        max-width: 1080px;<!-- [et_pb_line_break_holder] -->        margin:0 auto;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .earth-container {<!-- [et_pb_line_break_holder] -->        position: sticky;<!-- [et_pb_line_break_holder] -->        top: 150px;<!-- [et_pb_line_break_holder] -->        width: 100%;<!-- [et_pb_line_break_holder] -->        max-width: 1080px;<!-- [et_pb_line_break_holder] -->        height: 75vh;<!-- [et_pb_line_break_holder] -->        margin: 0 auto;<!-- [et_pb_line_break_holder] -->        background: transparent;<!-- [et_pb_line_break_holder] -->        border-radius: 0;<!-- [et_pb_line_break_holder] -->        overflow: visible;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    #earth-canvas {<!-- [et_pb_line_break_holder] -->        width: 100%;<!-- [et_pb_line_break_holder] -->        height: 100%;<!-- [et_pb_line_break_holder] -->        display: block;<!-- [et_pb_line_break_holder] -->        background: transparent;<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        top: 0;<!-- [et_pb_line_break_holder] -->        left: 0;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .loading-overlay {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        top: 0;<!-- [et_pb_line_break_holder] -->        left: 0;<!-- [et_pb_line_break_holder] -->        right: 0;<!-- [et_pb_line_break_holder] -->        bottom: 0;<!-- [et_pb_line_break_holder] -->        background: rgba(0, 0, 0, 0.8);<!-- [et_pb_line_break_holder] -->        display: flex;<!-- [et_pb_line_break_holder] -->        align-items: center;<!-- [et_pb_line_break_holder] -->        justify-content: center;<!-- [et_pb_line_break_holder] -->        border-radius: 16px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .loading-text {<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 16px;<!-- [et_pb_line_break_holder] -->        font-weight: 500;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-state {<!-- [et_pb_line_break_holder] -->        padding: 32px;<!-- [et_pb_line_break_holder] -->        text-align: center;<!-- [et_pb_line_break_holder] -->        color: #ef4444;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-title {<!-- [et_pb_line_break_holder] -->        font-weight: 600;<!-- [et_pb_line_break_holder] -->        margin-bottom: 8px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-message {<!-- [et_pb_line_break_holder] -->        color: #999999;<!-- [et_pb_line_break_holder] -->        font-size: 14px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .spinner {<!-- [et_pb_line_break_holder] -->        width: 24px;<!-- [et_pb_line_break_holder] -->        height: 24px;<!-- [et_pb_line_break_holder] -->        border: 2px solid #333333;<!-- [et_pb_line_break_holder] -->        border-top: 2px solid #ffffff;<!-- [et_pb_line_break_holder] -->        border-radius: 50%;<!-- [et_pb_line_break_holder] -->        animation: spin 1s linear infinite;<!-- [et_pb_line_break_holder] -->        margin-right: 12px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    @keyframes spin {<!-- [et_pb_line_break_holder] -->        0% { transform: rotate(0deg); }<!-- [et_pb_line_break_holder] -->        100% { transform: rotate(360deg); }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .phase-text {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        left: 50%;<!-- [et_pb_line_break_holder] -->        bottom: 36%;<!-- [et_pb_line_break_holder] -->        transform: translate(-50%, 12px);<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 18px;<!-- [et_pb_line_break_holder] -->        font-weight: 700;<!-- [et_pb_line_break_holder] -->        letter-spacing: 0.4px;<!-- [et_pb_line_break_holder] -->        text-shadow: 0 2px 10px rgba(0,0,0,0.55);<!-- [et_pb_line_break_holder] -->        opacity: 0;<!-- [et_pb_line_break_holder] -->        transition: opacity 0.35s ease, transform 0.35s ease;<!-- [et_pb_line_break_holder] -->        pointer-events: none;<!-- [et_pb_line_break_holder] -->        max-width: 84%;<!-- [et_pb_line_break_holder] -->        text-align: center;<!-- [et_pb_line_break_holder] -->        line-height: 1.45;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .phase-text.visible {<!-- [et_pb_line_break_holder] -->        opacity: 1;<!-- [et_pb_line_break_holder] -->        transform: translate(-50%, 0);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .autoplay-btn {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        right: 32px;<!-- [et_pb_line_break_holder] -->        top: 32px;<!-- [et_pb_line_break_holder] -->        padding: 10px 14px;<!-- [et_pb_line_break_holder] -->        border-radius: 12px;<!-- [et_pb_line_break_holder] -->        border: 1px solid rgba(255,255,255,0.3);<!-- [et_pb_line_break_holder] -->        background: rgba(0,0,0,0.45);<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 13px;<!-- [et_pb_line_break_holder] -->        font-weight: 600;<!-- [et_pb_line_break_holder] -->        cursor: pointer;<!-- [et_pb_line_break_holder] -->        backdrop-filter: blur(6px);<!-- [et_pb_line_break_holder] -->        transition: background 0.2s ease, transform 0.2s ease, opacity 0.2s ease;<!-- [et_pb_line_break_holder] -->        z-index: 5;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .autoplay-btn:hover {<!-- [et_pb_line_break_holder] -->        background: rgba(0,0,0,0.65);<!-- [et_pb_line_break_holder] -->        transform: translateY(-1px);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .autoplay-btn:active {<!-- [et_pb_line_break_holder] -->        transform: translateY(0);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><\/style>\n<p><!-- [et_pb_line_break_holder] --><\/p>\n<div class=\"earth-sticky-wrapper\" id=\"earth-sticky-wrapper\"><!-- [et_pb_line_break_holder] -->    <\/p>\n<div class=\"earth-container\"><!-- [et_pb_line_break_holder] -->        <canvas id=\"earth-canvas\"><\/canvas><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"loading-overlay\" class=\"loading-overlay\"><!-- [et_pb_line_break_holder] -->            <\/p>\n<div class=\"spinner\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->            <\/p>\n<div class=\"loading-text\">Loading Earth data&#8230;<\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"tooltip\" style=\"position: absolute; display: none; background: rgba(0,0,0,0.8); color: white; padding: 5px; border-radius: 3px; pointer-events: none; z-index: 1000;\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"phase-text\" class=\"phase-text\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->        <button id=\"autoplay-btn\" class=\"autoplay-btn\">Autoplay<\/button><!-- [et_pb_line_break_holder] -->    <\/div>\n<p><!-- [et_pb_line_break_holder] --><\/div>\n<p><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --><script><!-- [et_pb_line_break_holder] -->    \/\/ Fallback dataset used if the remote universities.json cannot be loaded<!-- [et_pb_line_break_holder] -->    const universityData = [<!-- [et_pb_line_break_holder] -->        {\"name\":\"Vancouver Island University\",\"lat\":\"49.1578851\",\"lng\":\"-123.9656421\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"International College of Management, Sydney (ICMS)\",\"lat\":\"-33.8040340\",\"lng\":\"151.2937461\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of North Carolina Wilmington\",\"lat\":\"34.2249827\",\"lng\":\"-77.8690774\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Griffith College Dublin\",\"lat\":\"53.3311882\",\"lng\":\"-6.2802781\",\"country\":\"Ireland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EUSA University Centre, Seville, Spain\",\"lat\":\"37.3754972\",\"lng\":\"-5.9806833\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Cesine Santander\",\"lat\":\"43.4715066\",\"lng\":\"-3.7915862\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Asia Pacific University of Technology & Innovation (APU)\",\"lat\":\"3.0479178\",\"lng\":\"101.6892466\",\"country\":\"Malaysia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Francisco State University\",\"lat\":\"37.7245167\",\"lng\":\"-122.4799957\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"International Business School Budapest\",\"lat\":\"47.5629570\",\"lng\":\"19.0541941\",\"country\":\"Hungary\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EU Business School Barcelona\",\"lat\":\"41.3825802\",\"lng\":\"2.1770730\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Institut Sup\u00e9rieur de Gestion (ISG) Paris\",\"lat\":\"48.8672799\",\"lng\":\"2.2758497\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Woosong University\",\"lat\":\"36.3392944\",\"lng\":\"127.4506140\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"SolBridge International School of Business\",\"lat\":\"36.3383278\",\"lng\":\"127.4324307\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Europea de Madrid\",\"lat\":\"40.3727017\",\"lng\":\"-3.9185823\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"St. Francis College New York\",\"lat\":\"40.6900603\",\"lng\":\"-73.9865997\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universitat Aut\u00f2noma de Barcelona\",\"lat\":\"41.5015527\",\"lng\":\"2.1062607\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of the Fraser Valley\",\"lat\":\"49.0283443\",\"lng\":\"-122.2849135\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad San Ignacio de Loyola\",\"lat\":\"-12.0734419\",\"lng\":\"-76.9484914\",\"country\":\"Peru\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Francisco de Vitoria Madrid\",\"lat\":\"40.4402647\",\"lng\":\"-3.8339056\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"American College of Thessaloniki\",\"lat\":\"40.5684100\",\"lng\":\"22.9912375\",\"country\":\"Greece\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Hankuk University of Foreign Studies\",\"lat\":\"37.5970815\",\"lng\":\"127.0587413\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Vrije Universiteit Amsterdam\",\"lat\":\"52.3340539\",\"lng\":\"4.8651882\",\"country\":\"Netherlands\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Austral\",\"lat\":\"-34.5934652\",\"lng\":\"-58.3833079\",\"country\":\"Argentina\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad de Alicante\",\"lat\":\"38.3850684\",\"lng\":\"-0.5146732\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universit\u00e0 Europea di Roma\",\"lat\":\"41.8791802\",\"lng\":\"12.3946472\",\"country\":\"Italy\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Cat\u00f3lica San Antonio de Murcia\",\"lat\":\"38.0042077\",\"lng\":\"-1.1774782\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Xi'an Jiaotong-Liverpool University\",\"lat\":\"31.2787148\",\"lng\":\"120.7237629\",\"country\":\"China\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universit\u00e0 Cattolica del Sacro Cuore\",\"lat\":\"45.4609998\",\"lng\":\"9.1769914\",\"country\":\"Italy\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"College of the Desert\",\"lat\":\"33.7324666\",\"lng\":\"-116.3868288\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Santa Barbara City College\",\"lat\":\"34.4058643\",\"lng\":\"-119.6974204\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Victoria University of Wellington\",\"lat\":\"-41.2793942\",\"lng\":\"174.7783525\",\"country\":\"New Zealand\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"City University of Seattle\",\"lat\":\"47.6177084\",\"lng\":\"-122.3444624\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of the Sunshine Coast (UniSC)\",\"lat\":\"-26.7159208\",\"lng\":\"153.0563306\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of Brighton\",\"lat\":\"50.8421128\",\"lng\":\"-0.1194002\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"London South Bank University\",\"lat\":\"51.4991489\",\"lng\":\"-0.0970188\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Dorset College Dublin\",\"lat\":\"53.3582148\",\"lng\":\"-6.2577212\",\"country\":\"Ireland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"BI Norwegian Business School\",\"lat\":\"59.9488700\",\"lng\":\"10.7682070\",\"country\":\"Norway\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EU Business School Geneva\",\"lat\":\"46.2067776\",\"lng\":\"6.1451264\",\"country\":\"Switzerland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"London Metropolitan University\",\"lat\":\"51.5522032\",\"lng\":\"-0.1111950\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Adolfo Ibanez\",\"lat\":\"-33.0196718\",\"lng\":\"-71.5304035\",\"country\":\"Chile\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Jose State University\",\"lat\":\"37.3351902\",\"lng\":\"-121.8812255\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Europea de Valencia\",\"lat\":\"39.4755093\",\"lng\":\"-0.3652411\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Latinoamericana de Cienca y Tecnologia\",\"lat\":\"9.9325427\",\"lng\":\"-84.0795782\",\"country\":\"Costa Rica\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Diego State University\",\"lat\":\"32.7760640\",\"lng\":\"-117.0702300\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Bond University\",\"lat\":\"-28.0724817\",\"lng\":\"153.4157960\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California San Diego\",\"lat\":\"32.8792438\",\"lng\":\"-117.2311247\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Alliant International University\",\"lat\":\"32.8966666\",\"lng\":\"-117.0938567\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"James Cook University Singapore\",\"lat\":\"1.3153627\",\"lng\":\"103.8760268\",\"country\":\"Singapore\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Capilano University\",\"lat\":\"49.3179806\",\"lng\":\"-123.0194770\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"James Cook University Australia\",\"lat\":\"-19.3293891\",\"lng\":\"146.7611734\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Nebrija University\",\"lat\":\"40.4295684\",\"lng\":\"-3.7130672\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California Santa Barbara\",\"lat\":\"34.4145523\",\"lng\":\"-119.8468383\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California Berkeley Extension\",\"lat\":\"37.8708393\",\"lng\":\"-122.2728630\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Westcliff University\",\"lat\":\"33.6853732\",\"lng\":\"-117.8480110\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"The University of Sydney\",\"lat\":\"-33.8854217\",\"lng\":\"151.1890168\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California, Los Angeles (UCLA) Extension\",\"lat\":\"34.0595933\",\"lng\":\"-118.4463883\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Auckland University of Technology\",\"lat\":\"-36.8525224\",\"lng\":\"174.7667714\",\"country\":\"New Zealand\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Pace University\",\"lat\":\"40.7111050\",\"lng\":\"-74.0046134\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Edinburgh Napier University\",\"lat\":\"55.9244726\",\"lng\":\"-3.2888614\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of Westminster\",\"lat\":\"51.5209064\",\"lng\":\"-0.1400730\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"IPAG Business School Paris\",\"lat\":\"48.8544327\",\"lng\":\"2.3312200\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Veritas\",\"lat\":\"9.9254713\",\"lng\":\"-84.0648763\",\"country\":\"Costa Rica\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"IPAG Business School Nice\",\"lat\":\"43.7145200\",\"lng\":\"7.2652823\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Royal Melbourne Institute of Technology Vietnam\",\"lat\":\"10.7755254\",\"lng\":\"106.7021047\",\"country\":\"Viet Nam\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Curtin University Dubai\",\"lat\":\"25.0742823\",\"lng\":\"55.1885387\",\"country\":\"United Arab Emirates\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Curtin University Singapore\",\"lat\":\"1.2884206\",\"lng\":\"103.7795669\",\"country\":\"Singapore\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"LCI Barcelona\",\"lat\":\"41.3994905\",\"lng\":\"2.1902139\",\"country\":\"Spain\"}<!-- [et_pb_line_break_holder] -->    ];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    class RotatingEarth {<!-- [et_pb_line_break_holder] -->        constructor(canvasId, options = {}) {<!-- [et_pb_line_break_holder] -->            this.canvas = document.getElementById(canvasId);<!-- [et_pb_line_break_holder] -->            this.context = this.canvas.getContext('2d');<!-- [et_pb_line_break_holder] -->            this.loadingOverlay = document.getElementById('loading-overlay');<!-- [et_pb_line_break_holder] -->            this.tooltip = document.getElementById('tooltip');<!-- [et_pb_line_break_holder] -->            this.phaseText = document.getElementById('phase-text');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.width = options.width || 800;<!-- [et_pb_line_break_holder] -->            this.height = options.height || 600;<!-- [et_pb_line_break_holder] -->            this.isLoading = true;<!-- [et_pb_line_break_holder] -->            this.error = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.allDots = [];<!-- [et_pb_line_break_holder] -->            this.landFeatures = null;<!-- [et_pb_line_break_holder] -->            this.universities = [];<!-- [et_pb_line_break_holder] -->            this.highlightedFeatureIndices = new Set();<!-- [et_pb_line_break_holder] -->            this.initialRotation = [0, -38];<!-- [et_pb_line_break_holder] -->            this.rotation = [...this.initialRotation];<!-- [et_pb_line_break_holder] -->            this.scrollBaseRotation = this.rotation[0];<!-- [et_pb_line_break_holder] -->            this.autoRotate = true;<!-- [et_pb_line_break_holder] -->            this.rotationSpeed = 0.25;<!-- [et_pb_line_break_holder] -->            this.rotationTimer = null;<!-- [et_pb_line_break_holder] -->            this.logoImage = null;<!-- [et_pb_line_break_holder] -->            this.isInteracting = false;<!-- [et_pb_line_break_holder] -->            this.bgColor = '#005499';<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->            this.autoPlayStart = 0;<!-- [et_pb_line_break_holder] -->            this.autoPlayDuration = 24000; \/\/ ms<!-- [et_pb_line_break_holder] -->            this.autoPlayHandle = null;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'globe';<!-- [et_pb_line_break_holder] -->            this.orthoProjection = null;<!-- [et_pb_line_break_holder] -->            this.flatProjection = null;<!-- [et_pb_line_break_holder] -->            this.path = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.graticuleGeo = d3.geoGraticule().step([15, 15])(); \/\/ MultiLineString<!-- [et_pb_line_break_holder] -->            this.sphereBoundary = this.buildSphereBoundaryCoords();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Continent tour configuration<!-- [et_pb_line_break_holder] -->            this.continentTours = [<!-- [et_pb_line_break_holder] -->                { name: 'Europe', lng: 10, lat: 50, zoom: 2.5 },<!-- [et_pb_line_break_holder] -->                { name: 'North America', lng: -100, lat: 40, zoom: 2.2 },<!-- [et_pb_line_break_holder] -->                { name: 'South America', lng: -60, lat: -20, zoom: 2.3 },<!-- [et_pb_line_break_holder] -->                { name: 'Asia', lng: 100, lat: 30, zoom: 2.2 },<!-- [et_pb_line_break_holder] -->                { name: 'Oceania', lng: 150, lat: -25, zoom: 2.5 }<!-- [et_pb_line_break_holder] -->            ];<!-- [et_pb_line_break_holder] -->            this.currentTourIndex = -1;<!-- [et_pb_line_break_holder] -->            this.baseFlatScale = null;<!-- [et_pb_line_break_holder] -->            this.showLabels = false;<!-- [et_pb_line_break_holder] -->            this.currentZoom = 1;<!-- [et_pb_line_break_holder] -->            this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->            this.zoomPhaseProgress = 0;<!-- [et_pb_line_break_holder] -->            this.zoomPhaseProgress = 0; \/\/ 0 at zoom start, 1 at end to fade graticule<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.init();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        init() {<!-- [et_pb_line_break_holder] -->            this.setupCanvas();<!-- [et_pb_line_break_holder] -->            this.setupProjection();<!-- [et_pb_line_break_holder] -->            this.setupInteractions();<!-- [et_pb_line_break_holder] -->            window.addEventListener('resize', () => this.handleResize(), { passive: true });<!-- [et_pb_line_break_holder] -->            this.loadWorldData();<!-- [et_pb_line_break_holder] -->            this.loadLogo();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        getDefaultContainerSize() {<!-- [et_pb_line_break_holder] -->            const containerWidth = Math.min(this.width, Math.max(320, window.innerWidth - 40));<!-- [et_pb_line_break_holder] -->            const maxHeight = Math.min(<!-- [et_pb_line_break_holder] -->                this.height,<!-- [et_pb_line_break_holder] -->                window.innerHeight - 40,<!-- [et_pb_line_break_holder] -->                containerWidth * 0.55 \/\/ align closer to final flat map height<!-- [et_pb_line_break_holder] -->            );<!-- [et_pb_line_break_holder] -->            const containerHeight = Math.max(320, maxHeight);<!-- [et_pb_line_break_holder] -->            return { containerWidth, containerHeight };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setCanvasSize(containerWidth, containerHeight, dprOverride) {<!-- [et_pb_line_break_holder] -->            const dpr = dprOverride ?? Math.min(3, window.devicePixelRatio || 2);<!-- [et_pb_line_break_holder] -->            this.dpr = dpr;<!-- [et_pb_line_break_holder] -->            \/\/ Smaller globe radius so the start view sits comfortably in the container<!-- [et_pb_line_break_holder] -->            this.radius = Math.min(containerWidth, containerHeight) \/ 3.0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.canvas.width = containerWidth * dpr;<!-- [et_pb_line_break_holder] -->            this.canvas.height = containerHeight * dpr;<!-- [et_pb_line_break_holder] -->            this.canvas.style.width = `${containerWidth}px`;<!-- [et_pb_line_break_holder] -->            this.canvas.style.height = `${containerHeight}px`;<!-- [et_pb_line_break_holder] -->            this.context.setTransform(dpr, 0, 0, dpr, 0, 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.containerWidth = containerWidth;<!-- [et_pb_line_break_holder] -->            this.containerHeight = containerHeight;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.setupProjection();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupCanvas() {<!-- [et_pb_line_break_holder] -->            const { containerWidth, containerHeight } = this.getDefaultContainerSize();<!-- [et_pb_line_break_holder] -->            this.setCanvasSize(containerWidth, containerHeight);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        sizeFactor() {<!-- [et_pb_line_break_holder] -->            return window.innerWidth < 768 ? 0.7 : 1;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleResize() {<!-- [et_pb_line_break_holder] -->            if (this.isRecording) return; \/\/ avoid disrupting active capture<!-- [et_pb_line_break_holder] -->            const { containerWidth, containerHeight } = this.getDefaultContainerSize();<!-- [et_pb_line_break_holder] -->            const oldBaseScale = this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.setCanvasSize(containerWidth, containerHeight);<!-- [et_pb_line_break_holder] -->            \/\/ Preserve relative zoom if we're in a continent tour<!-- [et_pb_line_break_holder] -->            if (oldBaseScale && this.mode === 'flat') {<!-- [et_pb_line_break_holder] -->                const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->                const zoomRatio = currentScale \/ oldBaseScale;<!-- [et_pb_line_break_holder] -->                this.flatProjection.scale(this.baseFlatScale * zoomRatio);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupProjection() {<!-- [et_pb_line_break_holder] -->            this.orthoProjection = d3.geoOrthographic()<!-- [et_pb_line_break_holder] -->                .scale(this.radius)<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2])<!-- [et_pb_line_break_holder] -->                .clipAngle(90)<!-- [et_pb_line_break_holder] -->                .rotate(this.rotation);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const flatScale = (this.containerWidth \/ (2 * Math.PI)) * 0.9;<!-- [et_pb_line_break_holder] -->            this.baseFlatScale = flatScale;<!-- [et_pb_line_break_holder] -->            this.flatProjection = d3.geoEquirectangular()<!-- [et_pb_line_break_holder] -->                .center([0, 0])<!-- [et_pb_line_break_holder] -->                .scale(flatScale)<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.path = d3.geoPath()<!-- [et_pb_line_break_holder] -->                .projection(this.orthoProjection)<!-- [et_pb_line_break_holder] -->                .context(this.context);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        pointInPolygon(point, polygon) {<!-- [et_pb_line_break_holder] -->            const [x, y] = point;<!-- [et_pb_line_break_holder] -->            let inside = false;<!-- [et_pb_line_break_holder] -->            for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {<!-- [et_pb_line_break_holder] -->                const [xi, yi] = polygon[i];<!-- [et_pb_line_break_holder] -->                const [xj, yj] = polygon[j];<!-- [et_pb_line_break_holder] -->                if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) \/ (yj - yi) + xi) {<!-- [et_pb_line_break_holder] -->                    inside = !inside;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return inside;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        pointInFeature(point, feature) {<!-- [et_pb_line_break_holder] -->            const geometry = feature.geometry;<!-- [et_pb_line_break_holder] -->            if (geometry.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                const coordinates = geometry.coordinates;<!-- [et_pb_line_break_holder] -->                if (!this.pointInPolygon(point, coordinates[0])) return false;<!-- [et_pb_line_break_holder] -->                for (let i = 1; i < coordinates.length; i++) {<!-- [et_pb_line_break_holder] -->                    if (this.pointInPolygon(point, coordinates[i])) return false;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                return true;<!-- [et_pb_line_break_holder] -->            } else if (geometry.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                for (const polygon of geometry.coordinates) {<!-- [et_pb_line_break_holder] -->                    if (this.pointInPolygon(point, polygon[0])) {<!-- [et_pb_line_break_holder] -->                        let inHole = false;<!-- [et_pb_line_break_holder] -->                        for (let i = 1; i < polygon.length; i++) {<!-- [et_pb_line_break_holder] -->                            if (this.pointInPolygon(point, polygon[i])) {<!-- [et_pb_line_break_holder] -->                                inHole = true;<!-- [et_pb_line_break_holder] -->                                break;<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                        if (!inHole) return true;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                return false;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return false;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        generateDotsInPolygon(feature, dotSpacing = 16) {<!-- [et_pb_line_break_holder] -->            \/\/ More robust sampler that handles antimeridian-crossing countries (e.g., Russia)<!-- [et_pb_line_break_holder] -->            const dots = [];<!-- [et_pb_line_break_holder] -->            const stepSize = Math.max(0.5, dotSpacing * 0.08); \/\/ cap max step to keep large countries filled<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const normLng = (lng) => {<!-- [et_pb_line_break_holder] -->                const n = ((lng + 180) % 360 + 360) % 360 - 180;<!-- [et_pb_line_break_holder] -->                return n === -180 ? 180 : n; \/\/ keep boundaries consistent<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const samplePolygon = (rings) => {<!-- [et_pb_line_break_holder] -->                const lons = rings.flat().map(([lng]) => normLng(lng));<!-- [et_pb_line_break_holder] -->                const lats = rings.flat().map(([, lat]) => lat);<!-- [et_pb_line_break_holder] -->                const minLng = Math.min(...lons);<!-- [et_pb_line_break_holder] -->                const maxLng = Math.max(...lons);<!-- [et_pb_line_break_holder] -->                const minLat = Math.min(...lats);<!-- [et_pb_line_break_holder] -->                const maxLat = Math.max(...lats);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const crossesDateline = maxLng - minLng > 180;<!-- [et_pb_line_break_holder] -->                const ranges = crossesDateline ? [[-180, 0], [0, 180]] : [[minLng, maxLng]];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                ranges.forEach(([lngStart, lngEnd]) => {<!-- [et_pb_line_break_holder] -->                    for (let lng = lngStart; lng <= lngEnd; lng += stepSize) {<!-- [et_pb_line_break_holder] -->                        for (let lat = minLat; lat <= maxLat; lat += stepSize) {<!-- [et_pb_line_break_holder] -->                            \/\/ Try with normalized and shifted longitudes so dateline polygons still hit<!-- [et_pb_line_break_holder] -->                            const candidates = [<!-- [et_pb_line_break_holder] -->                                [lng, lat],<!-- [et_pb_line_break_holder] -->                                [lng + 360, lat],<!-- [et_pb_line_break_holder] -->                                [lng - 360, lat]<!-- [et_pb_line_break_holder] -->                            ];<!-- [et_pb_line_break_holder] -->                            if (candidates.some((p) => d3.geoContains(feature, p))) {<!-- [et_pb_line_break_holder] -->                                dots.push([lng, lat]);<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->            if (!geom) return dots;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                samplePolygon(geom.coordinates);<!-- [et_pb_line_break_holder] -->            } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                geom.coordinates.forEach((poly) => samplePolygon(poly));<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            return dots;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        render() {<!-- [et_pb_line_break_holder] -->            this.context.clearRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            this.context.save();<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = this.bgColor || '#ffffff';<!-- [et_pb_line_break_holder] -->            this.context.fillRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            this.context.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.mode === 'globe') {<!-- [et_pb_line_break_holder] -->                this.renderGlobe();<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                this.renderFlat();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        isZoomingFlat() {<!-- [et_pb_line_break_holder] -->            return this.mode === 'flat' && this.zoomPhaseProgress > 0.01;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        angularDistance(lon1, lat1, lon2, lat2) {<!-- [et_pb_line_break_holder] -->            const toRad = (d) => (d * Math.PI) \/ 180;<!-- [et_pb_line_break_holder] -->            const \u03c61 = toRad(lat1);<!-- [et_pb_line_break_holder] -->            const \u03c62 = toRad(lat2);<!-- [et_pb_line_break_holder] -->            const \u0394\u03bb = toRad(lon2 - lon1);<!-- [et_pb_line_break_holder] -->            const sinDLat = Math.sin((\u03c62 - \u03c61) \/ 2);<!-- [et_pb_line_break_holder] -->            const sinDLon = Math.sin(\u0394\u03bb \/ 2);<!-- [et_pb_line_break_holder] -->            const a = sinDLat * sinDLat + Math.cos(\u03c61) * Math.cos(\u03c62) * sinDLon * sinDLon;<!-- [et_pb_line_break_holder] -->            return 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderGlobe() {<!-- [et_pb_line_break_holder] -->            const projection = this.orthoProjection;<!-- [et_pb_line_break_holder] -->            this.path.projection(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentScale = projection.scale();<!-- [et_pb_line_break_holder] -->            const scaleFactor = currentScale \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] -->            const centerX = this.containerWidth \/ 2;<!-- [et_pb_line_break_holder] -->            const centerY = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] -->            const centerCoord = projection.invert([centerX, centerY]);<!-- [et_pb_line_break_holder] -->            const isFront = (lon, lat) => d3.geoDistance([lon, lat], centerCoord) <= Math.PI \/ 2;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            projection.clipAngle(180);<!-- [et_pb_line_break_holder] -->            this.context.save();<!-- [et_pb_line_break_holder] -->            this.context.filter = 'blur(1px)';<!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.globalAlpha = 0.20;<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            this.context.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            projection.clipAngle(90);<!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->            this.context.lineWidth = 2 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->            this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                projection.clipAngle(180);<!-- [et_pb_line_break_holder] -->                const graticuleBack = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticuleBack());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.14 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.30;<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    this.context.beginPath();<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                    this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    this.context.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.18;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                projection.clipAngle(90);<!-- [et_pb_line_break_holder] -->                const graticuleFront = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticuleFront());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.18 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const dotRadius = 0.6 * sizeFactor; \/\/ keep dots small and consistent<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected &&<!-- [et_pb_line_break_holder] -->                        projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                        projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                        if (isFront(dot.lng, dot.lat)) {<!-- [et_pb_line_break_holder] -->                            const dx = projected[0] - centerX;<!-- [et_pb_line_break_holder] -->                            const dy = projected[1] - centerY;<!-- [et_pb_line_break_holder] -->                            const dist = Math.sqrt(dx * dx + dy * dy);<!-- [et_pb_line_break_holder] -->                            if (dist <= currentScale) {<!-- [et_pb_line_break_holder] -->                                const opacity = (90 - Math.abs(dot.lat)) \/ 90;<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = opacity;<!-- [et_pb_line_break_holder] -->                                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                                this.context.arc(projected[0], projected[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                                this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                                this.context.fill();<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.universities.forEach((uni) => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected &&<!-- [et_pb_line_break_holder] -->                        projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                        projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                        this.context.shadowColor = 'rgba(0,106,179,0.6)';<!-- [et_pb_line_break_holder] -->                        this.context.shadowBlur = 3 * scaleFactor;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetX = 2 * scaleFactor;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetY = 2 * scaleFactor;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                        this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetX = 0;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetY = 0;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.logoImage && this.logoImage.complete) {<!-- [et_pb_line_break_holder] -->                this.context.save();<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.context.arc(this.containerWidth \/ 2, this.containerHeight \/ 2, projection.scale(), 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                this.context.clip();<!-- [et_pb_line_break_holder] -->                if (this.isInteracting) {<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = 0.25;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                const logoSize = 2.2 * projection.scale();<!-- [et_pb_line_break_holder] -->                const x = this.containerWidth \/ 2 - logoSize \/ 2;<!-- [et_pb_line_break_holder] -->                const y = this.containerHeight \/ 2 - logoSize \/ 2;<!-- [et_pb_line_break_holder] -->                this.context.drawImage(this.logoImage, x, y, logoSize, logoSize);<!-- [et_pb_line_break_holder] -->                this.context.restore();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderFlat() {<!-- [et_pb_line_break_holder] -->            const projection = this.flatProjection;<!-- [et_pb_line_break_holder] -->            this.path.projection(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentScale = projection.scale();<!-- [et_pb_line_break_holder] -->            const scaleFactor = currentScale \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            \/\/ Only draw border when not in zooming flat tour phase<!-- [et_pb_line_break_holder] -->            if (!this.isZoomingFlat()) {<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                const graticule = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticule());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.8 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.4 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Fills with highlight like morph<!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.35;<!-- [et_pb_line_break_holder] -->                this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    this.context.beginPath();<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    this.context.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const dotRadius = 0.6 * sizeFactor; \/\/ consistent size<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Draw markers (consistent size across animation)<!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                const markerBoxes = [];<!-- [et_pb_line_break_holder] -->                const placements = [];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Draw markers and collect their boxes<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                        \/\/ store small box for overlap checks<!-- [et_pb_line_break_holder] -->                        const r = markerRadius + 2;<!-- [et_pb_line_break_holder] -->                        const box = {<!-- [et_pb_line_break_holder] -->                            x: projected[0] - r,<!-- [et_pb_line_break_holder] -->                            y: projected[1] - r,<!-- [et_pb_line_break_holder] -->                            w: r * 2,<!-- [et_pb_line_break_holder] -->                            h: r * 2<!-- [et_pb_line_break_holder] -->                        };<!-- [et_pb_line_break_holder] -->                        markerBoxes.push(box);<!-- [et_pb_line_break_holder] -->                        placements.push({<!-- [et_pb_line_break_holder] -->                            uni,<!-- [et_pb_line_break_holder] -->                            cx: projected[0],<!-- [et_pb_line_break_holder] -->                            cy: projected[1],<!-- [et_pb_line_break_holder] -->                            markerBox: box<!-- [et_pb_line_break_holder] -->                        });<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ University markers (labels removed per request)<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Country labels for highlighted countries (only during zoom\/tour)<!-- [et_pb_line_break_holder] -->                if (this.showLabels && this.activeContinentIdx != null) {<!-- [et_pb_line_break_holder] -->                    const countryFontSize = 12 * sizeFactor;<!-- [et_pb_line_break_holder] -->                    this.context.font = `600 ${countryFontSize}px \"Inter\", \"Segoe UI\", sans-serif`;<!-- [et_pb_line_break_holder] -->                    this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    this.context.textBaseline = 'middle';<!-- [et_pb_line_break_holder] -->                    this.context.textAlign = 'left';<!-- [et_pb_line_break_holder] -->                    this.context.shadowColor = 'rgba(0,0,0,0.55)';<!-- [et_pb_line_break_holder] -->                    this.context.shadowBlur = 6;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const placedCountry = [...markerBoxes]; \/\/ avoid overlapping markers<!-- [et_pb_line_break_holder] -->                    const intersects = (a, b) => !(<!-- [et_pb_line_break_holder] -->                        a.x + a.w < b.x ||<!-- [et_pb_line_break_holder] -->                        b.x + b.w < a.x ||<!-- [et_pb_line_break_holder] -->                        a.y + a.h < b.y ||<!-- [et_pb_line_break_holder] -->                        b.y + b.h < a.y<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const gaps = [0, 12, 24, 36, 48, 60];<!-- [et_pb_line_break_holder] -->                    const directions = [<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: 0 },   \/\/ right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: 0 },  \/\/ left<!-- [et_pb_line_break_holder] -->                        { dx: 0, dy: -1 },  \/\/ up<!-- [et_pb_line_break_holder] -->                        { dx: 0, dy: 1 },   \/\/ down<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: -1 },  \/\/ up-right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: -1 }, \/\/ up-left<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: 1 },   \/\/ down-right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: 1 },  \/\/ down-left<!-- [et_pb_line_break_holder] -->                    ];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const margin = 8;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const activeContinent = this.continentTours[this.activeContinentIdx];<!-- [et_pb_line_break_holder] -->                    const maxDistRad = Math.PI * 0.35; \/\/ tighten: ~63 degrees cone per continent<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    if (this.highlightedFeatureIndices && this.landFeatures?.features?.length && activeContinent) {<!-- [et_pb_line_break_holder] -->                        this.highlightedFeatureIndices.forEach(idx => {<!-- [et_pb_line_break_holder] -->                            const feature = this.landFeatures.features[idx];<!-- [et_pb_line_break_holder] -->                            if (!feature) return;<!-- [et_pb_line_break_holder] -->                            const name = feature.properties?.name;<!-- [et_pb_line_break_holder] -->                            if (!name) return;<!-- [et_pb_line_break_holder] -->                            const centroid = d3.geoCentroid(feature);<!-- [et_pb_line_break_holder] -->                            const projected = projection(centroid);<!-- [et_pb_line_break_holder] -->                            if (!projected) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            \/\/ Skip if centroid far from active continent center<!-- [et_pb_line_break_holder] -->                            const angDist = this.angularDistance(<!-- [et_pb_line_break_holder] -->                                activeContinent.lng, activeContinent.lat,<!-- [et_pb_line_break_holder] -->                                centroid[0], centroid[1]<!-- [et_pb_line_break_holder] -->                            );<!-- [et_pb_line_break_holder] -->                            if (angDist > maxDistRad) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        \/\/ Skip if the feature\u2019s projected bounds are off-screen<!-- [et_pb_line_break_holder] -->                        const b = d3.geoPath().projection(projection).bounds(feature);<!-- [et_pb_line_break_holder] -->                        const [minX, minY] = b[0];<!-- [et_pb_line_break_holder] -->                        const [maxX, maxY] = b[1];<!-- [et_pb_line_break_holder] -->                        const intersectsViewport = !(<!-- [et_pb_line_break_holder] -->                            maxX < 0 || maxY < 0 ||<!-- [et_pb_line_break_holder] -->                            minX > this.containerWidth || minY > this.containerHeight<!-- [et_pb_line_break_holder] -->                        );<!-- [et_pb_line_break_holder] -->                        if (!intersectsViewport) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            const width = this.context.measureText(name).width;<!-- [et_pb_line_break_holder] -->                            const height = countryFontSize * 1.2;<!-- [et_pb_line_break_holder] -->                            const cx = projected[0];<!-- [et_pb_line_break_holder] -->                            const cy = projected[1];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            let chosen = null;<!-- [et_pb_line_break_holder] -->                            let bestOverlap = Infinity;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            gaps.forEach((gap) => {<!-- [et_pb_line_break_holder] -->                                directions.forEach(({ dx, dy }) => {<!-- [et_pb_line_break_holder] -->                                    const offsetX = dx * gap;<!-- [et_pb_line_break_holder] -->                                    const offsetY = dy * gap;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    let x = cx + (dx >= 0 ? 8 + offsetX : -(width + 8 - offsetX));<!-- [et_pb_line_break_holder] -->                                    let y = cy - height \/ 2 + offsetY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    if (dx === 0) x = cx - width \/ 2 + offsetX;<!-- [et_pb_line_break_holder] -->                                    if (dy === 0) y = cy - height \/ 2 + offsetY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    \/\/ Clamp to stay within canvas<!-- [et_pb_line_break_holder] -->                                    x = Math.max(margin, Math.min(x, this.containerWidth - width - margin));<!-- [et_pb_line_break_holder] -->                                    y = Math.max(margin, Math.min(y, this.containerHeight - height - margin));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    const box = { x, y, w: width, h: height };<!-- [et_pb_line_break_holder] -->                                    const overlap = placedCountry.reduce((acc, b) => acc + (intersects(b, box) ? 1 : 0), 0);<!-- [et_pb_line_break_holder] -->                                    if (overlap < bestOverlap) {<!-- [et_pb_line_break_holder] -->                                        bestOverlap = overlap;<!-- [et_pb_line_break_holder] -->                                        chosen = { x, y, box, dx, dy, gap };<!-- [et_pb_line_break_holder] -->                                        if (overlap === 0) return;<!-- [et_pb_line_break_holder] -->                                    }<!-- [et_pb_line_break_holder] -->                                });<!-- [et_pb_line_break_holder] -->                            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            if (!chosen) return;<!-- [et_pb_line_break_holder] -->                            placedCountry.push(chosen.box);<!-- [et_pb_line_break_holder] -->                            const textY = chosen.y + height \/ 2;<!-- [et_pb_line_break_holder] -->                            this.context.fillText(name, chosen.x, textY);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            \/\/ Leader line if offset<!-- [et_pb_line_break_holder] -->                            const distX = chosen.x - cx;<!-- [et_pb_line_break_holder] -->                            const distY = textY - cy;<!-- [et_pb_line_break_holder] -->                            const leaderDist = Math.sqrt(distX * distX + distY * distY);<!-- [et_pb_line_break_holder] -->                            if (leaderDist > 10) {<!-- [et_pb_line_break_holder] -->                                let targetX = chosen.x;<!-- [et_pb_line_break_holder] -->                                let targetY = textY;<!-- [et_pb_line_break_holder] -->                                if (chosen.dx > 0) targetX = chosen.x - 4;<!-- [et_pb_line_break_holder] -->                                else if (chosen.dx < 0) targetX = chosen.x + chosen.box.w + 4;<!-- [et_pb_line_break_holder] -->                                else targetX = cx;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                if (chosen.dy > 0) targetY = chosen.y - 4;<!-- [et_pb_line_break_holder] -->                                else if (chosen.dy < 0) targetY = chosen.y + chosen.box.h + 4;<!-- [et_pb_line_break_holder] -->                                else targetY = textY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                                this.context.moveTo(cx, cy);<!-- [et_pb_line_break_holder] -->                                this.context.lineTo(targetX, targetY);<!-- [et_pb_line_break_holder] -->                                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                                this.context.lineWidth = 1;<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = 0.85;<!-- [et_pb_line_break_holder] -->                                this.context.stroke();<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        });<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                    this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/**<!-- [et_pb_line_break_holder] -->         * Scroll-driven animation:<!-- [et_pb_line_break_holder] -->         * 0 \u2192 0.15: Globe rotation<!-- [et_pb_line_break_holder] -->         * 0.15 \u2192 0.25: Morph to flat map<!-- [et_pb_line_break_holder] -->         * 0.25 \u2192 0.35: Show full flat map (pause)<!-- [et_pb_line_break_holder] -->         * 0.35 \u2192 0.85: Continent tours (zoom\/pan with pauses)<!-- [et_pb_line_break_holder] -->         * 0.85 \u2192 1.00: Fold back to globe (loop-friendly)<!-- [et_pb_line_break_holder] -->         *\/<!-- [et_pb_line_break_holder] -->        applyScrollProgress(progress) {<!-- [et_pb_line_break_holder] -->            const p = Math.max(0, Math.min(1, progress));<!-- [et_pb_line_break_holder] -->            const rotatePortion = 0.15;<!-- [et_pb_line_break_holder] -->            const morphPortion = 0.10;<!-- [et_pb_line_break_holder] -->            const flatHoldPortion = 0.10;<!-- [et_pb_line_break_holder] -->            const foldBackPortion = 0.15;<!-- [et_pb_line_break_holder] -->            const morphEnd = rotatePortion + morphPortion;<!-- [et_pb_line_break_holder] -->            const flatHoldEnd = morphEnd + flatHoldPortion;<!-- [et_pb_line_break_holder] -->            const foldBackStart = 1 - foldBackPortion;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Stop any auto motion when scroll is in control<!-- [et_pb_line_break_holder] -->            if (this.autoRotate) {<!-- [et_pb_line_break_holder] -->                this.autoRotate = false;<!-- [et_pb_line_break_holder] -->                if (this.rotationTimer) {<!-- [et_pb_line_break_holder] -->                    this.rotationTimer.stop();<!-- [et_pb_line_break_holder] -->                    this.rotationTimer = null;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Default graticule fade state<!-- [et_pb_line_break_holder] -->                this.zoomPhaseProgress = 0;<!-- [et_pb_line_break_holder] -->            this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (p <= rotatePortion) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 1: Globe rotation<!-- [et_pb_line_break_holder] -->                const t = p \/ rotatePortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeOutCubic(t);<!-- [et_pb_line_break_holder] -->                const angle = this.scrollBaseRotation + eased * 360;<!-- [et_pb_line_break_holder] -->                this.rotation[0] = angle;<!-- [et_pb_line_break_holder] -->                this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                this.mode = 'globe';<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Study Abroad. Find your perfect university now!');<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            } else if (p <= morphEnd) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 2: Morph to flat<!-- [et_pb_line_break_holder] -->                const unfoldT = (p - rotatePortion) \/ morphPortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeInOutCubic(unfoldT);<!-- [et_pb_line_break_holder] -->                this.mode = 'morphing';<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Semester abroad, Bachelor \/ Master Abroad, Double Degree, Gap Year, Summer Sessions, and many more!');<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                if (eased >= 0.98) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    this.renderMorph(eased);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            } else if (p <= flatHoldEnd) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 2.5: Show full flat map before zooming<!-- [et_pb_line_break_holder] -->                this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                this.showLabels = false; \/\/ hide country labels on full map<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('72 Universtities in 25 Countries');<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            } else if (p < foldBackStart) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 3: Continent tours<!-- [et_pb_line_break_holder] -->                const tourProgress = (p - flatHoldEnd) \/ (foldBackStart - flatHoldEnd);<!-- [et_pb_line_break_holder] -->                \/\/ Ensure we're in flat mode and projection is reset before starting tours<!-- [et_pb_line_break_holder] -->                if (this.mode !== 'flat' && tourProgress < 0.01) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                    this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                this.zoomPhaseProgress = Math.max(0, Math.min(1, tourProgress));<!-- [et_pb_line_break_holder] -->                const fastFadePortion = 0.15; \/\/ fade out early in the tour phase<!-- [et_pb_line_break_holder] -->                const fadeT = Math.max(0, Math.min(1, tourProgress \/ fastFadePortion));<!-- [et_pb_line_break_holder] -->                this.graticuleFade = Math.max(0, 1 - fadeT);<!-- [et_pb_line_break_holder] -->                this.showLabels = true;<!-- [et_pb_line_break_holder] -->                this.handleContinentTour(tourProgress);<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 4: Fold back to globe<!-- [et_pb_line_break_holder] -->                const foldT = (p - foldBackStart) \/ foldBackPortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeInOutCubic(foldT);<!-- [et_pb_line_break_holder] -->                this.mode = 'morphing';<!-- [et_pb_line_break_holder] -->                \/\/ Fade graticules back in as we fold up<!-- [et_pb_line_break_holder] -->                this.graticuleFade = Math.min(1, eased);<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->                \/\/ Reverse morph: use (1 - eased) to move from flat -> globe<!-- [et_pb_line_break_holder] -->                this.renderMorph(1 - eased);<!-- [et_pb_line_break_holder] -->                if (foldT >= 0.98) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'globe';<!-- [et_pb_line_break_holder] -->                    this.rotation = [...this.initialRotation];<!-- [et_pb_line_break_holder] -->                    this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                    this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->                    this.showLabels = false;<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleContinentTour(progress) {<!-- [et_pb_line_break_holder] -->            \/\/ Each continent gets: transition in (15%), hold (25%), transition out (15%)<!-- [et_pb_line_break_holder] -->            \/\/ Total per continent: 55%, with transitions between them<!-- [et_pb_line_break_holder] -->            const transitionIn = 0.15;<!-- [et_pb_line_break_holder] -->            const holdDuration = 0.25;<!-- [et_pb_line_break_holder] -->            const transitionOut = 0.15;<!-- [et_pb_line_break_holder] -->            const continentDuration = transitionIn + holdDuration + transitionOut;<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            const numContinents = this.continentTours.length;<!-- [et_pb_line_break_holder] -->            const returnToMapDuration = 0.20;<!-- [et_pb_line_break_holder] -->            const totalDuration = numContinents * continentDuration + returnToMapDuration;<!-- [et_pb_line_break_holder] -->            const normalizedProgress = Math.min(1, Math.max(0, progress)) * totalDuration;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Ensure we start from full map when entering tour phase<!-- [et_pb_line_break_holder] -->            if (normalizedProgress < 0.001) {<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            let accumulated = 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            for (let i = 0; i < numContinents; i++) {<!-- [et_pb_line_break_holder] -->                const continentStart = accumulated;<!-- [et_pb_line_break_holder] -->                const transitionInEnd = continentStart + transitionIn;<!-- [et_pb_line_break_holder] -->                const holdEnd = transitionInEnd + holdDuration;<!-- [et_pb_line_break_holder] -->                const transitionOutEnd = holdEnd + transitionOut;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (normalizedProgress >= continentStart && normalizedProgress < transitionInEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Transitioning into this continent<!-- [et_pb_line_break_holder] -->                    const t = (normalizedProgress - continentStart) \/ transitionIn;<!-- [et_pb_line_break_holder] -->                    const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                    \/\/ If this is the first continent and we're just starting, ensure we begin from full map<!-- [et_pb_line_break_holder] -->                    if (i === 0 && normalizedProgress < continentStart + 0.01) {<!-- [et_pb_line_break_holder] -->                        this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    this.animateToContinent(i, easedT);<!-- [et_pb_line_break_holder] -->                    const c = this.continentTours[i];<!-- [et_pb_line_break_holder] -->                    this.updatePhaseText(`Explore ${c.name}`);<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                } else if (normalizedProgress >= transitionInEnd && normalizedProgress < holdEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Holding at this continent<!-- [et_pb_line_break_holder] -->                    this.animateToContinent(i, 1);<!-- [et_pb_line_break_holder] -->                    const c = this.continentTours[i];<!-- [et_pb_line_break_holder] -->                    this.updatePhaseText(`Explore  ${c.name}`);<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                } else if (normalizedProgress >= holdEnd && normalizedProgress < transitionOutEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Transitioning out of this continent<!-- [et_pb_line_break_holder] -->                    const t = (normalizedProgress - holdEnd) \/ transitionOut;<!-- [et_pb_line_break_holder] -->                    const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                    const nextIndex = i < numContinents - 1 ? i + 1 : -1;<!-- [et_pb_line_break_holder] -->                    if (nextIndex >= 0) {<!-- [et_pb_line_break_holder] -->                        \/\/ Transition to next continent<!-- [et_pb_line_break_holder] -->                        this.transitionBetweenContinents(i, nextIndex, easedT);<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        \/\/ Transition back to full map<!-- [et_pb_line_break_holder] -->                        this.transitionToFullMap(easedT);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                accumulated = transitionOutEnd;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Final: return to full map<!-- [et_pb_line_break_holder] -->            const returnStart = accumulated;<!-- [et_pb_line_break_holder] -->            if (normalizedProgress >= returnStart) {<!-- [et_pb_line_break_holder] -->                const t = Math.min(1, (normalizedProgress - returnStart) \/ returnToMapDuration);<!-- [et_pb_line_break_holder] -->                const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                this.transitionToFullMap(easedT);<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Apply now to start your study abroad journey!\\n\\nworldofstudents.org');<!-- [et_pb_line_break_holder] -->            } else if (normalizedProgress < 0.001) {<!-- [et_pb_line_break_holder] -->                \/\/ At the very start, show full map<!-- [et_pb_line_break_holder] -->                this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        animateToContinent(index, t) {<!-- [et_pb_line_break_holder] -->            const continent = this.continentTours[index];<!-- [et_pb_line_break_holder] -->            const targetScale = this.baseFlatScale * continent.zoom;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const targetCenter = [continent.lng, continent.lat];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Interpolate from current state<!-- [et_pb_line_break_holder] -->            const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->            const currentCenter = this.flatProjection.center();<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            const scale = currentScale + (targetScale - currentScale) * t;<!-- [et_pb_line_break_holder] -->            const centerLng = currentCenter[0] + (targetCenter[0] - currentCenter[0]) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = currentCenter[1] + (targetCenter[1] - currentCenter[1]) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.currentZoom = scale \/ this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = index;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        transitionBetweenContinents(fromIndex, toIndex, t) {<!-- [et_pb_line_break_holder] -->            const from = this.continentTours[fromIndex];<!-- [et_pb_line_break_holder] -->            const to = this.continentTours[toIndex];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const fromScale = this.baseFlatScale * from.zoom;<!-- [et_pb_line_break_holder] -->            const toScale = this.baseFlatScale * to.zoom;<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            \/\/ Interpolate scale<!-- [et_pb_line_break_holder] -->            const scale = fromScale + (toScale - fromScale) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const centerLng = from.lng + (to.lng - from.lng) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = from.lat + (to.lat - from.lat) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.currentZoom = scale \/ this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = toIndex;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        transitionToFullMap(t) {<!-- [et_pb_line_break_holder] -->            const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->            const targetScale = this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            const scale = currentScale + (targetScale - currentScale) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentCenter = this.flatProjection.center();<!-- [et_pb_line_break_holder] -->            const targetCenter = [0, 0];<!-- [et_pb_line_break_holder] -->            const centerLng = currentCenter[0] + (targetCenter[0] - currentCenter[0]) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = currentCenter[1] + (targetCenter[1] - currentCenter[1]) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        resetFlatProjection() {<!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(this.baseFlatScale)<!-- [et_pb_line_break_holder] -->                .center([0, 0])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] -->            this.currentZoom = 1;<!-- [et_pb_line_break_holder] -->            this.showLabels = false;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        startAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = true;<!-- [et_pb_line_break_holder] -->            this.autoPlayStart = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const step = (ts) => {<!-- [et_pb_line_break_holder] -->                if (!this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->                if (this.autoPlayStart === null) this.autoPlayStart = ts;<!-- [et_pb_line_break_holder] -->                const elapsed = ts - this.autoPlayStart;<!-- [et_pb_line_break_holder] -->                const t = Math.min(1, elapsed \/ this.autoPlayDuration);<!-- [et_pb_line_break_holder] -->                this.applyScrollProgress(t);<!-- [et_pb_line_break_holder] -->                if (t >= 1) {<!-- [et_pb_line_break_holder] -->                    this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->                    this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                this.autoPlayHandle = requestAnimationFrame(step);<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->            this.autoPlayHandle = requestAnimationFrame(step);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        stopAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (!this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->            if (this.autoPlayHandle) {<!-- [et_pb_line_break_holder] -->                cancelAnimationFrame(this.autoPlayHandle);<!-- [et_pb_line_break_holder] -->                this.autoPlayHandle = null;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        toggleAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (this.isAutoPlaying) this.stopAutoPlay();<!-- [et_pb_line_break_holder] -->            else this.startAutoPlay();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        updateAutoPlayButton() {<!-- [et_pb_line_break_holder] -->            if (!this.autoplayBtn) return;<!-- [et_pb_line_break_holder] -->            this.autoplayBtn.textContent = this.isAutoPlaying ? 'Stop' : 'Autoplay';<!-- [et_pb_line_break_holder] -->            this.autoplayBtn.style.opacity = this.isAutoPlaying ? '0.95' : '1';<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        updatePhaseText(text) {<!-- [et_pb_line_break_holder] -->            if (!this.phaseText) return;<!-- [et_pb_line_break_holder] -->            if (this._currentPhaseText === text) return;<!-- [et_pb_line_break_holder] -->            this._currentPhaseText = text;<!-- [et_pb_line_break_holder] -->            this.phaseText.textContent = text || '';<!-- [et_pb_line_break_holder] -->            if (text) {<!-- [et_pb_line_break_holder] -->                this.phaseText.classList.add('visible');<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                this.phaseText.classList.remove('visible');<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupScrollTrigger(targetEl, options = {}) {<!-- [et_pb_line_break_holder] -->            const el = targetEl || this.canvas;<!-- [et_pb_line_break_holder] -->            this.scrollBaseRotation = this.rotation[0];<!-- [et_pb_line_break_holder] -->            const startOffsetOpt = options.startOffset;<!-- [et_pb_line_break_holder] -->            const startAtSticky = options.startAtSticky !== false; \/\/ default true<!-- [et_pb_line_break_holder] -->            const endOffset = options.endOffset ?? 0.8; \/\/ finish slightly before sticky ends<!-- [et_pb_line_break_holder] -->            const clamp01 = (v) => Math.max(0, Math.min(1, v));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const onScroll = () => {<!-- [et_pb_line_break_holder] -->                const viewport = window.innerHeight || 1;<!-- [et_pb_line_break_holder] -->                const rect = el.getBoundingClientRect();<!-- [et_pb_line_break_holder] -->                const wrapperHeight = el.offsetHeight || 1;<!-- [et_pb_line_break_holder] -->                const wrapperTopAbs = rect.top + (window.scrollY || window.pageYOffset || 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ How far we've scrolled since the wrapper reached the top of the viewport<!-- [et_pb_line_break_holder] -->                const scrolledInside = (window.scrollY || window.pageYOffset || 0) - wrapperTopAbs;<!-- [et_pb_line_break_holder] -->                const stickyRange = Math.max(1, wrapperHeight - viewport); \/\/ duration of stickiness<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ raw progress: 0 when top first sticks, 1 when sticky duration ends<!-- [et_pb_line_break_holder] -->                const raw = clamp01(scrolledInside \/ stickyRange);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ startOffset: either provided or aligned to stick start<!-- [et_pb_line_break_holder] -->                const startOffset = startOffsetOpt != null ? startOffsetOpt : (startAtSticky ? 0 : 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Map raw progress into [startOffset, endOffset] range<!-- [et_pb_line_break_holder] -->                const normalized = clamp01((raw - startOffset) \/ Math.max(0.0001, endOffset - startOffset));<!-- [et_pb_line_break_holder] -->                this.applyScrollProgress(normalized);<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            ['scroll', 'resize'].forEach(evt =><!-- [et_pb_line_break_holder] -->                window.addEventListener(evt, onScroll, { passive: true })<!-- [et_pb_line_break_holder] -->            );<!-- [et_pb_line_break_holder] -->            onScroll();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ ---- Morph helpers ----<!-- [et_pb_line_break_holder] -->        easeInOutCubic(t) {<!-- [et_pb_line_break_holder] -->            return t < 0.5<!-- [et_pb_line_break_holder] -->                ? 4 * t * t * t<!-- [et_pb_line_break_holder] -->                : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ Faster start, slower end<!-- [et_pb_line_break_holder] -->        easeOutCubic(t) {<!-- [et_pb_line_break_holder] -->            const clamped = Math.min(1, Math.max(0, t));<!-- [et_pb_line_break_holder] -->            return 1 - Math.pow(1 - clamped, 3);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        createForwardInterpolatedProjection(t) {<!-- [et_pb_line_break_holder] -->            \/\/ Use ease-out so the unfold decelerates into the final flat view<!-- [et_pb_line_break_holder] -->            const tt = this.easeOutCubic(t);<!-- [et_pb_line_break_holder] -->            const startProj = this.orthoProjection;<!-- [et_pb_line_break_holder] -->            const endProj = this.flatProjection;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            return (coords) => {<!-- [et_pb_line_break_holder] -->                const p0 = startProj(coords);<!-- [et_pb_line_break_holder] -->                const p1 = endProj(coords);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (!p0 && !p1) return null;<!-- [et_pb_line_break_holder] -->                const x0 = p0 ? p0[0] : p1[0];<!-- [et_pb_line_break_holder] -->                const y0 = p0 ? p0[1] : p1[1];<!-- [et_pb_line_break_holder] -->                const x1 = p1 ? p1[0] : p0[0];<!-- [et_pb_line_break_holder] -->                const y1 = p1 ? p1[1] : p0[1];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                return [<!-- [et_pb_line_break_holder] -->                    x0 + (x1 - x0) * tt,<!-- [et_pb_line_break_holder] -->                    y0 + (y1 - y0) * tt<!-- [et_pb_line_break_holder] -->                ];<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        buildSphereBoundaryCoords() {<!-- [et_pb_line_break_holder] -->            const coords = [];<!-- [et_pb_line_break_holder] -->            const step = 5;<!-- [et_pb_line_break_holder] -->            for (let lon = -180; lon <= 180; lon += step) {<!-- [et_pb_line_break_holder] -->                coords.push([lon, -89]);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            for (let lon = 180; lon >= -180; lon -= step) {<!-- [et_pb_line_break_holder] -->                coords.push([lon, 89]);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return coords;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ \u2705 NEW: break path when projected x-jump is large (no wrap lines)<!-- [et_pb_line_break_holder] -->        drawLineString(ctx, coords, proj) {<!-- [et_pb_line_break_holder] -->            let prevP = null;<!-- [et_pb_line_break_holder] -->            let prevC = null;<!-- [et_pb_line_break_holder] -->            const thresholdX = this.containerWidth \/ 3; \/\/ stricter to catch dateline jumps<!-- [et_pb_line_break_holder] -->            const thresholdY = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            coords.forEach((c) => {<!-- [et_pb_line_break_holder] -->                const p = proj(c);<!-- [et_pb_line_break_holder] -->                if (!p) return;<!-- [et_pb_line_break_holder] -->                const shouldBreak =<!-- [et_pb_line_break_holder] -->                    !!prevP &&<!-- [et_pb_line_break_holder] -->                    (<!-- [et_pb_line_break_holder] -->                        Math.abs(p[0] - prevP[0]) > thresholdX ||<!-- [et_pb_line_break_holder] -->                        Math.abs(p[1] - prevP[1]) > thresholdY ||<!-- [et_pb_line_break_holder] -->                        (prevC && Math.abs(c[0] - prevC[0]) > 170) \/\/ large lon jump \u2192 new subpath<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (!prevP || shouldBreak) {<!-- [et_pb_line_break_holder] -->                    ctx.moveTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    ctx.lineTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                prevP = p;<!-- [et_pb_line_break_holder] -->                prevC = c;<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        drawPolygon(ctx, coords, proj) {<!-- [et_pb_line_break_holder] -->            const thresholdX = this.containerWidth \/ 3;<!-- [et_pb_line_break_holder] -->            coords.forEach((ring) => {<!-- [et_pb_line_break_holder] -->                let prevP = null;<!-- [et_pb_line_break_holder] -->                let prevC = null;<!-- [et_pb_line_break_holder] -->                let firstP = null;<!-- [et_pb_line_break_holder] -->                let firstC = null;<!-- [et_pb_line_break_holder] -->                ring.forEach((c) => {<!-- [et_pb_line_break_holder] -->                    const p = proj(c);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    const shouldBreak =<!-- [et_pb_line_break_holder] -->                        !!prevP &&<!-- [et_pb_line_break_holder] -->                        (<!-- [et_pb_line_break_holder] -->                            Math.abs(p[0] - prevP[0]) > thresholdX ||<!-- [et_pb_line_break_holder] -->                            (prevC && Math.abs(c[0] - prevC[0]) > 170)<!-- [et_pb_line_break_holder] -->                        );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    if (!prevP || shouldBreak) {<!-- [et_pb_line_break_holder] -->                        ctx.moveTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                        firstP = firstP || p;<!-- [et_pb_line_break_holder] -->                        firstC = firstC || c;<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        ctx.lineTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    prevP = p;<!-- [et_pb_line_break_holder] -->                    prevC = c;<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                \/\/ Close ring only when it would not span the dateline<!-- [et_pb_line_break_holder] -->                if (firstP && prevP) {<!-- [et_pb_line_break_holder] -->                    const dxClose = Math.abs(firstP[0] - prevP[0]);<!-- [et_pb_line_break_holder] -->                    const lonJump = firstC && prevC ? Math.abs(firstC[0] - prevC[0]) : 0;<!-- [et_pb_line_break_holder] -->                    if (dxClose <= thresholdX &#038;&#038; lonJump <= 170) {<!-- [et_pb_line_break_holder] -->                        ctx.lineTo(firstP[0], firstP[1]);<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        ctx.moveTo(firstP[0], firstP[1]);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderMorph(t) {<!-- [et_pb_line_break_holder] -->            const proj = this.createForwardInterpolatedProjection(t);<!-- [et_pb_line_break_holder] -->            const ctx = this.context;<!-- [et_pb_line_break_holder] -->            const morphScale = this.flatProjection.scale() \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] -->            const dotRadius = 0.5 * morphScale * sizeFactor;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            ctx.clearRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            ctx.save();<!-- [et_pb_line_break_holder] -->            ctx.fillStyle = this.bgColor || '#ffffff';<!-- [et_pb_line_break_holder] -->            ctx.fillRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            ctx.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Ocean plate<!-- [et_pb_line_break_holder] -->            ctx.beginPath();<!-- [et_pb_line_break_holder] -->            this.drawLineString(ctx, this.sphereBoundary, proj);<!-- [et_pb_line_break_holder] -->            ctx.closePath();<!-- [et_pb_line_break_holder] -->            ctx.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            ctx.fill();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                \/\/ Graticule<!-- [et_pb_line_break_holder] -->                if (this.graticuleGeo && this.graticuleGeo.type === 'MultiLineString') {<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    this.graticuleGeo.coordinates.forEach(line => {<!-- [et_pb_line_break_holder] -->                        this.drawLineString(ctx, line, proj);<!-- [et_pb_line_break_holder] -->                    });<!-- [et_pb_line_break_holder] -->                    ctx.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = 0.4 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                    ctx.lineWidth = 0.8 * sizeFactor;<!-- [et_pb_line_break_holder] -->                    ctx.stroke();<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = 1;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Fills<!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.35;<!-- [et_pb_line_break_holder] -->                ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->                    if (!geom) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                        this.drawPolygon(ctx, geom.coordinates, proj);<!-- [et_pb_line_break_holder] -->                    } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                        geom.coordinates.forEach(poly => this.drawPolygon(ctx, poly, proj));<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                ctx.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Borders (draw after fills so every country gets stroked)<!-- [et_pb_line_break_holder] -->                ctx.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->                    if (!geom) return;<!-- [et_pb_line_break_holder] -->                    if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                        this.drawPolygon(ctx, geom.coordinates, proj);<!-- [et_pb_line_break_holder] -->                    } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                        geom.coordinates.forEach(poly => this.drawPolygon(ctx, poly, proj));<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                ctx.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                ctx.lineWidth = 0.7 * sizeFactor;<!-- [et_pb_line_break_holder] -->                ctx.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Dots<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const p = proj([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    ctx.arc(p[0], p[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                    ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ University markers (consistent size through morph)<!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const p = proj([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    ctx.arc(p[0], p[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                    ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        async loadWorldData() {<!-- [et_pb_line_break_holder] -->            try {<!-- [et_pb_line_break_holder] -->                this.isLoading = true;<!-- [et_pb_line_break_holder] -->                \/\/ Lightweight country TopoJSON (much smaller than full GeoJSON)<!-- [et_pb_line_break_holder] -->                let response = await fetch(<!-- [et_pb_line_break_holder] -->                    'https:\/\/cdn.jsdelivr.net\/npm\/world-atlas@2\/countries-110m.json'<!-- [et_pb_line_break_holder] -->                );<!-- [et_pb_line_break_holder] -->                if (!response.ok) {<!-- [et_pb_line_break_holder] -->                    \/\/ fallback to lightweight land polygons if countries fail<!-- [et_pb_line_break_holder] -->                    response = await fetch(<!-- [et_pb_line_break_holder] -->                        'https:\/\/raw.githubusercontent.com\/martynafford\/natural-earth-geojson\/master\/110m\/physical\/ne_110m_land.json'<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] -->                    if (!response.ok) throw new Error('Failed to load country\/land data');<!-- [et_pb_line_break_holder] -->                    this.landFeatures = await response.json();<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    const topo = await response.json();<!-- [et_pb_line_break_holder] -->                    this.landFeatures = topojson.feature(topo, topo.objects.countries);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                const [[minLng, minLat], [maxLng, maxLat]] = d3.geoBounds(feature);<!-- [et_pb_line_break_holder] -->                const lonSpan = maxLng - minLng;<!-- [et_pb_line_break_holder] -->                \/\/ Reduced density overall; still a bit denser for very wide countries<!-- [et_pb_line_break_holder] -->                const spacing = lonSpan > 120 ? 16 : 22;<!-- [et_pb_line_break_holder] -->                const dots = this.generateDotsInPolygon(feature, spacing);<!-- [et_pb_line_break_holder] -->                dots.forEach(([lng, lat]) => {<!-- [et_pb_line_break_holder] -->                    this.allDots.push({ lng, lat, visible: true });<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                await this.loadUniversitiesData();<!-- [et_pb_line_break_holder] -->                this.computeHighlightedFeatures();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->                this.isLoading = false;<!-- [et_pb_line_break_holder] -->                this.loadingOverlay.style.display = 'none';<!-- [et_pb_line_break_holder] -->                if (this.autoRotate) {<!-- [et_pb_line_break_holder] -->                    this.startRotation();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            } catch (err) {<!-- [et_pb_line_break_holder] -->                this.showError('Failed to load land map data');<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        async loadUniversitiesData() {<!-- [et_pb_line_break_holder] -->            try {<!-- [et_pb_line_break_holder] -->                const res = await fetch('https:\/\/worldofstudents.org\/universities.json', {<!-- [et_pb_line_break_holder] -->                    cache: 'no-cache'<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                if (!res.ok) throw new Error('Failed to fetch universities.json');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const data = await res.json();<!-- [et_pb_line_break_holder] -->                const items = data?.mainEntity?.itemListElement || [];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const parsed = items<!-- [et_pb_line_break_holder] -->                    .map(entry => entry?.item)<!-- [et_pb_line_break_holder] -->                    .map(item => {<!-- [et_pb_line_break_holder] -->                        const lat = parseFloat(item?.geo?.latitude);<!-- [et_pb_line_break_holder] -->                        const lng = parseFloat(item?.geo?.longitude);<!-- [et_pb_line_break_holder] -->                        if (!item?.name || !Number.isFinite(lat) || !Number.isFinite(lng)) {<!-- [et_pb_line_break_holder] -->                            return null;<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                        return {<!-- [et_pb_line_break_holder] -->                            name: item.name,<!-- [et_pb_line_break_holder] -->                            lat,<!-- [et_pb_line_break_holder] -->                            lng,<!-- [et_pb_line_break_holder] -->                            country: item?.address?.countryName || item?.address?.addressCountry || ''<!-- [et_pb_line_break_holder] -->                        };<!-- [et_pb_line_break_holder] -->                    })<!-- [et_pb_line_break_holder] -->                    .filter(Boolean);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.universities = parsed.length<!-- [et_pb_line_break_holder] -->                    ? parsed<!-- [et_pb_line_break_holder] -->                    : universityData.map(item => ({<!-- [et_pb_line_break_holder] -->                        name: item.name,<!-- [et_pb_line_break_holder] -->                        lat: parseFloat(item.lat),<!-- [et_pb_line_break_holder] -->                        lng: parseFloat(item.lng),<!-- [et_pb_line_break_holder] -->                        country: item.country<!-- [et_pb_line_break_holder] -->                    }));<!-- [et_pb_line_break_holder] -->            } catch (err) {<!-- [et_pb_line_break_holder] -->                console.error('Failed to load universities.json, using fallback data', err);<!-- [et_pb_line_break_holder] -->                this.universities = universityData.map(item => ({<!-- [et_pb_line_break_holder] -->                    name: item.name,<!-- [et_pb_line_break_holder] -->                    lat: parseFloat(item.lat),<!-- [et_pb_line_break_holder] -->                    lng: parseFloat(item.lng),<!-- [et_pb_line_break_holder] -->                    country: item.country<!-- [et_pb_line_break_holder] -->                }));<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        computeHighlightedFeatures() {<!-- [et_pb_line_break_holder] -->            this.highlightedFeatureIndices = new Set();<!-- [et_pb_line_break_holder] -->            if (!this.landFeatures?.features?.length || !this.universities?.length) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                const bounds = d3.geoBounds(feature);<!-- [et_pb_line_break_holder] -->                const [[minLng, minLat], [maxLng, maxLat]] = bounds;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const hasUni = this.universities.some(uni => {<!-- [et_pb_line_break_holder] -->                    \/\/ quick bbox reject<!-- [et_pb_line_break_holder] -->                    if (uni.lng < minLng || uni.lng > maxLng || uni.lat < minLat || uni.lat > maxLat) {<!-- [et_pb_line_break_holder] -->                        return false;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    return this.pointInFeature([uni.lng, uni.lat], feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (hasUni) this.highlightedFeatureIndices.add(idx);<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        loadLogo() {<!-- [et_pb_line_break_holder] -->            this.logoImage = new Image();<!-- [et_pb_line_break_holder] -->            this.logoImage.crossOrigin = 'anonymous';<!-- [et_pb_line_break_holder] -->            this.logoImage.src = 'https:\/\/worldofstudents.org\/wp-content\/uploads\/2025\/09\/WOS_Logo_white.png';<!-- [et_pb_line_break_holder] -->            this.logoImage.onload = () => {<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        showError(message) {<!-- [et_pb_line_break_holder] -->            this.error = message;<!-- [et_pb_line_break_holder] -->            this.isLoading = false;<!-- [et_pb_line_break_holder] -->            this.loadingOverlay.innerHTML = `<!-- [et_pb_line_break_holder] -->                <\/p>\n<div class=\"error-state\"><!-- [et_pb_line_break_holder] -->                    <\/p>\n<div class=\"error-title\">Error loading Earth visualization<\/div>\n<p><!-- [et_pb_line_break_holder] -->                    <\/p>\n<div class=\"error-message\">${message}<\/div>\n<p><!-- [et_pb_line_break_holder] -->                <\/div>\n<p><!-- [et_pb_line_break_holder] -->            `;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        startRotation() {<!-- [et_pb_line_break_holder] -->            this._lastElapsed = 0;<!-- [et_pb_line_break_holder] -->            this._lastFrame = 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const rotate = (elapsed) => {<!-- [et_pb_line_break_holder] -->                if (!this.autoRotate || this.mode !== 'globe') return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const deltaMs = elapsed - this._lastElapsed;<!-- [et_pb_line_break_holder] -->                this._lastElapsed = elapsed;<!-- [et_pb_line_break_holder] -->                const deltaSec = Math.max(0, deltaMs) \/ 1000;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const degPerSec = this.rotationSpeed * 60;<!-- [et_pb_line_break_holder] -->                this.rotation[0] += degPerSec * deltaSec;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const FRAME_MS = this.isInteracting ? 16 : 33;<!-- [et_pb_line_break_holder] -->                if (elapsed - this._lastFrame >= FRAME_MS) {<!-- [et_pb_line_break_holder] -->                    this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                    this._lastFrame = elapsed;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.rotationTimer) this.rotationTimer.stop();<!-- [et_pb_line_break_holder] -->            this.rotationTimer = d3.timer(rotate);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupInteractions() {<!-- [et_pb_line_break_holder] -->            \/\/ Disable drag\/zoom; keep hover for tooltip<!-- [et_pb_line_break_holder] -->            this.canvas.addEventListener('mousemove', (event) => this.handleMouseMove(event));<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleMouseMove(event) {<!-- [et_pb_line_break_holder] -->            const rect = this.canvas.getBoundingClientRect();<!-- [et_pb_line_break_holder] -->            const x = event.clientX - rect.left;<!-- [et_pb_line_break_holder] -->            const y = event.clientY - rect.top;<!-- [et_pb_line_break_holder] -->            let hoveredUni = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const projection = (this.mode === 'flat' || this.mode === 'morphing') ? this.flatProjection : this.orthoProjection;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                if (projected &&<!-- [et_pb_line_break_holder] -->                    projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                    projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                    const dx = projected[0] - x;<!-- [et_pb_line_break_holder] -->                    const dy = projected[1] - y;<!-- [et_pb_line_break_holder] -->                    const distance = Math.sqrt(dx * dx + dy * dy);<!-- [et_pb_line_break_holder] -->                    if (distance < 15) {<!-- [et_pb_line_break_holder] -->                        hoveredUni = uni;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (hoveredUni && this.tooltip) {<!-- [et_pb_line_break_holder] -->                this.tooltip.style.display = 'block';<!-- [et_pb_line_break_holder] -->                this.tooltip.style.left = `${x + 10}px`;<!-- [et_pb_line_break_holder] -->                this.tooltip.style.top = `${y + 10}px`;<!-- [et_pb_line_break_holder] -->                this.tooltip.innerHTML = `<strong>${hoveredUni.name}<\/strong><!\u2013- [et_pb_br_holder] -\u2013>${hoveredUni.country}`;<!-- [et_pb_line_break_holder] -->            } else if (this.tooltip) {<!-- [et_pb_line_break_holder] -->                this.tooltip.style.display = 'none';<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    document.addEventListener('DOMContentLoaded', () => {<!-- [et_pb_line_break_holder] -->        const earth = new RotatingEarth('earth-canvas', {<!-- [et_pb_line_break_holder] -->            width: 1080,<!-- [et_pb_line_break_holder] -->            height: 540,<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->        earth.autoplayBtn = document.getElementById('autoplay-btn');<!-- [et_pb_line_break_holder] -->        if (earth.autoplayBtn) {<!-- [et_pb_line_break_holder] -->            earth.autoplayBtn.addEventListener('click', () => earth.toggleAutoPlay());<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ Drive the animation by scroll progress; use the wrapper for full scroll length<!-- [et_pb_line_break_holder] -->        \/\/ startOffset delays the start; endOffset ends a bit before the wrapper ends<!-- [et_pb_line_break_holder] -->        earth.autoRotate = false;<!-- [et_pb_line_break_holder] -->        earth.setupScrollTrigger(document.getElementById('earth-sticky-wrapper'), {<!-- [et_pb_line_break_holder] -->            \/\/ startAtSticky uses the point where the wrapper first pins as the start<!-- [et_pb_line_break_holder] -->            startAtSticky: true,<!-- [et_pb_line_break_holder] -->            endOffset: 0.8<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->    });<!-- [et_pb_line_break_holder] --><\/script><!-- [et_pb_line_break_holder] -->[\/et_pb_fullwidth_code][et_pb_fullwidth_code disabled_on=&#8221;on|on|on&#8221; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; disabled=&#8221;on&#8221; global_colors_info=&#8221;{}&#8221;]<script src=\"https:\/\/d3js.org\/d3.v7.min.js\"><\/script><!-- [et_pb_line_break_holder] --><script src=\"https:\/\/cdn.jsdelivr.net\/npm\/topojson-client@3\/dist\/topojson-client.min.js\"><\/script><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --><\/p>\n<style><!-- [et_pb_line_break_holder] -->  html, body { margin: 0; padding: 0; height: 100%; }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->  .earth-sticky-wrapper {<!-- [et_pb_line_break_holder] -->    position: relative;<!-- [et_pb_line_break_holder] -->    height: 600vh;<!-- [et_pb_line_break_holder] -->    max-width: 1080px;<!-- [et_pb_line_break_holder] -->    margin: 0 auto;<!-- [et_pb_line_break_holder] -->  }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->  .earth-container {<!-- [et_pb_line_break_holder] -->    position: sticky;<!-- [et_pb_line_break_holder] -->    top: 150px;<!-- [et_pb_line_break_holder] -->    width: 100%;<!-- [et_pb_line_break_holder] -->    max-width: 1080px;<!-- [et_pb_line_break_holder] -->    height: 75vh;<!-- [et_pb_line_break_holder] -->    margin: 0 auto;<!-- [et_pb_line_break_holder] -->    background: transparent;<!-- [et_pb_line_break_holder] -->    border-radius: 0;<!-- [et_pb_line_break_holder] -->    overflow: visible;<!-- [et_pb_line_break_holder] -->  }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->  #earth-canvas {<!-- [et_pb_line_break_holder] -->    width: 100%;<!-- [et_pb_line_break_holder] -->    height: 100%;<!-- [et_pb_line_break_holder] -->    display: block;<!-- [et_pb_line_break_holder] -->    background: transparent;<!-- [et_pb_line_break_holder] -->    position: absolute;<!-- [et_pb_line_break_holder] -->    top: 0;<!-- [et_pb_line_break_holder] -->    left: 0;<!-- [et_pb_line_break_holder] -->  }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->  .loading-overlay {<!-- [et_pb_line_break_holder] -->    position: absolute; inset: 0;<!-- [et_pb_line_break_holder] -->    background: rgba(0,0,0,0.8);<!-- [et_pb_line_break_holder] -->    display: flex; align-items: center; justify-content: center;<!-- [et_pb_line_break_holder] -->    border-radius: 16px;<!-- [et_pb_line_break_holder] -->  }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->  .loading-text { color: #fff; font-size: 16px; font-weight: 500; }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->  .error-state { padding: 32px; text-align: center; color: #ef4444; }<!-- [et_pb_line_break_holder] -->  .error-title { font-weight: 600; margin-bottom: 8px; }<!-- [et_pb_line_break_holder] -->  .error-message { color: #999; font-size: 14px; }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->  .spinner {<!-- [et_pb_line_break_holder] -->    width: 24px; height: 24px;<!-- [et_pb_line_break_holder] -->    border: 2px solid #333;<!-- [et_pb_line_break_holder] -->    border-top: 2px solid #fff;<!-- [et_pb_line_break_holder] -->    border-radius: 50%;<!-- [et_pb_line_break_holder] -->    animation: spin 1s linear infinite;<!-- [et_pb_line_break_holder] -->    margin-right: 12px;<!-- [et_pb_line_break_holder] -->  }<!-- [et_pb_line_break_holder] -->  @keyframes spin { 0% { transform: rotate(0deg);} 100%{transform: rotate(360deg);} }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->  \/* Text reveal: snappy + cinematic *\/<!-- [et_pb_line_break_holder] -->  .phase-text {<!-- [et_pb_line_break_holder] -->    position: absolute;<!-- [et_pb_line_break_holder] -->    left: 50%;<!-- [et_pb_line_break_holder] -->    bottom: 36%;<!-- [et_pb_line_break_holder] -->    transform: translate(-50%, 10px);<!-- [et_pb_line_break_holder] -->    color: #fff;<!-- [et_pb_line_break_holder] -->    font-size: 18px;<!-- [et_pb_line_break_holder] -->    font-weight: 800;<!-- [et_pb_line_break_holder] -->    letter-spacing: 0.35px;<!-- [et_pb_line_break_holder] -->    text-shadow: 0 2px 10px rgba(0,0,0,0.55);<!-- [et_pb_line_break_holder] -->    opacity: 0;<!-- [et_pb_line_break_holder] -->    pointer-events: none;<!-- [et_pb_line_break_holder] -->    max-width: 84%;<!-- [et_pb_line_break_holder] -->    text-align: center;<!-- [et_pb_line_break_holder] -->    line-height: 1.45;<!-- [et_pb_line_break_holder] -->    white-space: pre-line;<!-- [et_pb_line_break_holder] -->    filter: blur(2px);<!-- [et_pb_line_break_holder] -->    will-change: transform, opacity, filter, clip-path;<!-- [et_pb_line_break_holder] -->    transition: opacity 0.18s ease-out, transform 0.18s ease-out, filter 0.18s ease-out;<!-- [et_pb_line_break_holder] -->  }<!-- [et_pb_line_break_holder] -->  .phase-text.visible {<!-- [et_pb_line_break_holder] -->    opacity: 1;<!-- [et_pb_line_break_holder] -->    transform: translate(-50%, 0);<!-- [et_pb_line_break_holder] -->    filter: blur(0px);<!-- [et_pb_line_break_holder] -->  }<!-- [et_pb_line_break_holder] -->  .phase-text.reveal {<!-- [et_pb_line_break_holder] -->    animation: phaseReveal 520ms cubic-bezier(.2,.9,.2,1) both;<!-- [et_pb_line_break_holder] -->  }<!-- [et_pb_line_break_holder] -->  @keyframes phaseReveal {<!-- [et_pb_line_break_holder] -->    0%   { opacity: 0; transform: translate(-50%, 10px) scale(0.985); filter: blur(6px); clip-path: inset(0 0 95% 0); }<!-- [et_pb_line_break_holder] -->    45%  { opacity: 1; transform: translate(-50%, 0) scale(1.00);     filter: blur(0px);  clip-path: inset(0 0 10% 0); }<!-- [et_pb_line_break_holder] -->    100% { opacity: 1; transform: translate(-50%, 0) scale(1.00);     filter: blur(0px);  clip-path: inset(0 0 0% 0); }<!-- [et_pb_line_break_holder] -->  }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->  .autoplay-btn {<!-- [et_pb_line_break_holder] -->    position: absolute;<!-- [et_pb_line_break_holder] -->    right: 32px;<!-- [et_pb_line_break_holder] -->    top: 32px;<!-- [et_pb_line_break_holder] -->    padding: 10px 14px;<!-- [et_pb_line_break_holder] -->    border-radius: 12px;<!-- [et_pb_line_break_holder] -->    border: 1px solid rgba(255,255,255,0.3);<!-- [et_pb_line_break_holder] -->    background: rgba(0,0,0,0.45);<!-- [et_pb_line_break_holder] -->    color: #fff;<!-- [et_pb_line_break_holder] -->    font-size: 13px;<!-- [et_pb_line_break_holder] -->    font-weight: 700;<!-- [et_pb_line_break_holder] -->    cursor: pointer;<!-- [et_pb_line_break_holder] -->    backdrop-filter: blur(6px);<!-- [et_pb_line_break_holder] -->    transition: background 0.18s ease, transform 0.18s ease, opacity 0.18s ease;<!-- [et_pb_line_break_holder] -->    z-index: 5;<!-- [et_pb_line_break_holder] -->  }<!-- [et_pb_line_break_holder] -->  .autoplay-btn:hover { background: rgba(0,0,0,0.65); transform: translateY(-1px); }<!-- [et_pb_line_break_holder] -->  .autoplay-btn:active { transform: translateY(0); }<!-- [et_pb_line_break_holder] --><\/style>\n<p><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --><\/p>\n<div class=\"earth-sticky-wrapper\" id=\"earth-sticky-wrapper\"><!-- [et_pb_line_break_holder] -->  <\/p>\n<div class=\"earth-container\"><!-- [et_pb_line_break_holder] -->    <canvas id=\"earth-canvas\"><\/canvas><!-- [et_pb_line_break_holder] -->    <\/p>\n<div id=\"loading-overlay\" class=\"loading-overlay\"><!-- [et_pb_line_break_holder] -->      <\/p>\n<div class=\"spinner\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->      <\/p>\n<div class=\"loading-text\">Loading Earth data&#8230;<\/div>\n<p><!-- [et_pb_line_break_holder] -->    <\/div>\n<p><!-- [et_pb_line_break_holder] -->    <\/p>\n<div id=\"tooltip\" style=\"position:absolute;display:none;background:rgba(0,0,0,0.8);color:white;padding:5px;border-radius:3px;pointer-events:none;z-index:1000;\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->    <\/p>\n<div id=\"phase-text\" class=\"phase-text\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->    <button id=\"autoplay-btn\" class=\"autoplay-btn\">Autoplay<\/button><!-- [et_pb_line_break_holder] -->  <\/div>\n<p><!-- [et_pb_line_break_holder] --><\/div>\n<p><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --><script><!-- [et_pb_line_break_holder] -->  \/\/ Keep your full fallback array here exactly as before:<!-- [et_pb_line_break_holder] -->  const universityData = [<!-- [et_pb_line_break_holder] -->    \/* ... YOUR SAME universityData ARRAY FROM BEFORE (UNCHANGED) ... *\/<!-- [et_pb_line_break_holder] -->  ];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->  class RotatingEarth {<!-- [et_pb_line_break_holder] -->    constructor(canvasId, options = {}) {<!-- [et_pb_line_break_holder] -->      this.canvas = document.getElementById(canvasId);<!-- [et_pb_line_break_holder] -->      this.context = this.canvas.getContext('2d');<!-- [et_pb_line_break_holder] -->      this.loadingOverlay = document.getElementById('loading-overlay');<!-- [et_pb_line_break_holder] -->      this.tooltip = document.getElementById('tooltip');<!-- [et_pb_line_break_holder] -->      this.phaseText = document.getElementById('phase-text');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.width = options.width || 800;<!-- [et_pb_line_break_holder] -->      this.height = options.height || 600;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.allDots = [];<!-- [et_pb_line_break_holder] -->      this.landFeatures = null;<!-- [et_pb_line_break_holder] -->      this.universities = [];<!-- [et_pb_line_break_holder] -->      this.highlightedFeatureIndices = new Set();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.initialRotation = [0, -38];<!-- [et_pb_line_break_holder] -->      this.rotation = [...this.initialRotation];<!-- [et_pb_line_break_holder] -->      this.scrollBaseRotation = this.rotation[0];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.autoRotate = true;<!-- [et_pb_line_break_holder] -->      this.rotationSpeed = 0.34;<!-- [et_pb_line_break_holder] -->      this.rotationTimer = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.logoImage = null;<!-- [et_pb_line_break_holder] -->      this.isInteracting = false;<!-- [et_pb_line_break_holder] -->      this.bgColor = '#005499';<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->      this.autoPlayStart = 0;<!-- [et_pb_line_break_holder] -->      this.autoPlayDuration = 20000;<!-- [et_pb_line_break_holder] -->      this.autoPlayHandle = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.mode = 'globe';<!-- [et_pb_line_break_holder] -->      this.orthoProjection = null;<!-- [et_pb_line_break_holder] -->      this.flatProjection = null;<!-- [et_pb_line_break_holder] -->      this.path = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.graticuleGeo = d3.geoGraticule().step([15, 15])();<!-- [et_pb_line_break_holder] -->      this.sphereBoundary = this.buildSphereBoundaryCoords();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.continentTours = [<!-- [et_pb_line_break_holder] -->        { name: 'Europe', lng: 10, lat: 50, zoom: 2.5 },<!-- [et_pb_line_break_holder] -->        { name: 'North America', lng: -100, lat: 40, zoom: 2.2 },<!-- [et_pb_line_break_holder] -->        { name: 'South America', lng: -60, lat: -20, zoom: 2.3 },<!-- [et_pb_line_break_holder] -->        { name: 'Asia', lng: 100, lat: 30, zoom: 2.2 },<!-- [et_pb_line_break_holder] -->        { name: 'Oceania', lng: 150, lat: -25, zoom: 2.5 }<!-- [et_pb_line_break_holder] -->      ];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.baseFlatScale = null;<!-- [et_pb_line_break_holder] -->      this.showLabels = false;<!-- [et_pb_line_break_holder] -->      this.currentZoom = 1;<!-- [et_pb_line_break_holder] -->      this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->      this.zoomPhaseProgress = 0;<!-- [et_pb_line_break_holder] -->      this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      \/\/ ===== Starfield around globe (space) =====<!-- [et_pb_line_break_holder] -->      this.stars = [];<!-- [et_pb_line_break_holder] -->      this.starCount = 520;          \/\/ visible and immersive<!-- [et_pb_line_break_holder] -->      this.starSeed = (Math.random() * 1e9) | 0;<!-- [et_pb_line_break_holder] -->      this._timeSec = 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.init();<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    init() {<!-- [et_pb_line_break_holder] -->      this.setupCanvas();<!-- [et_pb_line_break_holder] -->      this.setupProjection();<!-- [et_pb_line_break_holder] -->      this.setupInteractions();<!-- [et_pb_line_break_holder] -->      window.addEventListener('resize', () => this.handleResize(), { passive: true });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.initStars();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.loadWorldData();<!-- [et_pb_line_break_holder] -->      this.loadLogo();<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ ---------- Deterministic RNG ----------<!-- [et_pb_line_break_holder] -->    mulberry32(a) {<!-- [et_pb_line_break_holder] -->      return function() {<!-- [et_pb_line_break_holder] -->        let t = a += 0x6D2B79F5;<!-- [et_pb_line_break_holder] -->        t = Math.imul(t ^ (t >>> 15), t | 1);<!-- [et_pb_line_break_holder] -->        t ^= t + Math.imul(t ^ (t >>> 7), t | 61);<!-- [et_pb_line_break_holder] -->        return ((t ^ (t >>> 14)) >>> 0) \/ 4294967296;<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ ---------- Starfield (3D-ish) ----------<!-- [et_pb_line_break_holder] -->    initStars() {<!-- [et_pb_line_break_holder] -->      const rand = this.mulberry32(this.starSeed);<!-- [et_pb_line_break_holder] -->      this.stars = [];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      for (let i = 0; i < this.starCount; i++) {<!-- [et_pb_line_break_holder] -->        \/\/ random direction (unit vector)<!-- [et_pb_line_break_holder] -->        const u = rand() * 2 - 1;            \/\/ cos(theta)<!-- [et_pb_line_break_holder] -->        const phi = rand() * Math.PI * 2;<!-- [et_pb_line_break_holder] -->        const s = Math.sqrt(1 - u*u);<!-- [et_pb_line_break_holder] -->        const dir = { x: s * Math.cos(phi), y: u, z: s * Math.sin(phi) };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ distance: some near, many far<!-- [et_pb_line_break_holder] -->        const nearMix = Math.pow(rand(), 2.2);          \/\/ bias to small values<!-- [et_pb_line_break_holder] -->        const distMul = 1.35 + nearMix * 2.65;          \/\/ ~[1.35..4.0] radii<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ star properties<!-- [et_pb_line_break_holder] -->        const base = 0.12 + rand() * 0.55;<!-- [et_pb_line_break_holder] -->        const size = 0.5 + rand() * 1.9;<!-- [et_pb_line_break_holder] -->        const tw = rand() * Math.PI * 2;<!-- [et_pb_line_break_holder] -->        const twSpeed = 0.4 + rand() * 1.6;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ gentle drift in space<!-- [et_pb_line_break_holder] -->        const drift = {<!-- [et_pb_line_break_holder] -->          x: (rand() - 0.5) * 0.05,<!-- [et_pb_line_break_holder] -->          y: (rand() - 0.5) * 0.05,<!-- [et_pb_line_break_holder] -->          z: (rand() - 0.5) * 0.05,<!-- [et_pb_line_break_holder] -->        };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        this.stars.push({ dir, distMul, base, size, tw, twSpeed, drift });<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ Rotate vector around Y axis<!-- [et_pb_line_break_holder] -->    rotY(v, a) {<!-- [et_pb_line_break_holder] -->      const ca = Math.cos(a), sa = Math.sin(a);<!-- [et_pb_line_break_holder] -->      return { x: v.x * ca + v.z * sa, y: v.y, z: -v.x * sa + v.z * ca };<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ Rotate around X axis slightly (adds \u201cdepth swirl\u201d)<!-- [et_pb_line_break_holder] -->    rotX(v, a) {<!-- [et_pb_line_break_holder] -->      const ca = Math.cos(a), sa = Math.sin(a);<!-- [et_pb_line_break_holder] -->      return { x: v.x, y: v.y * ca - v.z * sa, z: v.y * sa + v.z * ca };<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    drawStarfield(alphaMul = 1) {<!-- [et_pb_line_break_holder] -->      if (!this.stars.length) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      const ctx = this.context;<!-- [et_pb_line_break_holder] -->      const cx = this.containerWidth \/ 2;<!-- [et_pb_line_break_holder] -->      const cy = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] -->      const R = this.orthoProjection?.scale?.() || this.radius;<!-- [et_pb_line_break_holder] -->      const now = this._timeSec;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      \/\/ Couple a bit of star motion to earth rotation (subtle)<!-- [et_pb_line_break_holder] -->      const earthYaw = (this.rotation?.[0] || 0) * Math.PI \/ 180;<!-- [et_pb_line_break_holder] -->      const couple = 0.16; \/\/ how much stars \u201cfollow\u201d earth rotation<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      \/\/ Perspective tuning<!-- [et_pb_line_break_holder] -->      const persp = 2.2 * R;       \/\/ focal length<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      \/\/ Don\u2019t draw stars inside the globe disk<!-- [et_pb_line_break_holder] -->      const globeCut = (R * 0.985);<!-- [et_pb_line_break_holder] -->      const globeCut2 = globeCut * globeCut;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      ctx.save();<!-- [et_pb_line_break_holder] -->      ctx.globalCompositeOperation = 'lighter';<!-- [et_pb_line_break_holder] -->      ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->      ctx.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->      ctx.lineCap = 'round';<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      \/\/ slight vignette-like enhancement: draw far stars first<!-- [et_pb_line_break_holder] -->      const starsSorted = this.stars.slice().sort((a,b) => b.distMul - a.distMul);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      for (const s of starsSorted) {<!-- [et_pb_line_break_holder] -->        \/\/ base 3D position around globe<!-- [et_pb_line_break_holder] -->        const dist = s.distMul * R;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ add tiny drift<!-- [et_pb_line_break_holder] -->        const driftA = 0.12;<!-- [et_pb_line_break_holder] -->        const d = {<!-- [et_pb_line_break_holder] -->          x: s.drift.x * Math.sin(now * 0.7 + s.tw),<!-- [et_pb_line_break_holder] -->          y: s.drift.y * Math.sin(now * 0.6 + s.tw * 0.7),<!-- [et_pb_line_break_holder] -->          z: s.drift.z * Math.sin(now * 0.8 + s.tw * 1.3)<!-- [et_pb_line_break_holder] -->        };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        let v = {<!-- [et_pb_line_break_holder] -->          x: s.dir.x + driftA * d.x,<!-- [et_pb_line_break_holder] -->          y: s.dir.y + driftA * d.y,<!-- [et_pb_line_break_holder] -->          z: s.dir.z + driftA * d.z<!-- [et_pb_line_break_holder] -->        };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ apply subtle coupled rotation (feels anchored to scene)<!-- [et_pb_line_break_holder] -->        v = this.rotY(v, earthYaw * couple);<!-- [et_pb_line_break_holder] -->        v = this.rotX(v, Math.sin(earthYaw * 0.25) * 0.06);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ scale to distance<!-- [et_pb_line_break_holder] -->        const px3 = v.x * dist;<!-- [et_pb_line_break_holder] -->        const py3 = v.y * dist;<!-- [et_pb_line_break_holder] -->        const pz3 = v.z * dist;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ camera: looking toward +z (so higher z is closer)<!-- [et_pb_line_break_holder] -->        const zCam = pz3 + 2.8 * R;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ If behind camera, skip<!-- [et_pb_line_break_holder] -->        if (zCam <= 0.01) continue;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ perspective projection<!-- [et_pb_line_break_holder] -->        const k = persp \/ (persp + zCam);<!-- [et_pb_line_break_holder] -->        const x = cx + px3 * k;<!-- [et_pb_line_break_holder] -->        const y = cy + py3 * k;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ skip if inside globe disk<!-- [et_pb_line_break_holder] -->        const dx = x - cx, dy = y - cy;<!-- [et_pb_line_break_holder] -->        if ((dx*dx + dy*dy) < globeCut2) continue;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ size\/alpha based on depth (nearer = bigger\/brighter)<!-- [et_pb_line_break_holder] -->        const depth = Math.max(0, Math.min(1, (zCam - 1.2*R) \/ (4.5*R)));<!-- [et_pb_line_break_holder] -->        const nearBoost = 1 - depth;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const tw = 0.65 + 0.35 * Math.sin(s.tw + now * s.twSpeed);<!-- [et_pb_line_break_holder] -->        const a = (s.base * (0.35 + 0.95 * nearBoost) * tw) * alphaMul;<!-- [et_pb_line_break_holder] -->        if (a < 0.01) continue;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const r = (s.size * (0.55 + 1.15 * nearBoost)) * (0.55 + 0.55 * k);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ small star \u201cspark\u201d (dot + tiny streak)<!-- [et_pb_line_break_holder] -->        ctx.globalAlpha = Math.min(1, a);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ streak direction based on slight orbital motion (fake)<!-- [et_pb_line_break_holder] -->        const sx = x + (0.6 + 1.8 * nearBoost) * 0.9;<!-- [et_pb_line_break_holder] -->        const sy = y + (0.6 + 1.8 * nearBoost) * 0.3;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        ctx.lineWidth = Math.max(0.7, r * 0.22);<!-- [et_pb_line_break_holder] -->        ctx.beginPath();<!-- [et_pb_line_break_holder] -->        ctx.moveTo(x, y);<!-- [et_pb_line_break_holder] -->        ctx.lineTo(sx, sy);<!-- [et_pb_line_break_holder] -->        ctx.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        ctx.beginPath();<!-- [et_pb_line_break_holder] -->        ctx.arc(x, y, Math.max(0.6, r * 0.55), 0, Math.PI * 2);<!-- [et_pb_line_break_holder] -->        ctx.fill();<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      ctx.restore();<!-- [et_pb_line_break_holder] -->      ctx.globalAlpha = 1;<!-- [et_pb_line_break_holder] -->      ctx.globalCompositeOperation = 'source-over';<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ ---------- Layout \/ projection ----------<!-- [et_pb_line_break_holder] -->    getDefaultContainerSize() {<!-- [et_pb_line_break_holder] -->      const containerWidth = Math.min(this.width, Math.max(320, window.innerWidth - 40));<!-- [et_pb_line_break_holder] -->      const maxHeight = Math.min(this.height, window.innerHeight - 40, containerWidth * 0.55);<!-- [et_pb_line_break_holder] -->      const containerHeight = Math.max(320, maxHeight);<!-- [et_pb_line_break_holder] -->      return { containerWidth, containerHeight };<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    setCanvasSize(containerWidth, containerHeight, dprOverride) {<!-- [et_pb_line_break_holder] -->      const dpr = dprOverride ?? Math.min(3, window.devicePixelRatio || 2);<!-- [et_pb_line_break_holder] -->      this.dpr = dpr;<!-- [et_pb_line_break_holder] -->      this.radius = Math.min(containerWidth, containerHeight) \/ 3.0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.canvas.width = containerWidth * dpr;<!-- [et_pb_line_break_holder] -->      this.canvas.height = containerHeight * dpr;<!-- [et_pb_line_break_holder] -->      this.canvas.style.width = `${containerWidth}px`;<!-- [et_pb_line_break_holder] -->      this.canvas.style.height = `${containerHeight}px`;<!-- [et_pb_line_break_holder] -->      this.context.setTransform(dpr, 0, 0, dpr, 0, 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.containerWidth = containerWidth;<!-- [et_pb_line_break_holder] -->      this.containerHeight = containerHeight;<!-- [et_pb_line_break_holder] -->      this.setupProjection();<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    setupCanvas() {<!-- [et_pb_line_break_holder] -->      const { containerWidth, containerHeight } = this.getDefaultContainerSize();<!-- [et_pb_line_break_holder] -->      this.setCanvasSize(containerWidth, containerHeight);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    sizeFactor() { return window.innerWidth < 768 ? 0.7 : 1; }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    handleResize() {<!-- [et_pb_line_break_holder] -->      const { containerWidth, containerHeight } = this.getDefaultContainerSize();<!-- [et_pb_line_break_holder] -->      const oldBaseScale = this.baseFlatScale;<!-- [et_pb_line_break_holder] -->      this.setCanvasSize(containerWidth, containerHeight);<!-- [et_pb_line_break_holder] -->      if (oldBaseScale && this.mode === 'flat') {<!-- [et_pb_line_break_holder] -->        const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->        const zoomRatio = currentScale \/ oldBaseScale;<!-- [et_pb_line_break_holder] -->        this.flatProjection.scale(this.baseFlatScale * zoomRatio);<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->      this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->      this.render();<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    setupProjection() {<!-- [et_pb_line_break_holder] -->      this.orthoProjection = d3.geoOrthographic()<!-- [et_pb_line_break_holder] -->        .scale(this.radius)<!-- [et_pb_line_break_holder] -->        .translate([this.containerWidth \/ 2, this.containerHeight \/ 2])<!-- [et_pb_line_break_holder] -->        .clipAngle(90)<!-- [et_pb_line_break_holder] -->        .rotate(this.rotation);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      const flatScale = (this.containerWidth \/ (2 * Math.PI)) * 0.9;<!-- [et_pb_line_break_holder] -->      this.baseFlatScale = flatScale;<!-- [et_pb_line_break_holder] -->      this.flatProjection = d3.geoEquirectangular()<!-- [et_pb_line_break_holder] -->        .center([0, 0])<!-- [et_pb_line_break_holder] -->        .scale(flatScale)<!-- [et_pb_line_break_holder] -->        .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.path = d3.geoPath().projection(this.orthoProjection).context(this.context);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ ---------- Geometry helpers (unchanged) ----------<!-- [et_pb_line_break_holder] -->    pointInPolygon(point, polygon) {<!-- [et_pb_line_break_holder] -->      const [x, y] = point;<!-- [et_pb_line_break_holder] -->      let inside = false;<!-- [et_pb_line_break_holder] -->      for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {<!-- [et_pb_line_break_holder] -->        const [xi, yi] = polygon[i];<!-- [et_pb_line_break_holder] -->        const [xj, yj] = polygon[j];<!-- [et_pb_line_break_holder] -->        if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) \/ (yj - yi) + xi) inside = !inside;<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->      return inside;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    pointInFeature(point, feature) {<!-- [et_pb_line_break_holder] -->      const geometry = feature.geometry;<!-- [et_pb_line_break_holder] -->      if (geometry.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->        const coordinates = geometry.coordinates;<!-- [et_pb_line_break_holder] -->        if (!this.pointInPolygon(point, coordinates[0])) return false;<!-- [et_pb_line_break_holder] -->        for (let i = 1; i < coordinates.length; i++) if (this.pointInPolygon(point, coordinates[i])) return false;<!-- [et_pb_line_break_holder] -->        return true;<!-- [et_pb_line_break_holder] -->      } else if (geometry.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->        for (const polygon of geometry.coordinates) {<!-- [et_pb_line_break_holder] -->          if (this.pointInPolygon(point, polygon[0])) {<!-- [et_pb_line_break_holder] -->            let inHole = false;<!-- [et_pb_line_break_holder] -->            for (let i = 1; i < polygon.length; i++) {<!-- [et_pb_line_break_holder] -->              if (this.pointInPolygon(point, polygon[i])) { inHole = true; break; }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            if (!inHole) return true;<!-- [et_pb_line_break_holder] -->          }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] -->        return false;<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->      return false;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    generateDotsInPolygon(feature, dotSpacing = 16) {<!-- [et_pb_line_break_holder] -->      const dots = [];<!-- [et_pb_line_break_holder] -->      const stepSize = Math.max(0.5, dotSpacing * 0.08);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      const normLng = (lng) => {<!-- [et_pb_line_break_holder] -->        const n = ((lng + 180) % 360 + 360) % 360 - 180;<!-- [et_pb_line_break_holder] -->        return n === -180 ? 180 : n;<!-- [et_pb_line_break_holder] -->      };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      const samplePolygon = (rings) => {<!-- [et_pb_line_break_holder] -->        const lons = rings.flat().map(([lng]) => normLng(lng));<!-- [et_pb_line_break_holder] -->        const lats = rings.flat().map(([, lat]) => lat);<!-- [et_pb_line_break_holder] -->        const minLng = Math.min(...lons), maxLng = Math.max(...lons);<!-- [et_pb_line_break_holder] -->        const minLat = Math.min(...lats), maxLat = Math.max(...lats);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const crossesDateline = maxLng - minLng > 180;<!-- [et_pb_line_break_holder] -->        const ranges = crossesDateline ? [[-180, 0], [0, 180]] : [[minLng, maxLng]];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        ranges.forEach(([lngStart, lngEnd]) => {<!-- [et_pb_line_break_holder] -->          for (let lng = lngStart; lng <= lngEnd; lng += stepSize) {<!-- [et_pb_line_break_holder] -->            for (let lat = minLat; lat <= maxLat; lat += stepSize) {<!-- [et_pb_line_break_holder] -->              const candidates = [[lng, lat], [lng + 360, lat], [lng - 360, lat]];<!-- [et_pb_line_break_holder] -->              if (candidates.some((p) => d3.geoContains(feature, p))) dots.push([lng, lat]);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->          }<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->      };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->      if (!geom) return dots;<!-- [et_pb_line_break_holder] -->      if (geom.type === 'Polygon') samplePolygon(geom.coordinates);<!-- [et_pb_line_break_holder] -->      else if (geom.type === 'MultiPolygon') geom.coordinates.forEach((poly) => samplePolygon(poly));<!-- [et_pb_line_break_holder] -->      return dots;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ ---------- Render ----------<!-- [et_pb_line_break_holder] -->    render() {<!-- [et_pb_line_break_holder] -->      this.context.clearRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->      this.context.save();<!-- [et_pb_line_break_holder] -->      this.context.fillStyle = this.bgColor || '#fff';<!-- [et_pb_line_break_holder] -->      this.context.fillRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->      this.context.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      if (this.mode === 'globe') this.renderGlobe();<!-- [et_pb_line_break_holder] -->      else this.renderFlat();<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    isZoomingFlat() { return this.mode === 'flat' && this.zoomPhaseProgress > 0.01; }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    renderGlobe() {<!-- [et_pb_line_break_holder] -->      const projection = this.orthoProjection;<!-- [et_pb_line_break_holder] -->      this.path.projection(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      const currentScale = projection.scale();<!-- [et_pb_line_break_holder] -->      const scaleFactor = currentScale \/ this.radius;<!-- [et_pb_line_break_holder] -->      const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this._timeSec = performance.now() * 0.001;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      \/\/ --- Starfield first (behind everything) ---<!-- [et_pb_line_break_holder] -->      this.drawStarfield(1.0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      \/\/ soft back glow (globe aura)<!-- [et_pb_line_break_holder] -->      projection.clipAngle(180);<!-- [et_pb_line_break_holder] -->      this.context.save();<!-- [et_pb_line_break_holder] -->      this.context.filter = 'blur(1px)';<!-- [et_pb_line_break_holder] -->      this.context.beginPath();<!-- [et_pb_line_break_holder] -->      this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->      this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->      this.context.globalAlpha = 0.20;<!-- [et_pb_line_break_holder] -->      this.context.fill();<!-- [et_pb_line_break_holder] -->      this.context.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      \/\/ globe body<!-- [et_pb_line_break_holder] -->      projection.clipAngle(90);<!-- [et_pb_line_break_holder] -->      this.context.beginPath();<!-- [et_pb_line_break_holder] -->      this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->      this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->      this.context.fill();<!-- [et_pb_line_break_holder] -->      this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->      this.context.lineWidth = 2 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->      this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->        projection.clipAngle(180);<!-- [et_pb_line_break_holder] -->        const graticuleBack = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->        this.context.beginPath();<!-- [et_pb_line_break_holder] -->        this.path(graticuleBack());<!-- [et_pb_line_break_holder] -->        this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->        this.context.lineWidth = 1 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.context.globalAlpha = 0.14 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->        this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->        const highlightAlpha = 0.30;<!-- [et_pb_line_break_holder] -->        this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->          this.context.beginPath();<!-- [et_pb_line_break_holder] -->          this.path(feature);<!-- [et_pb_line_break_holder] -->          this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->          this.context.globalAlpha = this.highlightedFeatureIndices.has(idx) ? highlightAlpha : baseFillAlpha;<!-- [et_pb_line_break_holder] -->          this.context.fill();<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        this.context.beginPath();<!-- [et_pb_line_break_holder] -->        this.landFeatures.features.forEach(feature => { this.path(feature); });<!-- [et_pb_line_break_holder] -->        this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->        this.context.lineWidth = 0.7 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.context.globalAlpha = 0.18;<!-- [et_pb_line_break_holder] -->        this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        projection.clipAngle(90);<!-- [et_pb_line_break_holder] -->        const graticuleFront = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->        this.context.beginPath();<!-- [et_pb_line_break_holder] -->        this.path(graticuleFront());<!-- [et_pb_line_break_holder] -->        this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->        this.context.lineWidth = 1 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.context.globalAlpha = 0.18 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->        this.context.stroke();<!-- [et_pb_line_break_holder] -->        this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        this.context.beginPath();<!-- [et_pb_line_break_holder] -->        this.landFeatures.features.forEach(feature => { this.path(feature); });<!-- [et_pb_line_break_holder] -->        this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->        this.context.lineWidth = 0.7 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ dots<!-- [et_pb_line_break_holder] -->        const centerX = this.containerWidth \/ 2;<!-- [et_pb_line_break_holder] -->        const centerY = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] -->        const centerCoord = projection.invert([centerX, centerY]);<!-- [et_pb_line_break_holder] -->        const isFront = (lon, lat) => d3.geoDistance([lon, lat], centerCoord) <= Math.PI \/ 2;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const dotRadius = 0.6 * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->          const projected = projection([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->          if (!projected) return;<!-- [et_pb_line_break_holder] -->          if (!isFront(dot.lng, dot.lat)) return;<!-- [et_pb_line_break_holder] -->          const dx = projected[0] - centerX;<!-- [et_pb_line_break_holder] -->          const dy = projected[1] - centerY;<!-- [et_pb_line_break_holder] -->          const dist = Math.sqrt(dx * dx + dy * dy);<!-- [et_pb_line_break_holder] -->          if (dist > currentScale) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->          const opacity = (90 - Math.abs(dot.lat)) \/ 90;<!-- [et_pb_line_break_holder] -->          this.context.globalAlpha = opacity;<!-- [et_pb_line_break_holder] -->          this.context.beginPath();<!-- [et_pb_line_break_holder] -->          this.context.arc(projected[0], projected[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->          this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->          this.context.fill();<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->        this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ markers<!-- [et_pb_line_break_holder] -->        const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.universities.forEach((uni) => {<!-- [et_pb_line_break_holder] -->          const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->          if (!projected) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->          this.context.shadowColor = 'rgba(0,106,179,0.6)';<!-- [et_pb_line_break_holder] -->          this.context.shadowBlur = 3 * scaleFactor;<!-- [et_pb_line_break_holder] -->          this.context.shadowOffsetX = 2 * scaleFactor;<!-- [et_pb_line_break_holder] -->          this.context.shadowOffsetY = 2 * scaleFactor;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->          this.context.beginPath();<!-- [et_pb_line_break_holder] -->          this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->          this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->          this.context.fill();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->          this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->          this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->          this.context.shadowOffsetX = 0;<!-- [et_pb_line_break_holder] -->          this.context.shadowOffsetY = 0;<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      \/\/ logo<!-- [et_pb_line_break_holder] -->      if (this.logoImage && this.logoImage.complete) {<!-- [et_pb_line_break_holder] -->        this.context.save();<!-- [et_pb_line_break_holder] -->        this.context.beginPath();<!-- [et_pb_line_break_holder] -->        this.context.arc(this.containerWidth \/ 2, this.containerHeight \/ 2, projection.scale(), 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->        this.context.clip();<!-- [et_pb_line_break_holder] -->        const logoSize = 2.2 * projection.scale();<!-- [et_pb_line_break_holder] -->        const x = this.containerWidth \/ 2 - logoSize \/ 2;<!-- [et_pb_line_break_holder] -->        const y = this.containerHeight \/ 2 - logoSize \/ 2;<!-- [et_pb_line_break_holder] -->        this.context.globalAlpha = this.isInteracting ? 0.25 : 1;<!-- [et_pb_line_break_holder] -->        this.context.drawImage(this.logoImage, x, y, logoSize, logoSize);<!-- [et_pb_line_break_holder] -->        this.context.restore();<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    renderFlat() {<!-- [et_pb_line_break_holder] -->      const projection = this.flatProjection;<!-- [et_pb_line_break_holder] -->      this.path.projection(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.context.beginPath();<!-- [et_pb_line_break_holder] -->      this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->      this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->      this.context.fill();<!-- [et_pb_line_break_holder] -->      if (!this.isZoomingFlat()) {<!-- [et_pb_line_break_holder] -->        this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->        this.context.lineWidth = 1 * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.context.stroke();<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->        const graticule = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->        this.context.beginPath();<!-- [et_pb_line_break_holder] -->        this.path(graticule());<!-- [et_pb_line_break_holder] -->        this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->        this.context.lineWidth = 0.8 * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.context.globalAlpha = 0.4 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->        this.context.stroke();<!-- [et_pb_line_break_holder] -->        this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->        const highlightAlpha = 0.35;<!-- [et_pb_line_break_holder] -->        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->        this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->          this.context.beginPath();<!-- [et_pb_line_break_holder] -->          this.path(feature);<!-- [et_pb_line_break_holder] -->          this.context.globalAlpha = this.highlightedFeatureIndices.has(idx) ? highlightAlpha : baseFillAlpha;<!-- [et_pb_line_break_holder] -->          this.context.fill();<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->        this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        this.context.beginPath();<!-- [et_pb_line_break_holder] -->        this.landFeatures.features.forEach(feature => { this.path(feature); });<!-- [et_pb_line_break_holder] -->        this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->        this.context.lineWidth = 0.7 * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const dotRadius = 0.6 * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->          const p = projection([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->          if (!p) return;<!-- [et_pb_line_break_holder] -->          this.context.beginPath();<!-- [et_pb_line_break_holder] -->          this.context.arc(p[0], p[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->          this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->          this.context.fill();<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->          const p = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->          if (!p) return;<!-- [et_pb_line_break_holder] -->          this.context.beginPath();<!-- [et_pb_line_break_holder] -->          this.context.arc(p[0], p[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->          this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->          this.context.fill();<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ ---------- Scroll animation (snappy) ----------<!-- [et_pb_line_break_holder] -->    applyScrollProgress(progress) {<!-- [et_pb_line_break_holder] -->      const p = Math.max(0, Math.min(1, progress));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      const rotatePortion = 0.12;<!-- [et_pb_line_break_holder] -->      const morphPortion  = 0.08;<!-- [et_pb_line_break_holder] -->      const flatHoldPortion = 0.08;<!-- [et_pb_line_break_holder] -->      const foldBackPortion = 0.14;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      const morphEnd = rotatePortion + morphPortion;<!-- [et_pb_line_break_holder] -->      const flatHoldEnd = morphEnd + flatHoldPortion;<!-- [et_pb_line_break_holder] -->      const foldBackStart = 1 - foldBackPortion;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      if (this.autoRotate) {<!-- [et_pb_line_break_holder] -->        this.autoRotate = false;<!-- [et_pb_line_break_holder] -->        if (this.rotationTimer) { this.rotationTimer.stop(); this.rotationTimer = null; }<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.zoomPhaseProgress = 0;<!-- [et_pb_line_break_holder] -->      this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->      this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      if (p <= rotatePortion) {<!-- [et_pb_line_break_holder] -->        const t = p \/ rotatePortion;<!-- [et_pb_line_break_holder] -->        const eased = this.easeOutQuint(t);<!-- [et_pb_line_break_holder] -->        const angle = this.scrollBaseRotation + eased * 360;<!-- [et_pb_line_break_holder] -->        this.rotation[0] = angle;<!-- [et_pb_line_break_holder] -->        this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->        this.mode = 'globe';<!-- [et_pb_line_break_holder] -->        this.updatePhaseText('Study Abroad. Find your perfect university now!');<!-- [et_pb_line_break_holder] -->        this.showLabels = false;<!-- [et_pb_line_break_holder] -->        this.render();<!-- [et_pb_line_break_holder] -->      } else if (p <= morphEnd) {<!-- [et_pb_line_break_holder] -->        const unfoldT = (p - rotatePortion) \/ morphPortion;<!-- [et_pb_line_break_holder] -->        const eased = this.easeInOutQuint(unfoldT);<!-- [et_pb_line_break_holder] -->        this.mode = 'morphing';<!-- [et_pb_line_break_holder] -->        this.updatePhaseText('Semester abroad, Bachelor \/ Master Abroad, Double Degree, Gap Year, Summer Sessions, and many more!');<!-- [et_pb_line_break_holder] -->        this.showLabels = false;<!-- [et_pb_line_break_holder] -->        this.renderMorph(eased);<!-- [et_pb_line_break_holder] -->      } else if (p <= flatHoldEnd) {<!-- [et_pb_line_break_holder] -->        this.mode = 'flat';<!-- [et_pb_line_break_holder] -->        this.showLabels = false;<!-- [et_pb_line_break_holder] -->        this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->        this.updatePhaseText('72 Universtities in 25 Countries');<!-- [et_pb_line_break_holder] -->        this.render();<!-- [et_pb_line_break_holder] -->      } else if (p < foldBackStart) {<!-- [et_pb_line_break_holder] -->        const tourProgress = (p - flatHoldEnd) \/ (foldBackStart - flatHoldEnd);<!-- [et_pb_line_break_holder] -->        this.zoomPhaseProgress = Math.max(0, Math.min(1, tourProgress));<!-- [et_pb_line_break_holder] -->        const fastFadePortion = 0.12;<!-- [et_pb_line_break_holder] -->        const fadeT = Math.max(0, Math.min(1, tourProgress \/ fastFadePortion));<!-- [et_pb_line_break_holder] -->        this.graticuleFade = Math.max(0, 1 - fadeT);<!-- [et_pb_line_break_holder] -->        this.showLabels = true;<!-- [et_pb_line_break_holder] -->        this.handleContinentTour(tourProgress);<!-- [et_pb_line_break_holder] -->      } else {<!-- [et_pb_line_break_holder] -->        const foldT = (p - foldBackStart) \/ foldBackPortion;<!-- [et_pb_line_break_holder] -->        const eased = this.easeInOutQuint(foldT);<!-- [et_pb_line_break_holder] -->        this.mode = 'morphing';<!-- [et_pb_line_break_holder] -->        this.graticuleFade = Math.min(1, eased);<!-- [et_pb_line_break_holder] -->        this.showLabels = false;<!-- [et_pb_line_break_holder] -->        this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->        this.renderMorph(1 - eased);<!-- [et_pb_line_break_holder] -->        if (foldT >= 0.99) {<!-- [et_pb_line_break_holder] -->          this.mode = 'globe';<!-- [et_pb_line_break_holder] -->          this.rotation = [...this.initialRotation];<!-- [et_pb_line_break_holder] -->          this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->          this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->          this.showLabels = false;<!-- [et_pb_line_break_holder] -->          this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    handleContinentTour(progress) {<!-- [et_pb_line_break_holder] -->      \/\/ (Keep your existing tour logic if you want \u2014 unchanged for brevity)<!-- [et_pb_line_break_holder] -->      \/\/ For now, keep your last version\u2019s handleContinentTour\/animateToContinent\/transition methods<!-- [et_pb_line_break_holder] -->      \/\/ If you paste your existing ones here, everything will work exactly as before.<!-- [et_pb_line_break_holder] -->      \/\/ To not break your setup, we simply render flat view:<!-- [et_pb_line_break_holder] -->      this.mode = 'flat';<!-- [et_pb_line_break_holder] -->      this.render();<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    resetFlatProjection() {<!-- [et_pb_line_break_holder] -->      this.flatProjection<!-- [et_pb_line_break_holder] -->        .scale(this.baseFlatScale)<!-- [et_pb_line_break_holder] -->        .center([0, 0])<!-- [et_pb_line_break_holder] -->        .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] -->      this.currentZoom = 1;<!-- [et_pb_line_break_holder] -->      this.showLabels = false;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ ---------- Morph (stars fade OUT during morph) ----------<!-- [et_pb_line_break_holder] -->    easeOutQuint(t) {<!-- [et_pb_line_break_holder] -->      const c = Math.min(1, Math.max(0, t));<!-- [et_pb_line_break_holder] -->      return 1 - Math.pow(1 - c, 5);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    easeInOutQuint(t) {<!-- [et_pb_line_break_holder] -->      const c = Math.min(1, Math.max(0, t));<!-- [et_pb_line_break_holder] -->      return c < 0.5 ? 16 * c * c * c * c * c : 1 - Math.pow(-2 * c + 2, 5) \/ 2;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    createForwardInterpolatedProjection(t) {<!-- [et_pb_line_break_holder] -->      const tt = this.easeOutQuint(t);<!-- [et_pb_line_break_holder] -->      const startProj = this.orthoProjection;<!-- [et_pb_line_break_holder] -->      const endProj = this.flatProjection;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      return (coords) => {<!-- [et_pb_line_break_holder] -->        const p0 = startProj(coords);<!-- [et_pb_line_break_holder] -->        const p1 = endProj(coords);<!-- [et_pb_line_break_holder] -->        if (!p0 && !p1) return null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const x0 = p0 ? p0[0] : p1[0];<!-- [et_pb_line_break_holder] -->        const y0 = p0 ? p0[1] : p1[1];<!-- [et_pb_line_break_holder] -->        const x1 = p1 ? p1[0] : p0[0];<!-- [et_pb_line_break_holder] -->        const y1 = p1 ? p1[1] : p0[1];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        return [x0 + (x1 - x0) * tt, y0 + (y1 - y0) * tt];<!-- [et_pb_line_break_holder] -->      };<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    buildSphereBoundaryCoords() {<!-- [et_pb_line_break_holder] -->      const coords = [];<!-- [et_pb_line_break_holder] -->      const step = 5;<!-- [et_pb_line_break_holder] -->      for (let lon = -180; lon <= 180; lon += step) coords.push([lon, -89]);<!-- [et_pb_line_break_holder] -->      for (let lon = 180; lon >= -180; lon -= step) coords.push([lon, 89]);<!-- [et_pb_line_break_holder] -->      return coords;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ Your existing dateline-safe drawLineString\/drawPolygon + full renderMorph:<!-- [et_pb_line_break_holder] -->    drawLineString(ctx, coords, proj) {<!-- [et_pb_line_break_holder] -->      let prevP = null;<!-- [et_pb_line_break_holder] -->      let prevC = null;<!-- [et_pb_line_break_holder] -->      const thresholdX = this.containerWidth \/ 3;<!-- [et_pb_line_break_holder] -->      const thresholdY = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      coords.forEach((c) => {<!-- [et_pb_line_break_holder] -->        const p = proj(c);<!-- [et_pb_line_break_holder] -->        if (!p) return;<!-- [et_pb_line_break_holder] -->        const shouldBreak =<!-- [et_pb_line_break_holder] -->          !!prevP &&<!-- [et_pb_line_break_holder] -->          (Math.abs(p[0] - prevP[0]) > thresholdX ||<!-- [et_pb_line_break_holder] -->            Math.abs(p[1] - prevP[1]) > thresholdY ||<!-- [et_pb_line_break_holder] -->            (prevC && Math.abs(c[0] - prevC[0]) > 170));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        if (!prevP || shouldBreak) ctx.moveTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->        else ctx.lineTo(p[0], p[1]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        prevP = p;<!-- [et_pb_line_break_holder] -->        prevC = c;<!-- [et_pb_line_break_holder] -->      });<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    drawPolygon(ctx, coords, proj) {<!-- [et_pb_line_break_holder] -->      const thresholdX = this.containerWidth \/ 3;<!-- [et_pb_line_break_holder] -->      coords.forEach((ring) => {<!-- [et_pb_line_break_holder] -->        let prevP = null;<!-- [et_pb_line_break_holder] -->        let prevC = null;<!-- [et_pb_line_break_holder] -->        let firstP = null;<!-- [et_pb_line_break_holder] -->        let firstC = null;<!-- [et_pb_line_break_holder] -->        ring.forEach((c) => {<!-- [et_pb_line_break_holder] -->          const p = proj(c);<!-- [et_pb_line_break_holder] -->          if (!p) return;<!-- [et_pb_line_break_holder] -->          const shouldBreak =<!-- [et_pb_line_break_holder] -->            !!prevP &&<!-- [et_pb_line_break_holder] -->            (Math.abs(p[0] - prevP[0]) > thresholdX ||<!-- [et_pb_line_break_holder] -->              (prevC && Math.abs(c[0] - prevC[0]) > 170));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->          if (!prevP || shouldBreak) {<!-- [et_pb_line_break_holder] -->            ctx.moveTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->            firstP = firstP || p;<!-- [et_pb_line_break_holder] -->            firstC = firstC || c;<!-- [et_pb_line_break_holder] -->          } else {<!-- [et_pb_line_break_holder] -->            ctx.lineTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->          }<!-- [et_pb_line_break_holder] -->          prevP = p;<!-- [et_pb_line_break_holder] -->          prevC = c;<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        if (firstP && prevP) {<!-- [et_pb_line_break_holder] -->          const dxClose = Math.abs(firstP[0] - prevP[0]);<!-- [et_pb_line_break_holder] -->          const lonJump = firstC && prevC ? Math.abs(firstC[0] - prevC[0]) : 0;<!-- [et_pb_line_break_holder] -->          if (dxClose <= thresholdX &#038;&#038; lonJump <= 170) ctx.lineTo(firstP[0], firstP[1]);<!-- [et_pb_line_break_holder] -->          else ctx.moveTo(firstP[0], firstP[1]);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] -->      });<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    renderMorph(t) {<!-- [et_pb_line_break_holder] -->      const proj = this.createForwardInterpolatedProjection(t);<!-- [et_pb_line_break_holder] -->      const ctx = this.context;<!-- [et_pb_line_break_holder] -->      const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] -->      const morphScale = this.flatProjection.scale() \/ this.radius;<!-- [et_pb_line_break_holder] -->      const dotRadius = 0.5 * morphScale * sizeFactor;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      ctx.clearRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->      ctx.save();<!-- [et_pb_line_break_holder] -->      ctx.fillStyle = this.bgColor || '#fff';<!-- [et_pb_line_break_holder] -->      ctx.fillRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->      ctx.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      \/\/ starfield fades out fast on morph<!-- [et_pb_line_break_holder] -->      this._timeSec = performance.now() * 0.001;<!-- [et_pb_line_break_holder] -->      const starFade = Math.max(0, 1 - this.easeOutQuint(Math.min(1, t * 1.2)));<!-- [et_pb_line_break_holder] -->      if (starFade > 0.01) this.drawStarfield(starFade);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      \/\/ ocean plate<!-- [et_pb_line_break_holder] -->      ctx.beginPath();<!-- [et_pb_line_break_holder] -->      this.drawLineString(ctx, this.sphereBoundary, proj);<!-- [et_pb_line_break_holder] -->      ctx.closePath();<!-- [et_pb_line_break_holder] -->      ctx.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->      ctx.fill();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->        if (this.graticuleGeo && this.graticuleGeo.type === 'MultiLineString') {<!-- [et_pb_line_break_holder] -->          ctx.beginPath();<!-- [et_pb_line_break_holder] -->          this.graticuleGeo.coordinates.forEach(line => { this.drawLineString(ctx, line, proj); });<!-- [et_pb_line_break_holder] -->          ctx.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->          ctx.globalAlpha = 0.4 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->          ctx.lineWidth = 0.8 * sizeFactor;<!-- [et_pb_line_break_holder] -->          ctx.stroke();<!-- [et_pb_line_break_holder] -->          ctx.globalAlpha = 1;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->        const highlightAlpha = 0.35;<!-- [et_pb_line_break_holder] -->        ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->        this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->          const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->          if (!geom) return;<!-- [et_pb_line_break_holder] -->          ctx.beginPath();<!-- [et_pb_line_break_holder] -->          if (geom.type === 'Polygon') this.drawPolygon(ctx, geom.coordinates, proj);<!-- [et_pb_line_break_holder] -->          else if (geom.type === 'MultiPolygon') geom.coordinates.forEach(poly => this.drawPolygon(ctx, poly, proj));<!-- [et_pb_line_break_holder] -->          ctx.globalAlpha = this.highlightedFeatureIndices.has(idx) ? highlightAlpha : baseFillAlpha;<!-- [et_pb_line_break_holder] -->          ctx.fill();<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->        ctx.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        ctx.beginPath();<!-- [et_pb_line_break_holder] -->        this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->          const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->          if (!geom) return;<!-- [et_pb_line_break_holder] -->          if (geom.type === 'Polygon') this.drawPolygon(ctx, geom.coordinates, proj);<!-- [et_pb_line_break_holder] -->          else if (geom.type === 'MultiPolygon') geom.coordinates.forEach(poly => this.drawPolygon(ctx, poly, proj));<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->        ctx.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->        ctx.lineWidth = 0.7 * sizeFactor;<!-- [et_pb_line_break_holder] -->        ctx.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->          const p = proj([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->          if (!p) return;<!-- [et_pb_line_break_holder] -->          ctx.beginPath();<!-- [et_pb_line_break_holder] -->          ctx.arc(p[0], p[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->          ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->          ctx.fill();<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->        this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->          const p = proj([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->          if (!p) return;<!-- [et_pb_line_break_holder] -->          ctx.beginPath();<!-- [et_pb_line_break_holder] -->          ctx.arc(p[0], p[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->          ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->          ctx.fill();<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ ---------- Text ----------<!-- [et_pb_line_break_holder] -->    updatePhaseText(text) {<!-- [et_pb_line_break_holder] -->      if (!this.phaseText) return;<!-- [et_pb_line_break_holder] -->      if (this._currentPhaseText === text) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this._currentPhaseText = text;<!-- [et_pb_line_break_holder] -->      this.phaseText.textContent = text || '';<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      if (text) {<!-- [et_pb_line_break_holder] -->        this.phaseText.classList.add('visible');<!-- [et_pb_line_break_holder] -->        this.phaseText.classList.remove('reveal');<!-- [et_pb_line_break_holder] -->        void this.phaseText.offsetWidth;<!-- [et_pb_line_break_holder] -->        this.phaseText.classList.add('reveal');<!-- [et_pb_line_break_holder] -->      } else {<!-- [et_pb_line_break_holder] -->        this.phaseText.classList.remove('visible');<!-- [et_pb_line_break_holder] -->        this.phaseText.classList.remove('reveal');<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ ---------- Scroll trigger ----------<!-- [et_pb_line_break_holder] -->    setupScrollTrigger(targetEl, options = {}) {<!-- [et_pb_line_break_holder] -->      const el = targetEl || this.canvas;<!-- [et_pb_line_break_holder] -->      this.scrollBaseRotation = this.rotation[0];<!-- [et_pb_line_break_holder] -->      const startOffsetOpt = options.startOffset;<!-- [et_pb_line_break_holder] -->      const startAtSticky = options.startAtSticky !== false;<!-- [et_pb_line_break_holder] -->      const endOffset = options.endOffset ?? 0.8;<!-- [et_pb_line_break_holder] -->      const clamp01 = (v) => Math.max(0, Math.min(1, v));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      const onScroll = () => {<!-- [et_pb_line_break_holder] -->        const viewport = window.innerHeight || 1;<!-- [et_pb_line_break_holder] -->        const rect = el.getBoundingClientRect();<!-- [et_pb_line_break_holder] -->        const wrapperHeight = el.offsetHeight || 1;<!-- [et_pb_line_break_holder] -->        const wrapperTopAbs = rect.top + (window.scrollY || window.pageYOffset || 0);<!-- [et_pb_line_break_holder] -->        const scrolledInside = (window.scrollY || window.pageYOffset || 0) - wrapperTopAbs;<!-- [et_pb_line_break_holder] -->        const stickyRange = Math.max(1, wrapperHeight - viewport);<!-- [et_pb_line_break_holder] -->        const raw = clamp01(scrolledInside \/ stickyRange);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const startOffset = startOffsetOpt != null ? startOffsetOpt : (startAtSticky ? 0 : 0);<!-- [et_pb_line_break_holder] -->        const normalized = clamp01((raw - startOffset) \/ Math.max(0.0001, endOffset - startOffset));<!-- [et_pb_line_break_holder] -->        this.applyScrollProgress(normalized);<!-- [et_pb_line_break_holder] -->      };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      ['scroll', 'resize'].forEach(evt => window.addEventListener(evt, onScroll, { passive: true }));<!-- [et_pb_line_break_holder] -->      onScroll();<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ ---------- Data loading ----------<!-- [et_pb_line_break_holder] -->    async loadWorldData() {<!-- [et_pb_line_break_holder] -->      try {<!-- [et_pb_line_break_holder] -->        let response = await fetch('https:\/\/cdn.jsdelivr.net\/npm\/world-atlas@2\/countries-110m.json');<!-- [et_pb_line_break_holder] -->        if (!response.ok) {<!-- [et_pb_line_break_holder] -->          response = await fetch('https:\/\/raw.githubusercontent.com\/martynafford\/natural-earth-geojson\/master\/110m\/physical\/ne_110m_land.json');<!-- [et_pb_line_break_holder] -->          if (!response.ok) throw new Error('Failed to load country\/land data');<!-- [et_pb_line_break_holder] -->          this.landFeatures = await response.json();<!-- [et_pb_line_break_holder] -->        } else {<!-- [et_pb_line_break_holder] -->          const topo = await response.json();<!-- [et_pb_line_break_holder] -->          this.landFeatures = topojson.feature(topo, topo.objects.countries);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->          const [[minLng, minLat], [maxLng, maxLat]] = d3.geoBounds(feature);<!-- [et_pb_line_break_holder] -->          const lonSpan = maxLng - minLng;<!-- [et_pb_line_break_holder] -->          const spacing = lonSpan > 120 ? 16 : 22;<!-- [et_pb_line_break_holder] -->          const dots = this.generateDotsInPolygon(feature, spacing);<!-- [et_pb_line_break_holder] -->          dots.forEach(([lng, lat]) => this.allDots.push({ lng, lat, visible: true }));<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        await this.loadUniversitiesData();<!-- [et_pb_line_break_holder] -->        this.computeHighlightedFeatures();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        this.loadingOverlay.style.display = 'none';<!-- [et_pb_line_break_holder] -->        this.render();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      } catch (err) {<!-- [et_pb_line_break_holder] -->        this.loadingOverlay.innerHTML = `<!-- [et_pb_line_break_holder] -->          <\/p>\n<div class=\"error-state\"><!-- [et_pb_line_break_holder] -->            <\/p>\n<div class=\"error-title\">Error loading Earth visualization<\/div>\n<p><!-- [et_pb_line_break_holder] -->            <\/p>\n<div class=\"error-message\">${err?.message || 'Unknown error'}<\/div>\n<p><!-- [et_pb_line_break_holder] -->          <\/div>\n<p>`;<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    async loadUniversitiesData() {<!-- [et_pb_line_break_holder] -->      try {<!-- [et_pb_line_break_holder] -->        const res = await fetch('https:\/\/worldofstudents.org\/universities.json', { cache: 'no-cache' });<!-- [et_pb_line_break_holder] -->        if (!res.ok) throw new Error('Failed to fetch universities.json');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const data = await res.json();<!-- [et_pb_line_break_holder] -->        const items = data?.mainEntity?.itemListElement || [];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        const parsed = items<!-- [et_pb_line_break_holder] -->          .map(entry => entry?.item)<!-- [et_pb_line_break_holder] -->          .map(item => {<!-- [et_pb_line_break_holder] -->            const lat = parseFloat(item?.geo?.latitude);<!-- [et_pb_line_break_holder] -->            const lng = parseFloat(item?.geo?.longitude);<!-- [et_pb_line_break_holder] -->            if (!item?.name || !Number.isFinite(lat) || !Number.isFinite(lng)) return null;<!-- [et_pb_line_break_holder] -->            return { name: item.name, lat, lng, country: item?.address?.countryName || item?.address?.addressCountry || '' };<!-- [et_pb_line_break_holder] -->          })<!-- [et_pb_line_break_holder] -->          .filter(Boolean);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        this.universities = parsed.length<!-- [et_pb_line_break_holder] -->          ? parsed<!-- [et_pb_line_break_holder] -->          : universityData.map(item => ({ name: item.name, lat: parseFloat(item.lat), lng: parseFloat(item.lng), country: item.country }));<!-- [et_pb_line_break_holder] -->      } catch (err) {<!-- [et_pb_line_break_holder] -->        this.universities = universityData.map(item => ({ name: item.name, lat: parseFloat(item.lat), lng: parseFloat(item.lng), country: item.country }));<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    computeHighlightedFeatures() {<!-- [et_pb_line_break_holder] -->      this.highlightedFeatureIndices = new Set();<!-- [et_pb_line_break_holder] -->      if (!this.landFeatures?.features?.length || !this.universities?.length) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->        const [[minLng, minLat], [maxLng, maxLat]] = d3.geoBounds(feature);<!-- [et_pb_line_break_holder] -->        const hasUni = this.universities.some(uni => {<!-- [et_pb_line_break_holder] -->          if (uni.lng < minLng || uni.lng > maxLng || uni.lat < minLat || uni.lat > maxLat) return false;<!-- [et_pb_line_break_holder] -->          return this.pointInFeature([uni.lng, uni.lat], feature);<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->        if (hasUni) this.highlightedFeatureIndices.add(idx);<!-- [et_pb_line_break_holder] -->      });<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    loadLogo() {<!-- [et_pb_line_break_holder] -->      this.logoImage = new Image();<!-- [et_pb_line_break_holder] -->      this.logoImage.crossOrigin = 'anonymous';<!-- [et_pb_line_break_holder] -->      this.logoImage.src = 'https:\/\/worldofstudents.org\/wp-content\/uploads\/2025\/09\/WOS_Logo_white.png';<!-- [et_pb_line_break_holder] -->      this.logoImage.onload = () => this.render();<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/\/ ---------- Interactions ----------<!-- [et_pb_line_break_holder] -->    setupInteractions() {<!-- [et_pb_line_break_holder] -->      this.canvas.addEventListener('mousemove', (event) => this.handleMouseMove(event));<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    handleMouseMove(event) {<!-- [et_pb_line_break_holder] -->      const rect = this.canvas.getBoundingClientRect();<!-- [et_pb_line_break_holder] -->      const x = event.clientX - rect.left;<!-- [et_pb_line_break_holder] -->      const y = event.clientY - rect.top;<!-- [et_pb_line_break_holder] -->      let hoveredUni = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      const projection = (this.mode === 'flat' || this.mode === 'morphing') ? this.flatProjection : this.orthoProjection;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->        const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->        if (!projected) return;<!-- [et_pb_line_break_holder] -->        const dx = projected[0] - x;<!-- [et_pb_line_break_holder] -->        const dy = projected[1] - y;<!-- [et_pb_line_break_holder] -->        const distance = Math.sqrt(dx * dx + dy * dy);<!-- [et_pb_line_break_holder] -->        if (distance < 15) hoveredUni = uni;<!-- [et_pb_line_break_holder] -->      });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->      if (hoveredUni && this.tooltip) {<!-- [et_pb_line_break_holder] -->        this.tooltip.style.display = 'block';<!-- [et_pb_line_break_holder] -->        this.tooltip.style.left = `${x + 10}px`;<!-- [et_pb_line_break_holder] -->        this.tooltip.style.top = `${y + 10}px`;<!-- [et_pb_line_break_holder] -->        this.tooltip.innerHTML = `<strong>${hoveredUni.name}<\/strong><!\u2013- [et_pb_br_holder] -\u2013>${hoveredUni.country}`;<!-- [et_pb_line_break_holder] -->      } else if (this.tooltip) {<!-- [et_pb_line_break_holder] -->        this.tooltip.style.display = 'none';<!-- [et_pb_line_break_holder] -->      }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->  }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->  document.addEventListener('DOMContentLoaded', () => {<!-- [et_pb_line_break_holder] -->    const earth = new RotatingEarth('earth-canvas', { width: 1080, height: 540 });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    earth.autoplayBtn = document.getElementById('autoplay-btn');<!-- [et_pb_line_break_holder] -->    if (earth.autoplayBtn) {<!-- [et_pb_line_break_holder] -->      earth.autoplayBtn.addEventListener('click', () => earth.isAutoPlaying ? earth.stopAutoPlay?.() : earth.startAutoPlay?.());<!-- [et_pb_line_break_holder] -->      earth.autoplayBtn.textContent = 'Autoplay';<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    earth.autoRotate = false;<!-- [et_pb_line_break_holder] -->    earth.setupScrollTrigger(document.getElementById('earth-sticky-wrapper'), {<!-- [et_pb_line_break_holder] -->      startAtSticky: true,<!-- [et_pb_line_break_holder] -->      endOffset: 0.8<!-- [et_pb_line_break_holder] -->    });<!-- [et_pb_line_break_holder] -->  });<!-- [et_pb_line_break_holder] --><\/script>[\/et_pb_fullwidth_code][et_pb_fullwidth_code _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; hover_enabled=&#8221;0&#8243; sticky_enabled=&#8221;0&#8243; admin_label=&#8221;15-12-2025&#8243; disabled_on=&#8221;on|on|on&#8221; disabled=&#8221;on&#8221;]<script src=\"https:\/\/d3js.org\/d3.v7.min.js\"><\/script><!-- [et_pb_line_break_holder] --><script src=\"https:\/\/cdn.jsdelivr.net\/npm\/topojson-client@3\/dist\/topojson-client.min.js\"><\/script><!-- [et_pb_line_break_holder] --><\/p>\n<style><!-- [et_pb_line_break_holder] -->    html, body {<!-- [et_pb_line_break_holder] -->        margin: 0;<!-- [et_pb_line_break_holder] -->        padding: 0;<!-- [et_pb_line_break_holder] -->        height: 100%;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/* Wrapper defines how long the sticky canvas stays fixed *\/<!-- [et_pb_line_break_holder] -->    .earth-sticky-wrapper {<!-- [et_pb_line_break_holder] -->        position: relative;<!-- [et_pb_line_break_holder] -->        height: 600vh; \/* Extended for continent tours *\/<!-- [et_pb_line_break_holder] -->        max-width: 1080px;<!-- [et_pb_line_break_holder] -->        margin:0 auto;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .earth-container {<!-- [et_pb_line_break_holder] -->        position: sticky;<!-- [et_pb_line_break_holder] -->        top: 150px;<!-- [et_pb_line_break_holder] -->        width: 100%;<!-- [et_pb_line_break_holder] -->        max-width: 1080px;<!-- [et_pb_line_break_holder] -->        height: 75vh;<!-- [et_pb_line_break_holder] -->        margin: 0 auto;<!-- [et_pb_line_break_holder] -->        background: transparent;<!-- [et_pb_line_break_holder] -->        border-radius: 0;<!-- [et_pb_line_break_holder] -->        overflow: visible;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    #earth-canvas {<!-- [et_pb_line_break_holder] -->        width: 100%;<!-- [et_pb_line_break_holder] -->        height: 100%;<!-- [et_pb_line_break_holder] -->        display: block;<!-- [et_pb_line_break_holder] -->        background: transparent;<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        top: 0;<!-- [et_pb_line_break_holder] -->        left: 0;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .loading-overlay {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        top: 0;<!-- [et_pb_line_break_holder] -->        left: 0;<!-- [et_pb_line_break_holder] -->        right: 0;<!-- [et_pb_line_break_holder] -->        bottom: 0;<!-- [et_pb_line_break_holder] -->        background: rgba(0, 0, 0, 0.8);<!-- [et_pb_line_break_holder] -->        display: flex;<!-- [et_pb_line_break_holder] -->        align-items: center;<!-- [et_pb_line_break_holder] -->        justify-content: center;<!-- [et_pb_line_break_holder] -->        border-radius: 16px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .loading-text {<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 16px;<!-- [et_pb_line_break_holder] -->        font-weight: 500;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-state {<!-- [et_pb_line_break_holder] -->        padding: 32px;<!-- [et_pb_line_break_holder] -->        text-align: center;<!-- [et_pb_line_break_holder] -->        color: #ef4444;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-title {<!-- [et_pb_line_break_holder] -->        font-weight: 600;<!-- [et_pb_line_break_holder] -->        margin-bottom: 8px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-message {<!-- [et_pb_line_break_holder] -->        color: #999999;<!-- [et_pb_line_break_holder] -->        font-size: 14px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .spinner {<!-- [et_pb_line_break_holder] -->        width: 24px;<!-- [et_pb_line_break_holder] -->        height: 24px;<!-- [et_pb_line_break_holder] -->        border: 2px solid #333333;<!-- [et_pb_line_break_holder] -->        border-top: 2px solid #ffffff;<!-- [et_pb_line_break_holder] -->        border-radius: 50%;<!-- [et_pb_line_break_holder] -->        animation: spin 1s linear infinite;<!-- [et_pb_line_break_holder] -->        margin-right: 12px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    @keyframes spin {<!-- [et_pb_line_break_holder] -->        0% { transform: rotate(0deg); }<!-- [et_pb_line_break_holder] -->        100% { transform: rotate(360deg); }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .phase-text {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        left: 50%;<!-- [et_pb_line_break_holder] -->        bottom: 36%;<!-- [et_pb_line_break_holder] -->        transform: translate(-50%, 12px);<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 18px;<!-- [et_pb_line_break_holder] -->        font-weight: 700;<!-- [et_pb_line_break_holder] -->        letter-spacing: 0.4px;<!-- [et_pb_line_break_holder] -->        text-shadow: 0 2px 10px rgba(0,0,0,0.55);<!-- [et_pb_line_break_holder] -->        opacity: 0;<!-- [et_pb_line_break_holder] -->        transition: opacity 0.35s ease, transform 0.35s ease;<!-- [et_pb_line_break_holder] -->        pointer-events: none;<!-- [et_pb_line_break_holder] -->        max-width: 84%;<!-- [et_pb_line_break_holder] -->        text-align: center;<!-- [et_pb_line_break_holder] -->        line-height: 1.45;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .phase-text.visible {<!-- [et_pb_line_break_holder] -->        opacity: 1;<!-- [et_pb_line_break_holder] -->        transform: translate(-50%, 0);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .autoplay-btn {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        right: 32px;<!-- [et_pb_line_break_holder] -->        top: 32px;<!-- [et_pb_line_break_holder] -->        padding: 10px 14px;<!-- [et_pb_line_break_holder] -->        border-radius: 12px;<!-- [et_pb_line_break_holder] -->        border: 1px solid rgba(255,255,255,0.3);<!-- [et_pb_line_break_holder] -->        background: rgba(0,0,0,0.45);<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 13px;<!-- [et_pb_line_break_holder] -->        font-weight: 600;<!-- [et_pb_line_break_holder] -->        cursor: pointer;<!-- [et_pb_line_break_holder] -->        backdrop-filter: blur(6px);<!-- [et_pb_line_break_holder] -->        transition: background 0.2s ease, transform 0.2s ease, opacity 0.2s ease;<!-- [et_pb_line_break_holder] -->        z-index: 5;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .autoplay-btn:hover {<!-- [et_pb_line_break_holder] -->        background: rgba(0,0,0,0.65);<!-- [et_pb_line_break_holder] -->        transform: translateY(-1px);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .autoplay-btn:active {<!-- [et_pb_line_break_holder] -->        transform: translateY(0);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><\/style>\n<p><!-- [et_pb_line_break_holder] --><\/p>\n<div class=\"earth-sticky-wrapper\" id=\"earth-sticky-wrapper\"><!-- [et_pb_line_break_holder] -->    <\/p>\n<div class=\"earth-container\"><!-- [et_pb_line_break_holder] -->        <canvas id=\"earth-canvas\"><\/canvas><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"loading-overlay\" class=\"loading-overlay\"><!-- [et_pb_line_break_holder] -->            <\/p>\n<div class=\"spinner\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->            <\/p>\n<div class=\"loading-text\">Loading Earth data&#8230;<\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"tooltip\" style=\"position: absolute; display: none; background: rgba(0,0,0,0.8); color: white; padding: 5px; border-radius: 3px; pointer-events: none; z-index: 1000;\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"phase-text\" class=\"phase-text\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->        <button id=\"autoplay-btn\" class=\"autoplay-btn\">Autoplay<\/button><!-- [et_pb_line_break_holder] -->    <\/div>\n<p><!-- [et_pb_line_break_holder] --><\/div>\n<p><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --><script><!-- [et_pb_line_break_holder] -->    \/\/ Fallback dataset used if the remote universities.json cannot be loaded<!-- [et_pb_line_break_holder] -->    const universityData = [<!-- [et_pb_line_break_holder] -->        {\"name\":\"Vancouver Island University\",\"lat\":\"49.1578851\",\"lng\":\"-123.9656421\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"International College of Management, Sydney (ICMS)\",\"lat\":\"-33.8040340\",\"lng\":\"151.2937461\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of North Carolina Wilmington\",\"lat\":\"34.2249827\",\"lng\":\"-77.8690774\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Griffith College Dublin\",\"lat\":\"53.3311882\",\"lng\":\"-6.2802781\",\"country\":\"Ireland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EUSA University Centre, Seville, Spain\",\"lat\":\"37.3754972\",\"lng\":\"-5.9806833\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Cesine Santander\",\"lat\":\"43.4715066\",\"lng\":\"-3.7915862\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Asia Pacific University of Technology & Innovation (APU)\",\"lat\":\"3.0479178\",\"lng\":\"101.6892466\",\"country\":\"Malaysia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Francisco State University\",\"lat\":\"37.7245167\",\"lng\":\"-122.4799957\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"International Business School Budapest\",\"lat\":\"47.5629570\",\"lng\":\"19.0541941\",\"country\":\"Hungary\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EU Business School Barcelona\",\"lat\":\"41.3825802\",\"lng\":\"2.1770730\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Institut Sup\u00e9rieur de Gestion (ISG) Paris\",\"lat\":\"48.8672799\",\"lng\":\"2.2758497\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Woosong University\",\"lat\":\"36.3392944\",\"lng\":\"127.4506140\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"SolBridge International School of Business\",\"lat\":\"36.3383278\",\"lng\":\"127.4324307\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Europea de Madrid\",\"lat\":\"40.3727017\",\"lng\":\"-3.9185823\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"St. Francis College New York\",\"lat\":\"40.6900603\",\"lng\":\"-73.9865997\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universitat Aut\u00f2noma de Barcelona\",\"lat\":\"41.5015527\",\"lng\":\"2.1062607\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of the Fraser Valley\",\"lat\":\"49.0283443\",\"lng\":\"-122.2849135\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad San Ignacio de Loyola\",\"lat\":\"-12.0734419\",\"lng\":\"-76.9484914\",\"country\":\"Peru\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Francisco de Vitoria Madrid\",\"lat\":\"40.4402647\",\"lng\":\"-3.8339056\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"American College of Thessaloniki\",\"lat\":\"40.5684100\",\"lng\":\"22.9912375\",\"country\":\"Greece\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Hankuk University of Foreign Studies\",\"lat\":\"37.5970815\",\"lng\":\"127.0587413\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Vrije Universiteit Amsterdam\",\"lat\":\"52.3340539\",\"lng\":\"4.8651882\",\"country\":\"Netherlands\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Austral\",\"lat\":\"-34.5934652\",\"lng\":\"-58.3833079\",\"country\":\"Argentina\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad de Alicante\",\"lat\":\"38.3850684\",\"lng\":\"-0.5146732\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universit\u00e0 Europea di Roma\",\"lat\":\"41.8791802\",\"lng\":\"12.3946472\",\"country\":\"Italy\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Cat\u00f3lica San Antonio de Murcia\",\"lat\":\"38.0042077\",\"lng\":\"-1.1774782\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Xi'an Jiaotong-Liverpool University\",\"lat\":\"31.2787148\",\"lng\":\"120.7237629\",\"country\":\"China\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universit\u00e0 Cattolica del Sacro Cuore\",\"lat\":\"45.4609998\",\"lng\":\"9.1769914\",\"country\":\"Italy\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"College of the Desert\",\"lat\":\"33.7324666\",\"lng\":\"-116.3868288\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Santa Barbara City College\",\"lat\":\"34.4058643\",\"lng\":\"-119.6974204\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Victoria University of Wellington\",\"lat\":\"-41.2793942\",\"lng\":\"174.7783525\",\"country\":\"New Zealand\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"City University of Seattle\",\"lat\":\"47.6177084\",\"lng\":\"-122.3444624\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of the Sunshine Coast (UniSC)\",\"lat\":\"-26.7159208\",\"lng\":\"153.0563306\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of Brighton\",\"lat\":\"50.8421128\",\"lng\":\"-0.1194002\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"London South Bank University\",\"lat\":\"51.4991489\",\"lng\":\"-0.0970188\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Dorset College Dublin\",\"lat\":\"53.3582148\",\"lng\":\"-6.2577212\",\"country\":\"Ireland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"BI Norwegian Business School\",\"lat\":\"59.9488700\",\"lng\":\"10.7682070\",\"country\":\"Norway\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EU Business School Geneva\",\"lat\":\"46.2067776\",\"lng\":\"6.1451264\",\"country\":\"Switzerland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"London Metropolitan University\",\"lat\":\"51.5522032\",\"lng\":\"-0.1111950\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Adolfo Ibanez\",\"lat\":\"-33.0196718\",\"lng\":\"-71.5304035\",\"country\":\"Chile\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Jose State University\",\"lat\":\"37.3351902\",\"lng\":\"-121.8812255\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Europea de Valencia\",\"lat\":\"39.4755093\",\"lng\":\"-0.3652411\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Latinoamericana de Cienca y Tecnologia\",\"lat\":\"9.9325427\",\"lng\":\"-84.0795782\",\"country\":\"Costa Rica\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Diego State University\",\"lat\":\"32.7760640\",\"lng\":\"-117.0702300\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Bond University\",\"lat\":\"-28.0724817\",\"lng\":\"153.4157960\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California San Diego\",\"lat\":\"32.8792438\",\"lng\":\"-117.2311247\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Alliant International University\",\"lat\":\"32.8966666\",\"lng\":\"-117.0938567\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"James Cook University Singapore\",\"lat\":\"1.3153627\",\"lng\":\"103.8760268\",\"country\":\"Singapore\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Capilano University\",\"lat\":\"49.3179806\",\"lng\":\"-123.0194770\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"James Cook University Australia\",\"lat\":\"-19.3293891\",\"lng\":\"146.7611734\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Nebrija University\",\"lat\":\"40.4295684\",\"lng\":\"-3.7130672\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California Santa Barbara\",\"lat\":\"34.4145523\",\"lng\":\"-119.8468383\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California Berkeley Extension\",\"lat\":\"37.8708393\",\"lng\":\"-122.2728630\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Westcliff University\",\"lat\":\"33.6853732\",\"lng\":\"-117.8480110\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"The University of Sydney\",\"lat\":\"-33.8854217\",\"lng\":\"151.1890168\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California, Los Angeles (UCLA) Extension\",\"lat\":\"34.0595933\",\"lng\":\"-118.4463883\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Auckland University of Technology\",\"lat\":\"-36.8525224\",\"lng\":\"174.7667714\",\"country\":\"New Zealand\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Pace University\",\"lat\":\"40.7111050\",\"lng\":\"-74.0046134\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Edinburgh Napier University\",\"lat\":\"55.9244726\",\"lng\":\"-3.2888614\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of Westminster\",\"lat\":\"51.5209064\",\"lng\":\"-0.1400730\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"IPAG Business School Paris\",\"lat\":\"48.8544327\",\"lng\":\"2.3312200\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Veritas\",\"lat\":\"9.9254713\",\"lng\":\"-84.0648763\",\"country\":\"Costa Rica\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"IPAG Business School Nice\",\"lat\":\"43.7145200\",\"lng\":\"7.2652823\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Royal Melbourne Institute of Technology Vietnam\",\"lat\":\"10.7755254\",\"lng\":\"106.7021047\",\"country\":\"Viet Nam\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Curtin University Dubai\",\"lat\":\"25.0742823\",\"lng\":\"55.1885387\",\"country\":\"United Arab Emirates\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Curtin University Singapore\",\"lat\":\"1.2884206\",\"lng\":\"103.7795669\",\"country\":\"Singapore\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"LCI Barcelona\",\"lat\":\"41.3994905\",\"lng\":\"2.1902139\",\"country\":\"Spain\"}<!-- [et_pb_line_break_holder] -->    ];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    class RotatingEarth {<!-- [et_pb_line_break_holder] -->        constructor(canvasId, options = {}) {<!-- [et_pb_line_break_holder] -->            this.canvas = document.getElementById(canvasId);<!-- [et_pb_line_break_holder] -->            this.context = this.canvas.getContext('2d');<!-- [et_pb_line_break_holder] -->            this.loadingOverlay = document.getElementById('loading-overlay');<!-- [et_pb_line_break_holder] -->            this.tooltip = document.getElementById('tooltip');<!-- [et_pb_line_break_holder] -->            this.phaseText = document.getElementById('phase-text');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.width = options.width || 800;<!-- [et_pb_line_break_holder] -->            this.height = options.height || 600;<!-- [et_pb_line_break_holder] -->            this.isLoading = true;<!-- [et_pb_line_break_holder] -->            this.error = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.allDots = [];<!-- [et_pb_line_break_holder] -->            this.landFeatures = null;<!-- [et_pb_line_break_holder] -->            this.universities = [];<!-- [et_pb_line_break_holder] -->            this.highlightedFeatureIndices = new Set();<!-- [et_pb_line_break_holder] -->            this.initialRotation = [0, -38];<!-- [et_pb_line_break_holder] -->            this.rotation = [...this.initialRotation];<!-- [et_pb_line_break_holder] -->            this.scrollBaseRotation = this.rotation[0];<!-- [et_pb_line_break_holder] -->            this.autoRotate = true;<!-- [et_pb_line_break_holder] -->            this.rotationSpeed = 0.25;<!-- [et_pb_line_break_holder] -->            this.rotationTimer = null;<!-- [et_pb_line_break_holder] -->            this.logoImage = null;<!-- [et_pb_line_break_holder] -->            this.isInteracting = false;<!-- [et_pb_line_break_holder] -->            this.bgColor = '#005499';<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->            this.autoPlayStart = 0;<!-- [et_pb_line_break_holder] -->            this.autoPlayDuration = 24000; \/\/ ms<!-- [et_pb_line_break_holder] -->            this.autoPlayHandle = null;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'globe';<!-- [et_pb_line_break_holder] -->            this.orthoProjection = null;<!-- [et_pb_line_break_holder] -->            this.flatProjection = null;<!-- [et_pb_line_break_holder] -->            this.path = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.graticuleGeo = d3.geoGraticule().step([15, 15])(); \/\/ MultiLineString<!-- [et_pb_line_break_holder] -->            this.sphereBoundary = this.buildSphereBoundaryCoords();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Continent tour configuration<!-- [et_pb_line_break_holder] -->            this.continentTours = [<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'Europe',<!-- [et_pb_line_break_holder] -->                    lng: 10,<!-- [et_pb_line_break_holder] -->                    lat: 50,<!-- [et_pb_line_break_holder] -->                    zoom: 2.5,<!-- [et_pb_line_break_holder] -->                    latRange: [35, 72],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[-25, 60]],<!-- [et_pb_line_break_holder] -->                    countries: [<!-- [et_pb_line_break_holder] -->                        'austria','belgium','bulgaria','croatia','cyprus','czechia','czech republic',<!-- [et_pb_line_break_holder] -->                        'denmark','estonia','finland','france','germany','greece','hungary','iceland',<!-- [et_pb_line_break_holder] -->                        'ireland','italy','latvia','lithuania','luxembourg','malta','netherlands',<!-- [et_pb_line_break_holder] -->                        'norway','poland','portugal','romania','slovakia','slovenia','spain','sweden',<!-- [et_pb_line_break_holder] -->                        'switzerland','united kingdom','uk','england','scotland','wales','northern ireland'<!-- [et_pb_line_break_holder] -->                    ]<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'North America',<!-- [et_pb_line_break_holder] -->                    lng: -100,<!-- [et_pb_line_break_holder] -->                    lat: 40,<!-- [et_pb_line_break_holder] -->                    zoom: 2.2,<!-- [et_pb_line_break_holder] -->                    latRange: [7, 83],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[-170, -50]],<!-- [et_pb_line_break_holder] -->                    countries: ['canada','united states','united states of america','usa','us']<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'South America',<!-- [et_pb_line_break_holder] -->                    lng: -60,<!-- [et_pb_line_break_holder] -->                    lat: -20,<!-- [et_pb_line_break_holder] -->                    zoom: 2.3,<!-- [et_pb_line_break_holder] -->                    latRange: [-60, 15],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[-90, -30]],<!-- [et_pb_line_break_holder] -->                    countries: ['argentina','bolivia','brazil','chile','colombia','ecuador','guyana','paraguay','peru','suriname','uruguay','venezuela', 'costa rica']<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'Asia',<!-- [et_pb_line_break_holder] -->                    lng: 100,<!-- [et_pb_line_break_holder] -->                    lat: 30,<!-- [et_pb_line_break_holder] -->                    zoom: 2.2,<!-- [et_pb_line_break_holder] -->                    latRange: [-15, 80],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[35, 180], [-180, -100]],<!-- [et_pb_line_break_holder] -->                    countries: [<!-- [et_pb_line_break_holder] -->                        'china','japan','south korea','korea','republic of korea','north korea','taiwan',<!-- [et_pb_line_break_holder] -->                        'hong kong','macau','india','pakistan','bangladesh','sri lanka','nepal','bhutan',<!-- [et_pb_line_break_holder] -->                        'maldives','indonesia','malaysia','philippines','vietnam', 'viet nam', 'thailand','laos','cambodia',<!-- [et_pb_line_break_holder] -->                        'myanmar','brunei','singapore','mongolia','kazakhstan','kyrgyzstan','uzbekistan',<!-- [et_pb_line_break_holder] -->                        'tajikistan','turkmenistan','afghanistan','iran','iraq','saudi arabia','united arab emirates',<!-- [et_pb_line_break_holder] -->                        'uae','qatar','kuwait','bahrain','oman','yemen','jordan','lebanon','israel','palestine',<!-- [et_pb_line_break_holder] -->                        'turkey','georgia','armenia','azerbaijan'<!-- [et_pb_line_break_holder] -->                    ]<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'Oceania',<!-- [et_pb_line_break_holder] -->                    lng: 150,<!-- [et_pb_line_break_holder] -->                    lat: -25,<!-- [et_pb_line_break_holder] -->                    zoom: 2.5,<!-- [et_pb_line_break_holder] -->                    latRange: [-50, 10],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[110, 190], [-190, -140]],<!-- [et_pb_line_break_holder] -->                    countries: ['australia','new zealand','fiji','papua new guinea','samoa','tonga','vanuatu','solomon islands','new caledonia']<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            ];<!-- [et_pb_line_break_holder] -->            this._continentCounts = new Map();<!-- [et_pb_line_break_holder] -->            this.currentTourIndex = -1;<!-- [et_pb_line_break_holder] -->            this.baseFlatScale = null;<!-- [et_pb_line_break_holder] -->            this.showLabels = false;<!-- [et_pb_line_break_holder] -->            this.currentZoom = 1;<!-- [et_pb_line_break_holder] -->            this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->            this.zoomPhaseProgress = 0;<!-- [et_pb_line_break_holder] -->            this.zoomPhaseProgress = 0; \/\/ 0 at zoom start, 1 at end to fade graticule<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.init();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        init() {<!-- [et_pb_line_break_holder] -->            this.setupCanvas();<!-- [et_pb_line_break_holder] -->            this.setupProjection();<!-- [et_pb_line_break_holder] -->            this.setupInteractions();<!-- [et_pb_line_break_holder] -->            window.addEventListener('resize', () => this.handleResize(), { passive: true });<!-- [et_pb_line_break_holder] -->            this.loadWorldData();<!-- [et_pb_line_break_holder] -->            this.loadLogo();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        getDefaultContainerSize() {<!-- [et_pb_line_break_holder] -->            const containerWidth = Math.min(this.width, Math.max(320, window.innerWidth - 40));<!-- [et_pb_line_break_holder] -->            const maxHeight = Math.min(<!-- [et_pb_line_break_holder] -->                this.height,<!-- [et_pb_line_break_holder] -->                window.innerHeight - 40,<!-- [et_pb_line_break_holder] -->                containerWidth * 0.55 \/\/ align closer to final flat map height<!-- [et_pb_line_break_holder] -->            );<!-- [et_pb_line_break_holder] -->            const containerHeight = Math.max(320, maxHeight);<!-- [et_pb_line_break_holder] -->            return { containerWidth, containerHeight };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setCanvasSize(containerWidth, containerHeight, dprOverride) {<!-- [et_pb_line_break_holder] -->            const dpr = dprOverride ?? Math.min(3, window.devicePixelRatio || 2);<!-- [et_pb_line_break_holder] -->            this.dpr = dpr;<!-- [et_pb_line_break_holder] -->            \/\/ Smaller globe radius so the start view sits comfortably in the container<!-- [et_pb_line_break_holder] -->            this.radius = Math.min(containerWidth, containerHeight) \/ 3.0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.canvas.width = containerWidth * dpr;<!-- [et_pb_line_break_holder] -->            this.canvas.height = containerHeight * dpr;<!-- [et_pb_line_break_holder] -->            this.canvas.style.width = `${containerWidth}px`;<!-- [et_pb_line_break_holder] -->            this.canvas.style.height = `${containerHeight}px`;<!-- [et_pb_line_break_holder] -->            this.context.setTransform(dpr, 0, 0, dpr, 0, 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.containerWidth = containerWidth;<!-- [et_pb_line_break_holder] -->            this.containerHeight = containerHeight;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.setupProjection();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupCanvas() {<!-- [et_pb_line_break_holder] -->            const { containerWidth, containerHeight } = this.getDefaultContainerSize();<!-- [et_pb_line_break_holder] -->            this.setCanvasSize(containerWidth, containerHeight);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        sizeFactor() {<!-- [et_pb_line_break_holder] -->            return window.innerWidth < 768 ? 0.7 : 1;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleResize() {<!-- [et_pb_line_break_holder] -->            if (this.isRecording) return; \/\/ avoid disrupting active capture<!-- [et_pb_line_break_holder] -->            const { containerWidth, containerHeight } = this.getDefaultContainerSize();<!-- [et_pb_line_break_holder] -->            const oldBaseScale = this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.setCanvasSize(containerWidth, containerHeight);<!-- [et_pb_line_break_holder] -->            \/\/ Preserve relative zoom if we're in a continent tour<!-- [et_pb_line_break_holder] -->            if (oldBaseScale && this.mode === 'flat') {<!-- [et_pb_line_break_holder] -->                const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->                const zoomRatio = currentScale \/ oldBaseScale;<!-- [et_pb_line_break_holder] -->                this.flatProjection.scale(this.baseFlatScale * zoomRatio);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupProjection() {<!-- [et_pb_line_break_holder] -->            this.orthoProjection = d3.geoOrthographic()<!-- [et_pb_line_break_holder] -->                .scale(this.radius)<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2])<!-- [et_pb_line_break_holder] -->                .clipAngle(90)<!-- [et_pb_line_break_holder] -->                .rotate(this.rotation);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const flatScale = (this.containerWidth \/ (2 * Math.PI)) * 0.9;<!-- [et_pb_line_break_holder] -->            this.baseFlatScale = flatScale;<!-- [et_pb_line_break_holder] -->            this.flatProjection = d3.geoEquirectangular()<!-- [et_pb_line_break_holder] -->                .center([0, 0])<!-- [et_pb_line_break_holder] -->                .scale(flatScale)<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.path = d3.geoPath()<!-- [et_pb_line_break_holder] -->                .projection(this.orthoProjection)<!-- [et_pb_line_break_holder] -->                .context(this.context);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        pointInPolygon(point, polygon) {<!-- [et_pb_line_break_holder] -->            const [x, y] = point;<!-- [et_pb_line_break_holder] -->            let inside = false;<!-- [et_pb_line_break_holder] -->            for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {<!-- [et_pb_line_break_holder] -->                const [xi, yi] = polygon[i];<!-- [et_pb_line_break_holder] -->                const [xj, yj] = polygon[j];<!-- [et_pb_line_break_holder] -->                if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) \/ (yj - yi) + xi) {<!-- [et_pb_line_break_holder] -->                    inside = !inside;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return inside;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        pointInFeature(point, feature) {<!-- [et_pb_line_break_holder] -->            const geometry = feature.geometry;<!-- [et_pb_line_break_holder] -->            if (geometry.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                const coordinates = geometry.coordinates;<!-- [et_pb_line_break_holder] -->                if (!this.pointInPolygon(point, coordinates[0])) return false;<!-- [et_pb_line_break_holder] -->                for (let i = 1; i < coordinates.length; i++) {<!-- [et_pb_line_break_holder] -->                    if (this.pointInPolygon(point, coordinates[i])) return false;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                return true;<!-- [et_pb_line_break_holder] -->            } else if (geometry.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                for (const polygon of geometry.coordinates) {<!-- [et_pb_line_break_holder] -->                    if (this.pointInPolygon(point, polygon[0])) {<!-- [et_pb_line_break_holder] -->                        let inHole = false;<!-- [et_pb_line_break_holder] -->                        for (let i = 1; i < polygon.length; i++) {<!-- [et_pb_line_break_holder] -->                            if (this.pointInPolygon(point, polygon[i])) {<!-- [et_pb_line_break_holder] -->                                inHole = true;<!-- [et_pb_line_break_holder] -->                                break;<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                        if (!inHole) return true;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                return false;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return false;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        generateDotsInPolygon(feature, dotSpacing = 16) {<!-- [et_pb_line_break_holder] -->            \/\/ More robust sampler that handles antimeridian-crossing countries (e.g., Russia)<!-- [et_pb_line_break_holder] -->            const dots = [];<!-- [et_pb_line_break_holder] -->            const stepSize = Math.max(0.5, dotSpacing * 0.08); \/\/ cap max step to keep large countries filled<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const normLng = (lng) => {<!-- [et_pb_line_break_holder] -->                const n = ((lng + 180) % 360 + 360) % 360 - 180;<!-- [et_pb_line_break_holder] -->                return n === -180 ? 180 : n; \/\/ keep boundaries consistent<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const samplePolygon = (rings) => {<!-- [et_pb_line_break_holder] -->                const lons = rings.flat().map(([lng]) => normLng(lng));<!-- [et_pb_line_break_holder] -->                const lats = rings.flat().map(([, lat]) => lat);<!-- [et_pb_line_break_holder] -->                const minLng = Math.min(...lons);<!-- [et_pb_line_break_holder] -->                const maxLng = Math.max(...lons);<!-- [et_pb_line_break_holder] -->                const minLat = Math.min(...lats);<!-- [et_pb_line_break_holder] -->                const maxLat = Math.max(...lats);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const crossesDateline = maxLng - minLng > 180;<!-- [et_pb_line_break_holder] -->                const ranges = crossesDateline ? [[-180, 0], [0, 180]] : [[minLng, maxLng]];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                ranges.forEach(([lngStart, lngEnd]) => {<!-- [et_pb_line_break_holder] -->                    for (let lng = lngStart; lng <= lngEnd; lng += stepSize) {<!-- [et_pb_line_break_holder] -->                        for (let lat = minLat; lat <= maxLat; lat += stepSize) {<!-- [et_pb_line_break_holder] -->                            \/\/ Try with normalized and shifted longitudes so dateline polygons still hit<!-- [et_pb_line_break_holder] -->                            const candidates = [<!-- [et_pb_line_break_holder] -->                                [lng, lat],<!-- [et_pb_line_break_holder] -->                                [lng + 360, lat],<!-- [et_pb_line_break_holder] -->                                [lng - 360, lat]<!-- [et_pb_line_break_holder] -->                            ];<!-- [et_pb_line_break_holder] -->                            if (candidates.some((p) => d3.geoContains(feature, p))) {<!-- [et_pb_line_break_holder] -->                                dots.push([lng, lat]);<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->            if (!geom) return dots;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                samplePolygon(geom.coordinates);<!-- [et_pb_line_break_holder] -->            } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                geom.coordinates.forEach((poly) => samplePolygon(poly));<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            return dots;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        render() {<!-- [et_pb_line_break_holder] -->            this.context.clearRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            this.context.save();<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = this.bgColor || '#ffffff';<!-- [et_pb_line_break_holder] -->            this.context.fillRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            this.context.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.mode === 'globe') {<!-- [et_pb_line_break_holder] -->                this.renderGlobe();<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                this.renderFlat();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        isZoomingFlat() {<!-- [et_pb_line_break_holder] -->            return this.mode === 'flat' && this.zoomPhaseProgress > 0.01;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        angularDistance(lon1, lat1, lon2, lat2) {<!-- [et_pb_line_break_holder] -->            const toRad = (d) => (d * Math.PI) \/ 180;<!-- [et_pb_line_break_holder] -->            const \u03c61 = toRad(lat1);<!-- [et_pb_line_break_holder] -->            const \u03c62 = toRad(lat2);<!-- [et_pb_line_break_holder] -->            const \u0394\u03bb = toRad(lon2 - lon1);<!-- [et_pb_line_break_holder] -->            const sinDLat = Math.sin((\u03c62 - \u03c61) \/ 2);<!-- [et_pb_line_break_holder] -->            const sinDLon = Math.sin(\u0394\u03bb \/ 2);<!-- [et_pb_line_break_holder] -->            const a = sinDLat * sinDLat + Math.cos(\u03c61) * Math.cos(\u03c62) * sinDLon * sinDLon;<!-- [et_pb_line_break_holder] -->            return 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderGlobe() {<!-- [et_pb_line_break_holder] -->            const projection = this.orthoProjection;<!-- [et_pb_line_break_holder] -->            this.path.projection(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentScale = projection.scale();<!-- [et_pb_line_break_holder] -->            const scaleFactor = currentScale \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] -->            const centerX = this.containerWidth \/ 2;<!-- [et_pb_line_break_holder] -->            const centerY = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] -->            const centerCoord = projection.invert([centerX, centerY]);<!-- [et_pb_line_break_holder] -->            const isFront = (lon, lat) => d3.geoDistance([lon, lat], centerCoord) <= Math.PI \/ 2;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            projection.clipAngle(180);<!-- [et_pb_line_break_holder] -->            this.context.save();<!-- [et_pb_line_break_holder] -->            this.context.filter = 'blur(1px)';<!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.globalAlpha = 0.20;<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            this.context.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            projection.clipAngle(90);<!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->            this.context.lineWidth = 2 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->            this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                projection.clipAngle(180);<!-- [et_pb_line_break_holder] -->                const graticuleBack = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticuleBack());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.14 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.30;<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    this.context.beginPath();<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                    this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    this.context.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.18;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                projection.clipAngle(90);<!-- [et_pb_line_break_holder] -->                const graticuleFront = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticuleFront());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.18 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const dotRadius = 0.6 * sizeFactor; \/\/ keep dots small and consistent<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected &&<!-- [et_pb_line_break_holder] -->                        projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                        projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                        if (isFront(dot.lng, dot.lat)) {<!-- [et_pb_line_break_holder] -->                            const dx = projected[0] - centerX;<!-- [et_pb_line_break_holder] -->                            const dy = projected[1] - centerY;<!-- [et_pb_line_break_holder] -->                            const dist = Math.sqrt(dx * dx + dy * dy);<!-- [et_pb_line_break_holder] -->                            if (dist <= currentScale) {<!-- [et_pb_line_break_holder] -->                                const opacity = (90 - Math.abs(dot.lat)) \/ 90;<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = opacity;<!-- [et_pb_line_break_holder] -->                                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                                this.context.arc(projected[0], projected[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                                this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                                this.context.fill();<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.universities.forEach((uni) => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected &&<!-- [et_pb_line_break_holder] -->                        projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                        projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                        this.context.shadowColor = 'rgba(0,106,179,0.6)';<!-- [et_pb_line_break_holder] -->                        this.context.shadowBlur = 3 * scaleFactor;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetX = 2 * scaleFactor;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetY = 2 * scaleFactor;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                        this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetX = 0;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetY = 0;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.logoImage && this.logoImage.complete) {<!-- [et_pb_line_break_holder] -->                this.context.save();<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.context.arc(this.containerWidth \/ 2, this.containerHeight \/ 2, projection.scale(), 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                this.context.clip();<!-- [et_pb_line_break_holder] -->                if (this.isInteracting) {<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = 0.25;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                const logoSize = 2.2 * projection.scale();<!-- [et_pb_line_break_holder] -->                const x = this.containerWidth \/ 2 - logoSize \/ 2;<!-- [et_pb_line_break_holder] -->                const y = this.containerHeight \/ 2 - logoSize \/ 2;<!-- [et_pb_line_break_holder] -->                this.context.drawImage(this.logoImage, x, y, logoSize, logoSize);<!-- [et_pb_line_break_holder] -->                this.context.restore();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderFlat() {<!-- [et_pb_line_break_holder] -->            const projection = this.flatProjection;<!-- [et_pb_line_break_holder] -->            this.path.projection(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentScale = projection.scale();<!-- [et_pb_line_break_holder] -->            const scaleFactor = currentScale \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            \/\/ Only draw border when not in zooming flat tour phase<!-- [et_pb_line_break_holder] -->            if (!this.isZoomingFlat()) {<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                const graticule = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticule());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.8 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.4 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Fills with highlight like morph<!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.35;<!-- [et_pb_line_break_holder] -->                this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    this.context.beginPath();<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    this.context.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const dotRadius = 0.6 * sizeFactor; \/\/ consistent size<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Draw markers (consistent size across animation)<!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                const markerBoxes = [];<!-- [et_pb_line_break_holder] -->                const placements = [];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Draw markers and collect their boxes<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                        \/\/ store small box for overlap checks<!-- [et_pb_line_break_holder] -->                        const r = markerRadius + 2;<!-- [et_pb_line_break_holder] -->                        const box = {<!-- [et_pb_line_break_holder] -->                            x: projected[0] - r,<!-- [et_pb_line_break_holder] -->                            y: projected[1] - r,<!-- [et_pb_line_break_holder] -->                            w: r * 2,<!-- [et_pb_line_break_holder] -->                            h: r * 2<!-- [et_pb_line_break_holder] -->                        };<!-- [et_pb_line_break_holder] -->                        markerBoxes.push(box);<!-- [et_pb_line_break_holder] -->                        placements.push({<!-- [et_pb_line_break_holder] -->                            uni,<!-- [et_pb_line_break_holder] -->                            cx: projected[0],<!-- [et_pb_line_break_holder] -->                            cy: projected[1],<!-- [et_pb_line_break_holder] -->                            markerBox: box<!-- [et_pb_line_break_holder] -->                        });<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ University markers (labels removed per request)<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Country labels for highlighted countries (only during zoom\/tour)<!-- [et_pb_line_break_holder] -->                if (this.showLabels && this.activeContinentIdx != null) {<!-- [et_pb_line_break_holder] -->                    const countryFontSize = 12 * sizeFactor;<!-- [et_pb_line_break_holder] -->                    this.context.font = `600 ${countryFontSize}px \"Inter\", \"Segoe UI\", sans-serif`;<!-- [et_pb_line_break_holder] -->                    this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    this.context.textBaseline = 'middle';<!-- [et_pb_line_break_holder] -->                    this.context.textAlign = 'left';<!-- [et_pb_line_break_holder] -->                    this.context.shadowColor = 'rgba(0,0,0,0.55)';<!-- [et_pb_line_break_holder] -->                    this.context.shadowBlur = 6;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const placedCountry = [...markerBoxes]; \/\/ avoid overlapping markers<!-- [et_pb_line_break_holder] -->                    const intersects = (a, b) => !(<!-- [et_pb_line_break_holder] -->                        a.x + a.w < b.x ||<!-- [et_pb_line_break_holder] -->                        b.x + b.w < a.x ||<!-- [et_pb_line_break_holder] -->                        a.y + a.h < b.y ||<!-- [et_pb_line_break_holder] -->                        b.y + b.h < a.y<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const gaps = [0, 12, 24, 36, 48, 60];<!-- [et_pb_line_break_holder] -->                    const directions = [<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: 0 },   \/\/ right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: 0 },  \/\/ left<!-- [et_pb_line_break_holder] -->                        { dx: 0, dy: -1 },  \/\/ up<!-- [et_pb_line_break_holder] -->                        { dx: 0, dy: 1 },   \/\/ down<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: -1 },  \/\/ up-right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: -1 }, \/\/ up-left<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: 1 },   \/\/ down-right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: 1 },  \/\/ down-left<!-- [et_pb_line_break_holder] -->                    ];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const margin = 8;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const activeContinent = this.continentTours[this.activeContinentIdx];<!-- [et_pb_line_break_holder] -->                    const maxDistRad = Math.PI * 0.35; \/\/ tighten: ~63 degrees cone per continent<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    if (this.highlightedFeatureIndices && this.landFeatures?.features?.length && activeContinent) {<!-- [et_pb_line_break_holder] -->                        this.highlightedFeatureIndices.forEach(idx => {<!-- [et_pb_line_break_holder] -->                            const feature = this.landFeatures.features[idx];<!-- [et_pb_line_break_holder] -->                            if (!feature) return;<!-- [et_pb_line_break_holder] -->                            const name = feature.properties?.name;<!-- [et_pb_line_break_holder] -->                            if (!name) return;<!-- [et_pb_line_break_holder] -->                            const centroid = d3.geoCentroid(feature);<!-- [et_pb_line_break_holder] -->                            const projected = projection(centroid);<!-- [et_pb_line_break_holder] -->                            if (!projected) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            \/\/ Skip if centroid far from active continent center<!-- [et_pb_line_break_holder] -->                            const angDist = this.angularDistance(<!-- [et_pb_line_break_holder] -->                                activeContinent.lng, activeContinent.lat,<!-- [et_pb_line_break_holder] -->                                centroid[0], centroid[1]<!-- [et_pb_line_break_holder] -->                            );<!-- [et_pb_line_break_holder] -->                            if (angDist > maxDistRad) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        \/\/ Skip if the feature\u2019s projected bounds are off-screen<!-- [et_pb_line_break_holder] -->                        const b = d3.geoPath().projection(projection).bounds(feature);<!-- [et_pb_line_break_holder] -->                        const [minX, minY] = b[0];<!-- [et_pb_line_break_holder] -->                        const [maxX, maxY] = b[1];<!-- [et_pb_line_break_holder] -->                        const intersectsViewport = !(<!-- [et_pb_line_break_holder] -->                            maxX < 0 || maxY < 0 ||<!-- [et_pb_line_break_holder] -->                            minX > this.containerWidth || minY > this.containerHeight<!-- [et_pb_line_break_holder] -->                        );<!-- [et_pb_line_break_holder] -->                        if (!intersectsViewport) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            const width = this.context.measureText(name).width;<!-- [et_pb_line_break_holder] -->                            const height = countryFontSize * 1.2;<!-- [et_pb_line_break_holder] -->                            const cx = projected[0];<!-- [et_pb_line_break_holder] -->                            const cy = projected[1];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            let chosen = null;<!-- [et_pb_line_break_holder] -->                            let bestOverlap = Infinity;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            gaps.forEach((gap) => {<!-- [et_pb_line_break_holder] -->                                directions.forEach(({ dx, dy }) => {<!-- [et_pb_line_break_holder] -->                                    const offsetX = dx * gap;<!-- [et_pb_line_break_holder] -->                                    const offsetY = dy * gap;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    let x = cx + (dx >= 0 ? 8 + offsetX : -(width + 8 - offsetX));<!-- [et_pb_line_break_holder] -->                                    let y = cy - height \/ 2 + offsetY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    if (dx === 0) x = cx - width \/ 2 + offsetX;<!-- [et_pb_line_break_holder] -->                                    if (dy === 0) y = cy - height \/ 2 + offsetY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    \/\/ Clamp to stay within canvas<!-- [et_pb_line_break_holder] -->                                    x = Math.max(margin, Math.min(x, this.containerWidth - width - margin));<!-- [et_pb_line_break_holder] -->                                    y = Math.max(margin, Math.min(y, this.containerHeight - height - margin));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    const box = { x, y, w: width, h: height };<!-- [et_pb_line_break_holder] -->                                    const overlap = placedCountry.reduce((acc, b) => acc + (intersects(b, box) ? 1 : 0), 0);<!-- [et_pb_line_break_holder] -->                                    if (overlap < bestOverlap) {<!-- [et_pb_line_break_holder] -->                                        bestOverlap = overlap;<!-- [et_pb_line_break_holder] -->                                        chosen = { x, y, box, dx, dy, gap };<!-- [et_pb_line_break_holder] -->                                        if (overlap === 0) return;<!-- [et_pb_line_break_holder] -->                                    }<!-- [et_pb_line_break_holder] -->                                });<!-- [et_pb_line_break_holder] -->                            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            if (!chosen) return;<!-- [et_pb_line_break_holder] -->                            placedCountry.push(chosen.box);<!-- [et_pb_line_break_holder] -->                            const textY = chosen.y + height \/ 2;<!-- [et_pb_line_break_holder] -->                            this.context.fillText(name, chosen.x, textY);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            \/\/ Leader line if offset<!-- [et_pb_line_break_holder] -->                            const distX = chosen.x - cx;<!-- [et_pb_line_break_holder] -->                            const distY = textY - cy;<!-- [et_pb_line_break_holder] -->                            const leaderDist = Math.sqrt(distX * distX + distY * distY);<!-- [et_pb_line_break_holder] -->                            if (leaderDist > 10) {<!-- [et_pb_line_break_holder] -->                                let targetX = chosen.x;<!-- [et_pb_line_break_holder] -->                                let targetY = textY;<!-- [et_pb_line_break_holder] -->                                if (chosen.dx > 0) targetX = chosen.x - 4;<!-- [et_pb_line_break_holder] -->                                else if (chosen.dx < 0) targetX = chosen.x + chosen.box.w + 4;<!-- [et_pb_line_break_holder] -->                                else targetX = cx;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                if (chosen.dy > 0) targetY = chosen.y - 4;<!-- [et_pb_line_break_holder] -->                                else if (chosen.dy < 0) targetY = chosen.y + chosen.box.h + 4;<!-- [et_pb_line_break_holder] -->                                else targetY = textY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                                this.context.moveTo(cx, cy);<!-- [et_pb_line_break_holder] -->                                this.context.lineTo(targetX, targetY);<!-- [et_pb_line_break_holder] -->                                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                                this.context.lineWidth = 1;<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = 0.85;<!-- [et_pb_line_break_holder] -->                                this.context.stroke();<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        });<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                    this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/**<!-- [et_pb_line_break_holder] -->         * Scroll-driven animation:<!-- [et_pb_line_break_holder] -->         * 0 \u2192 0.15: Globe rotation<!-- [et_pb_line_break_holder] -->         * 0.15 \u2192 0.25: Morph to flat map<!-- [et_pb_line_break_holder] -->         * 0.25 \u2192 0.35: Show full flat map (pause)<!-- [et_pb_line_break_holder] -->         * 0.35 \u2192 0.85: Continent tours (zoom\/pan with pauses)<!-- [et_pb_line_break_holder] -->         * 0.85 \u2192 1.00: Fold back to globe (loop-friendly)<!-- [et_pb_line_break_holder] -->         *\/<!-- [et_pb_line_break_holder] -->        applyScrollProgress(progress) {<!-- [et_pb_line_break_holder] -->            const p = Math.max(0, Math.min(1, progress));<!-- [et_pb_line_break_holder] -->            const rotatePortion = 0.15;<!-- [et_pb_line_break_holder] -->            const morphPortion = 0.10;<!-- [et_pb_line_break_holder] -->            const flatHoldPortion = 0.10;<!-- [et_pb_line_break_holder] -->            const foldBackPortion = 0.15;<!-- [et_pb_line_break_holder] -->            const morphEnd = rotatePortion + morphPortion;<!-- [et_pb_line_break_holder] -->            const flatHoldEnd = morphEnd + flatHoldPortion;<!-- [et_pb_line_break_holder] -->            const foldBackStart = 1 - foldBackPortion;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Stop any auto motion when scroll is in control<!-- [et_pb_line_break_holder] -->            if (this.autoRotate) {<!-- [et_pb_line_break_holder] -->                this.autoRotate = false;<!-- [et_pb_line_break_holder] -->                if (this.rotationTimer) {<!-- [et_pb_line_break_holder] -->                    this.rotationTimer.stop();<!-- [et_pb_line_break_holder] -->                    this.rotationTimer = null;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Default graticule fade state<!-- [et_pb_line_break_holder] -->                this.zoomPhaseProgress = 0;<!-- [et_pb_line_break_holder] -->            this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (p <= rotatePortion) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 1: Globe rotation<!-- [et_pb_line_break_holder] -->                const t = p \/ rotatePortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeOutCubic(t);<!-- [et_pb_line_break_holder] -->                const angle = this.scrollBaseRotation + eased * 360;<!-- [et_pb_line_break_holder] -->                this.rotation[0] = angle;<!-- [et_pb_line_break_holder] -->                this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                this.mode = 'globe';<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Study Abroad. Find your perfect university now!');<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            } else if (p <= morphEnd) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 2: Morph to flat<!-- [et_pb_line_break_holder] -->                const unfoldT = (p - rotatePortion) \/ morphPortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeInOutCubic(unfoldT);<!-- [et_pb_line_break_holder] -->                this.mode = 'morphing';<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Semester abroad, Bachelor \/ Master Abroad, Double Degree, Gap Year, Summer Sessions, and many more!');<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                if (eased >= 0.98) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    this.renderMorph(eased);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            } else if (p <= flatHoldEnd) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 2.5: Show full flat map before zooming<!-- [et_pb_line_break_holder] -->                this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                this.showLabels = false; \/\/ hide country labels on full map<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('72 Universtities in 25 Countries');<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            } else if (p < foldBackStart) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 3: Continent tours<!-- [et_pb_line_break_holder] -->                const tourProgress = (p - flatHoldEnd) \/ (foldBackStart - flatHoldEnd);<!-- [et_pb_line_break_holder] -->                \/\/ Ensure we're in flat mode and projection is reset before starting tours<!-- [et_pb_line_break_holder] -->                if (this.mode !== 'flat' && tourProgress < 0.01) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                    this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                this.zoomPhaseProgress = Math.max(0, Math.min(1, tourProgress));<!-- [et_pb_line_break_holder] -->                const fastFadePortion = 0.15; \/\/ fade out early in the tour phase<!-- [et_pb_line_break_holder] -->                const fadeT = Math.max(0, Math.min(1, tourProgress \/ fastFadePortion));<!-- [et_pb_line_break_holder] -->                this.graticuleFade = Math.max(0, 1 - fadeT);<!-- [et_pb_line_break_holder] -->                this.showLabels = true;<!-- [et_pb_line_break_holder] -->                this.handleContinentTour(tourProgress);<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 4: Fold back to globe<!-- [et_pb_line_break_holder] -->                const foldT = (p - foldBackStart) \/ foldBackPortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeInOutCubic(foldT);<!-- [et_pb_line_break_holder] -->                this.mode = 'morphing';<!-- [et_pb_line_break_holder] -->                \/\/ Fade graticules back in as we fold up<!-- [et_pb_line_break_holder] -->                this.graticuleFade = Math.min(1, eased);<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->                \/\/ Reverse morph: use (1 - eased) to move from flat -> globe<!-- [et_pb_line_break_holder] -->                this.renderMorph(1 - eased);<!-- [et_pb_line_break_holder] -->                if (foldT >= 0.98) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'globe';<!-- [et_pb_line_break_holder] -->                    this.rotation = [...this.initialRotation];<!-- [et_pb_line_break_holder] -->                    this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                    this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->                    this.showLabels = false;<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleContinentTour(progress) {<!-- [et_pb_line_break_holder] -->            \/\/ Each continent gets: transition in (15%), hold (25%), transition out (15%)<!-- [et_pb_line_break_holder] -->            \/\/ Total per continent: 55%, with transitions between them<!-- [et_pb_line_break_holder] -->            const transitionIn = 0.15;<!-- [et_pb_line_break_holder] -->            const holdDuration = 0.25;<!-- [et_pb_line_break_holder] -->            const transitionOut = 0.15;<!-- [et_pb_line_break_holder] -->            const continentDuration = transitionIn + holdDuration + transitionOut;<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            const numContinents = this.continentTours.length;<!-- [et_pb_line_break_holder] -->            const returnToMapDuration = 0.20;<!-- [et_pb_line_break_holder] -->            const totalDuration = numContinents * continentDuration + returnToMapDuration;<!-- [et_pb_line_break_holder] -->            const normalizedProgress = Math.min(1, Math.max(0, progress)) * totalDuration;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Ensure we start from full map when entering tour phase<!-- [et_pb_line_break_holder] -->            if (normalizedProgress < 0.001) {<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            let accumulated = 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            for (let i = 0; i < numContinents; i++) {<!-- [et_pb_line_break_holder] -->                const continentStart = accumulated;<!-- [et_pb_line_break_holder] -->                const transitionInEnd = continentStart + transitionIn;<!-- [et_pb_line_break_holder] -->                const holdEnd = transitionInEnd + holdDuration;<!-- [et_pb_line_break_holder] -->                const transitionOutEnd = holdEnd + transitionOut;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (normalizedProgress >= continentStart && normalizedProgress < transitionInEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Transitioning into this continent<!-- [et_pb_line_break_holder] -->                    const t = (normalizedProgress - continentStart) \/ transitionIn;<!-- [et_pb_line_break_holder] -->                    const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                    \/\/ If this is the first continent and we're just starting, ensure we begin from full map<!-- [et_pb_line_break_holder] -->                    if (i === 0 && normalizedProgress < continentStart + 0.01) {<!-- [et_pb_line_break_holder] -->                        this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    this.animateToContinent(i, easedT);<!-- [et_pb_line_break_holder] -->                    const c = this.continentTours[i];<!-- [et_pb_line_break_holder] -->                    const count = this.getContinentUniversityCount(i);<!-- [et_pb_line_break_holder] -->                    this.updatePhaseText(`${count} Universities in ${c.name}`);<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                } else if (normalizedProgress >= transitionInEnd && normalizedProgress < holdEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Holding at this continent<!-- [et_pb_line_break_holder] -->                    this.animateToContinent(i, 1);<!-- [et_pb_line_break_holder] -->                    const c = this.continentTours[i];<!-- [et_pb_line_break_holder] -->                    const count = this.getContinentUniversityCount(i);<!-- [et_pb_line_break_holder] -->                    this.updatePhaseText(`${count} Universities in ${c.name}`);<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                } else if (normalizedProgress >= holdEnd && normalizedProgress < transitionOutEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Transitioning out of this continent<!-- [et_pb_line_break_holder] -->                    const t = (normalizedProgress - holdEnd) \/ transitionOut;<!-- [et_pb_line_break_holder] -->                    const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                    const nextIndex = i < numContinents - 1 ? i + 1 : -1;<!-- [et_pb_line_break_holder] -->                    if (nextIndex >= 0) {<!-- [et_pb_line_break_holder] -->                        \/\/ Transition to next continent<!-- [et_pb_line_break_holder] -->                        this.transitionBetweenContinents(i, nextIndex, easedT);<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        \/\/ Transition back to full map<!-- [et_pb_line_break_holder] -->                        this.transitionToFullMap(easedT);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                accumulated = transitionOutEnd;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Final: return to full map<!-- [et_pb_line_break_holder] -->            const returnStart = accumulated;<!-- [et_pb_line_break_holder] -->            if (normalizedProgress >= returnStart) {<!-- [et_pb_line_break_holder] -->                const t = Math.min(1, (normalizedProgress - returnStart) \/ returnToMapDuration);<!-- [et_pb_line_break_holder] -->                const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                this.transitionToFullMap(easedT);<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Apply now to start your study abroad journey!\\n\\nworldofstudents.org');<!-- [et_pb_line_break_holder] -->            } else if (normalizedProgress < 0.001) {<!-- [et_pb_line_break_holder] -->                \/\/ At the very start, show full map<!-- [et_pb_line_break_holder] -->                this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        animateToContinent(index, t) {<!-- [et_pb_line_break_holder] -->            const continent = this.continentTours[index];<!-- [et_pb_line_break_holder] -->            const targetScale = this.baseFlatScale * continent.zoom;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const targetCenter = [continent.lng, continent.lat];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Interpolate from current state<!-- [et_pb_line_break_holder] -->            const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->            const currentCenter = this.flatProjection.center();<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            const scale = currentScale + (targetScale - currentScale) * t;<!-- [et_pb_line_break_holder] -->            const centerLng = currentCenter[0] + (targetCenter[0] - currentCenter[0]) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = currentCenter[1] + (targetCenter[1] - currentCenter[1]) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.currentZoom = scale \/ this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = index;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        transitionBetweenContinents(fromIndex, toIndex, t) {<!-- [et_pb_line_break_holder] -->            const from = this.continentTours[fromIndex];<!-- [et_pb_line_break_holder] -->            const to = this.continentTours[toIndex];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const fromScale = this.baseFlatScale * from.zoom;<!-- [et_pb_line_break_holder] -->            const toScale = this.baseFlatScale * to.zoom;<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            \/\/ Interpolate scale<!-- [et_pb_line_break_holder] -->            const scale = fromScale + (toScale - fromScale) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const centerLng = from.lng + (to.lng - from.lng) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = from.lat + (to.lat - from.lat) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.currentZoom = scale \/ this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = toIndex;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        transitionToFullMap(t) {<!-- [et_pb_line_break_holder] -->            const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->            const targetScale = this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            const scale = currentScale + (targetScale - currentScale) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentCenter = this.flatProjection.center();<!-- [et_pb_line_break_holder] -->            const targetCenter = [0, 0];<!-- [et_pb_line_break_holder] -->            const centerLng = currentCenter[0] + (targetCenter[0] - currentCenter[0]) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = currentCenter[1] + (targetCenter[1] - currentCenter[1]) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        resetFlatProjection() {<!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(this.baseFlatScale)<!-- [et_pb_line_break_holder] -->                .center([0, 0])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] -->            this.currentZoom = 1;<!-- [et_pb_line_break_holder] -->            this.showLabels = false;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        startAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = true;<!-- [et_pb_line_break_holder] -->            this.autoPlayStart = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const step = (ts) => {<!-- [et_pb_line_break_holder] -->                if (!this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->                if (this.autoPlayStart === null) this.autoPlayStart = ts;<!-- [et_pb_line_break_holder] -->                const elapsed = ts - this.autoPlayStart;<!-- [et_pb_line_break_holder] -->                const t = Math.min(1, elapsed \/ this.autoPlayDuration);<!-- [et_pb_line_break_holder] -->                this.applyScrollProgress(t);<!-- [et_pb_line_break_holder] -->                if (t >= 1) {<!-- [et_pb_line_break_holder] -->                    this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->                    this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                this.autoPlayHandle = requestAnimationFrame(step);<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->            this.autoPlayHandle = requestAnimationFrame(step);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        stopAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (!this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->            if (this.autoPlayHandle) {<!-- [et_pb_line_break_holder] -->                cancelAnimationFrame(this.autoPlayHandle);<!-- [et_pb_line_break_holder] -->                this.autoPlayHandle = null;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        toggleAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (this.isAutoPlaying) this.stopAutoPlay();<!-- [et_pb_line_break_holder] -->            else this.startAutoPlay();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        updateAutoPlayButton() {<!-- [et_pb_line_break_holder] -->            if (!this.autoplayBtn) return;<!-- [et_pb_line_break_holder] -->            this.autoplayBtn.textContent = this.isAutoPlaying ? 'Stop' : 'Autoplay';<!-- [et_pb_line_break_holder] -->            this.autoplayBtn.style.opacity = this.isAutoPlaying ? '0.95' : '1';<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        updatePhaseText(text) {<!-- [et_pb_line_break_holder] -->            if (!this.phaseText) return;<!-- [et_pb_line_break_holder] -->            if (this._currentPhaseText === text) return;<!-- [et_pb_line_break_holder] -->            this._currentPhaseText = text;<!-- [et_pb_line_break_holder] -->            this.phaseText.textContent = text || '';<!-- [et_pb_line_break_holder] -->            if (text) {<!-- [et_pb_line_break_holder] -->                this.phaseText.classList.add('visible');<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                this.phaseText.classList.remove('visible');<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupScrollTrigger(targetEl, options = {}) {<!-- [et_pb_line_break_holder] -->            const el = targetEl || this.canvas;<!-- [et_pb_line_break_holder] -->            this.scrollBaseRotation = this.rotation[0];<!-- [et_pb_line_break_holder] -->            const startOffsetOpt = options.startOffset;<!-- [et_pb_line_break_holder] -->            const startAtSticky = options.startAtSticky !== false; \/\/ default true<!-- [et_pb_line_break_holder] -->            const endOffset = options.endOffset ?? 0.8; \/\/ finish slightly before sticky ends<!-- [et_pb_line_break_holder] -->            const clamp01 = (v) => Math.max(0, Math.min(1, v));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const onScroll = () => {<!-- [et_pb_line_break_holder] -->                const viewport = window.innerHeight || 1;<!-- [et_pb_line_break_holder] -->                const rect = el.getBoundingClientRect();<!-- [et_pb_line_break_holder] -->                const wrapperHeight = el.offsetHeight || 1;<!-- [et_pb_line_break_holder] -->                const wrapperTopAbs = rect.top + (window.scrollY || window.pageYOffset || 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ How far we've scrolled since the wrapper reached the top of the viewport<!-- [et_pb_line_break_holder] -->                const scrolledInside = (window.scrollY || window.pageYOffset || 0) - wrapperTopAbs;<!-- [et_pb_line_break_holder] -->                const stickyRange = Math.max(1, wrapperHeight - viewport); \/\/ duration of stickiness<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ raw progress: 0 when top first sticks, 1 when sticky duration ends<!-- [et_pb_line_break_holder] -->                const raw = clamp01(scrolledInside \/ stickyRange);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ startOffset: either provided or aligned to stick start<!-- [et_pb_line_break_holder] -->                const startOffset = startOffsetOpt != null ? startOffsetOpt : (startAtSticky ? 0 : 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Map raw progress into [startOffset, endOffset] range<!-- [et_pb_line_break_holder] -->                const normalized = clamp01((raw - startOffset) \/ Math.max(0.0001, endOffset - startOffset));<!-- [et_pb_line_break_holder] -->                this.applyScrollProgress(normalized);<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            ['scroll', 'resize'].forEach(evt =><!-- [et_pb_line_break_holder] -->                window.addEventListener(evt, onScroll, { passive: true })<!-- [et_pb_line_break_holder] -->            );<!-- [et_pb_line_break_holder] -->            onScroll();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ ---- Morph helpers ----<!-- [et_pb_line_break_holder] -->        easeInOutCubic(t) {<!-- [et_pb_line_break_holder] -->            return t < 0.5<!-- [et_pb_line_break_holder] -->                ? 4 * t * t * t<!-- [et_pb_line_break_holder] -->                : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ Faster start, slower end<!-- [et_pb_line_break_holder] -->        easeOutCubic(t) {<!-- [et_pb_line_break_holder] -->            const clamped = Math.min(1, Math.max(0, t));<!-- [et_pb_line_break_holder] -->            return 1 - Math.pow(1 - clamped, 3);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        createForwardInterpolatedProjection(t) {<!-- [et_pb_line_break_holder] -->            \/\/ Use ease-out so the unfold decelerates into the final flat view<!-- [et_pb_line_break_holder] -->            const tt = this.easeOutCubic(t);<!-- [et_pb_line_break_holder] -->            const startProj = this.orthoProjection;<!-- [et_pb_line_break_holder] -->            const endProj = this.flatProjection;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            return (coords) => {<!-- [et_pb_line_break_holder] -->                const p0 = startProj(coords);<!-- [et_pb_line_break_holder] -->                const p1 = endProj(coords);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (!p0 && !p1) return null;<!-- [et_pb_line_break_holder] -->                const x0 = p0 ? p0[0] : p1[0];<!-- [et_pb_line_break_holder] -->                const y0 = p0 ? p0[1] : p1[1];<!-- [et_pb_line_break_holder] -->                const x1 = p1 ? p1[0] : p0[0];<!-- [et_pb_line_break_holder] -->                const y1 = p1 ? p1[1] : p0[1];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                return [<!-- [et_pb_line_break_holder] -->                    x0 + (x1 - x0) * tt,<!-- [et_pb_line_break_holder] -->                    y0 + (y1 - y0) * tt<!-- [et_pb_line_break_holder] -->                ];<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        buildSphereBoundaryCoords() {<!-- [et_pb_line_break_holder] -->            const coords = [];<!-- [et_pb_line_break_holder] -->            const step = 5;<!-- [et_pb_line_break_holder] -->            for (let lon = -180; lon <= 180; lon += step) {<!-- [et_pb_line_break_holder] -->                coords.push([lon, -89]);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            for (let lon = 180; lon >= -180; lon -= step) {<!-- [et_pb_line_break_holder] -->                coords.push([lon, 89]);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return coords;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ \u2705 NEW: break path when projected x-jump is large (no wrap lines)<!-- [et_pb_line_break_holder] -->        drawLineString(ctx, coords, proj) {<!-- [et_pb_line_break_holder] -->            let prevP = null;<!-- [et_pb_line_break_holder] -->            let prevC = null;<!-- [et_pb_line_break_holder] -->            const thresholdX = this.containerWidth \/ 3; \/\/ stricter to catch dateline jumps<!-- [et_pb_line_break_holder] -->            const thresholdY = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            coords.forEach((c) => {<!-- [et_pb_line_break_holder] -->                const p = proj(c);<!-- [et_pb_line_break_holder] -->                if (!p) return;<!-- [et_pb_line_break_holder] -->                const shouldBreak =<!-- [et_pb_line_break_holder] -->                    !!prevP &&<!-- [et_pb_line_break_holder] -->                    (<!-- [et_pb_line_break_holder] -->                        Math.abs(p[0] - prevP[0]) > thresholdX ||<!-- [et_pb_line_break_holder] -->                        Math.abs(p[1] - prevP[1]) > thresholdY ||<!-- [et_pb_line_break_holder] -->                        (prevC && Math.abs(c[0] - prevC[0]) > 170) \/\/ large lon jump \u2192 new subpath<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (!prevP || shouldBreak) {<!-- [et_pb_line_break_holder] -->                    ctx.moveTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    ctx.lineTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                prevP = p;<!-- [et_pb_line_break_holder] -->                prevC = c;<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        drawPolygon(ctx, coords, proj) {<!-- [et_pb_line_break_holder] -->            const thresholdX = this.containerWidth \/ 3;<!-- [et_pb_line_break_holder] -->            coords.forEach((ring) => {<!-- [et_pb_line_break_holder] -->                let prevP = null;<!-- [et_pb_line_break_holder] -->                let prevC = null;<!-- [et_pb_line_break_holder] -->                let firstP = null;<!-- [et_pb_line_break_holder] -->                let firstC = null;<!-- [et_pb_line_break_holder] -->                ring.forEach((c) => {<!-- [et_pb_line_break_holder] -->                    const p = proj(c);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    const shouldBreak =<!-- [et_pb_line_break_holder] -->                        !!prevP &&<!-- [et_pb_line_break_holder] -->                        (<!-- [et_pb_line_break_holder] -->                            Math.abs(p[0] - prevP[0]) > thresholdX ||<!-- [et_pb_line_break_holder] -->                            (prevC && Math.abs(c[0] - prevC[0]) > 170)<!-- [et_pb_line_break_holder] -->                        );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    if (!prevP || shouldBreak) {<!-- [et_pb_line_break_holder] -->                        ctx.moveTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                        firstP = firstP || p;<!-- [et_pb_line_break_holder] -->                        firstC = firstC || c;<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        ctx.lineTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    prevP = p;<!-- [et_pb_line_break_holder] -->                    prevC = c;<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                \/\/ Close ring only when it would not span the dateline<!-- [et_pb_line_break_holder] -->                if (firstP && prevP) {<!-- [et_pb_line_break_holder] -->                    const dxClose = Math.abs(firstP[0] - prevP[0]);<!-- [et_pb_line_break_holder] -->                    const lonJump = firstC && prevC ? Math.abs(firstC[0] - prevC[0]) : 0;<!-- [et_pb_line_break_holder] -->                    if (dxClose <= thresholdX &#038;&#038; lonJump <= 170) {<!-- [et_pb_line_break_holder] -->                        ctx.lineTo(firstP[0], firstP[1]);<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        ctx.moveTo(firstP[0], firstP[1]);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderMorph(t) {<!-- [et_pb_line_break_holder] -->            const proj = this.createForwardInterpolatedProjection(t);<!-- [et_pb_line_break_holder] -->            const ctx = this.context;<!-- [et_pb_line_break_holder] -->            const morphScale = this.flatProjection.scale() \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] -->            const dotRadius = 0.5 * morphScale * sizeFactor;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            ctx.clearRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            ctx.save();<!-- [et_pb_line_break_holder] -->            ctx.fillStyle = this.bgColor || '#ffffff';<!-- [et_pb_line_break_holder] -->            ctx.fillRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            ctx.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Ocean plate<!-- [et_pb_line_break_holder] -->            ctx.beginPath();<!-- [et_pb_line_break_holder] -->            this.drawLineString(ctx, this.sphereBoundary, proj);<!-- [et_pb_line_break_holder] -->            ctx.closePath();<!-- [et_pb_line_break_holder] -->            ctx.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            ctx.fill();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                \/\/ Graticule<!-- [et_pb_line_break_holder] -->                if (this.graticuleGeo && this.graticuleGeo.type === 'MultiLineString') {<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    this.graticuleGeo.coordinates.forEach(line => {<!-- [et_pb_line_break_holder] -->                        this.drawLineString(ctx, line, proj);<!-- [et_pb_line_break_holder] -->                    });<!-- [et_pb_line_break_holder] -->                    ctx.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = 0.4 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                    ctx.lineWidth = 0.8 * sizeFactor;<!-- [et_pb_line_break_holder] -->                    ctx.stroke();<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = 1;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Fills<!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.35;<!-- [et_pb_line_break_holder] -->                ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->                    if (!geom) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                        this.drawPolygon(ctx, geom.coordinates, proj);<!-- [et_pb_line_break_holder] -->                    } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                        geom.coordinates.forEach(poly => this.drawPolygon(ctx, poly, proj));<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                ctx.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Borders (draw after fills so every country gets stroked)<!-- [et_pb_line_break_holder] -->                ctx.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->                    if (!geom) return;<!-- [et_pb_line_break_holder] -->                    if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                        this.drawPolygon(ctx, geom.coordinates, proj);<!-- [et_pb_line_break_holder] -->                    } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                        geom.coordinates.forEach(poly => this.drawPolygon(ctx, poly, proj));<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                ctx.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                ctx.lineWidth = 0.7 * sizeFactor;<!-- [et_pb_line_break_holder] -->                ctx.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Dots<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const p = proj([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    ctx.arc(p[0], p[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                    ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ University markers (consistent size through morph)<!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const p = proj([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    ctx.arc(p[0], p[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                    ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        async loadWorldData() {<!-- [et_pb_line_break_holder] -->            try {<!-- [et_pb_line_break_holder] -->                this.isLoading = true;<!-- [et_pb_line_break_holder] -->                \/\/ Lightweight country TopoJSON (much smaller than full GeoJSON)<!-- [et_pb_line_break_holder] -->                let response = await fetch(<!-- [et_pb_line_break_holder] -->                    'https:\/\/cdn.jsdelivr.net\/npm\/world-atlas@2\/countries-110m.json'<!-- [et_pb_line_break_holder] -->                );<!-- [et_pb_line_break_holder] -->                if (!response.ok) {<!-- [et_pb_line_break_holder] -->                    \/\/ fallback to lightweight land polygons if countries fail<!-- [et_pb_line_break_holder] -->                    response = await fetch(<!-- [et_pb_line_break_holder] -->                        'https:\/\/raw.githubusercontent.com\/martynafford\/natural-earth-geojson\/master\/110m\/physical\/ne_110m_land.json'<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] -->                    if (!response.ok) throw new Error('Failed to load country\/land data');<!-- [et_pb_line_break_holder] -->                    this.landFeatures = await response.json();<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    const topo = await response.json();<!-- [et_pb_line_break_holder] -->                    this.landFeatures = topojson.feature(topo, topo.objects.countries);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                const [[minLng, minLat], [maxLng, maxLat]] = d3.geoBounds(feature);<!-- [et_pb_line_break_holder] -->                const lonSpan = maxLng - minLng;<!-- [et_pb_line_break_holder] -->                \/\/ Reduced density overall; still a bit denser for very wide countries<!-- [et_pb_line_break_holder] -->                const spacing = lonSpan > 120 ? 16 : 22;<!-- [et_pb_line_break_holder] -->                const dots = this.generateDotsInPolygon(feature, spacing);<!-- [et_pb_line_break_holder] -->                dots.forEach(([lng, lat]) => {<!-- [et_pb_line_break_holder] -->                    this.allDots.push({ lng, lat, visible: true });<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                await this.loadUniversitiesData();<!-- [et_pb_line_break_holder] -->                this.computeHighlightedFeatures();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->                this.isLoading = false;<!-- [et_pb_line_break_holder] -->                this.loadingOverlay.style.display = 'none';<!-- [et_pb_line_break_holder] -->                if (this.autoRotate) {<!-- [et_pb_line_break_holder] -->                    this.startRotation();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            } catch (err) {<!-- [et_pb_line_break_holder] -->                this.showError('Failed to load land map data');<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        async loadUniversitiesData() {<!-- [et_pb_line_break_holder] -->            try {<!-- [et_pb_line_break_holder] -->                const res = await fetch('https:\/\/worldofstudents.org\/universities.json', {<!-- [et_pb_line_break_holder] -->                    cache: 'no-cache'<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                if (!res.ok) throw new Error('Failed to fetch universities.json');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const data = await res.json();<!-- [et_pb_line_break_holder] -->                const items = data?.mainEntity?.itemListElement || [];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const parsed = items<!-- [et_pb_line_break_holder] -->                    .map(entry => entry?.item)<!-- [et_pb_line_break_holder] -->                    .map(item => {<!-- [et_pb_line_break_holder] -->                        const lat = parseFloat(item?.geo?.latitude);<!-- [et_pb_line_break_holder] -->                        const lng = parseFloat(item?.geo?.longitude);<!-- [et_pb_line_break_holder] -->                        if (!item?.name || !Number.isFinite(lat) || !Number.isFinite(lng)) {<!-- [et_pb_line_break_holder] -->                            return null;<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                        return {<!-- [et_pb_line_break_holder] -->                            name: item.name,<!-- [et_pb_line_break_holder] -->                            lat,<!-- [et_pb_line_break_holder] -->                            lng,<!-- [et_pb_line_break_holder] -->                            country: item?.address?.countryName || item?.address?.addressCountry || ''<!-- [et_pb_line_break_holder] -->                        };<!-- [et_pb_line_break_holder] -->                    })<!-- [et_pb_line_break_holder] -->                    .filter(Boolean);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.universities = parsed.length<!-- [et_pb_line_break_holder] -->                    ? parsed<!-- [et_pb_line_break_holder] -->                    : universityData.map(item => ({<!-- [et_pb_line_break_holder] -->                        name: item.name,<!-- [et_pb_line_break_holder] -->                        lat: parseFloat(item.lat),<!-- [et_pb_line_break_holder] -->                        lng: parseFloat(item.lng),<!-- [et_pb_line_break_holder] -->                        country: item.country<!-- [et_pb_line_break_holder] -->                    }));<!-- [et_pb_line_break_holder] -->            } catch (err) {<!-- [et_pb_line_break_holder] -->                console.error('Failed to load universities.json, using fallback data', err);<!-- [et_pb_line_break_holder] -->                this.universities = universityData.map(item => ({<!-- [et_pb_line_break_holder] -->                    name: item.name,<!-- [et_pb_line_break_holder] -->                    lat: parseFloat(item.lat),<!-- [et_pb_line_break_holder] -->                    lng: parseFloat(item.lng),<!-- [et_pb_line_break_holder] -->                    country: item.country<!-- [et_pb_line_break_holder] -->                }));<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            \/\/ Invalidate cached continent counts when the dataset updates<!-- [et_pb_line_break_holder] -->            this._continentCounts.clear();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        computeHighlightedFeatures() {<!-- [et_pb_line_break_holder] -->            this.highlightedFeatureIndices = new Set();<!-- [et_pb_line_break_holder] -->            if (!this.landFeatures?.features?.length || !this.universities?.length) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                const bounds = d3.geoBounds(feature);<!-- [et_pb_line_break_holder] -->                const [[minLng, minLat], [maxLng, maxLat]] = bounds;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const hasUni = this.universities.some(uni => {<!-- [et_pb_line_break_holder] -->                    \/\/ quick bbox reject<!-- [et_pb_line_break_holder] -->                    if (uni.lng < minLng || uni.lng > maxLng || uni.lat < minLat || uni.lat > maxLat) {<!-- [et_pb_line_break_holder] -->                        return false;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    return this.pointInFeature([uni.lng, uni.lat], feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (hasUni) this.highlightedFeatureIndices.add(idx);<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        loadLogo() {<!-- [et_pb_line_break_holder] -->            this.logoImage = new Image();<!-- [et_pb_line_break_holder] -->            this.logoImage.crossOrigin = 'anonymous';<!-- [et_pb_line_break_holder] -->            this.logoImage.src = 'https:\/\/worldofstudents.org\/wp-content\/uploads\/2025\/09\/WOS_Logo_white.png';<!-- [et_pb_line_break_holder] -->            this.logoImage.onload = () => {<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        showError(message) {<!-- [et_pb_line_break_holder] -->            this.error = message;<!-- [et_pb_line_break_holder] -->            this.isLoading = false;<!-- [et_pb_line_break_holder] -->            this.loadingOverlay.innerHTML = `<!-- [et_pb_line_break_holder] -->                <\/p>\n<div class=\"error-state\"><!-- [et_pb_line_break_holder] -->                    <\/p>\n<div class=\"error-title\">Error loading Earth visualization<\/div>\n<p><!-- [et_pb_line_break_holder] -->                    <\/p>\n<div class=\"error-message\">${message}<\/div>\n<p><!-- [et_pb_line_break_holder] -->                <\/div>\n<p><!-- [et_pb_line_break_holder] -->            `;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        getContinentUniversityCount(index) {<!-- [et_pb_line_break_holder] -->            if (this._continentCounts.has(index)) {<!-- [et_pb_line_break_holder] -->                return this._continentCounts.get(index);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const tour = this.continentTours[index];<!-- [et_pb_line_break_holder] -->            if (!tour || !Array.isArray(this.universities)) return 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const latRange = tour.latRange || [-90, 90];<!-- [et_pb_line_break_holder] -->            const lngRanges = tour.lngRanges || [[-180, 180]];<!-- [et_pb_line_break_holder] -->            const countrySet = new Set((tour.countries || []).map(c => c.toLowerCase()));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const inLatRange = (lat) => lat >= latRange[0] && lat <= latRange[1];<!-- [et_pb_line_break_holder] -->            const inLngRange = (lng) => {<!-- [et_pb_line_break_holder] -->                const normLng = this.normalizeLng(lng);<!-- [et_pb_line_break_holder] -->                return lngRanges.some(([min, max]) => this.lngWithinRange(normLng, min, max));<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] -->            const inCountrySet = (country) => {<!-- [et_pb_line_break_holder] -->                if (!country) return false;<!-- [et_pb_line_break_holder] -->                const c = country.toLowerCase().trim();<!-- [et_pb_line_break_holder] -->                return countrySet.has(c);<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const count = this.universities.reduce((acc, uni) => {<!-- [et_pb_line_break_holder] -->                if (!Number.isFinite(uni.lat) || !Number.isFinite(uni.lng)) return acc;<!-- [et_pb_line_break_holder] -->                const countryOk = countrySet.size ? inCountrySet(uni.country) : true;<!-- [et_pb_line_break_holder] -->                const geoOk = inLatRange(uni.lat) && inLngRange(uni.lng);<!-- [et_pb_line_break_holder] -->                if (countryOk && geoOk) return acc + 1;<!-- [et_pb_line_break_holder] -->                return acc;<!-- [et_pb_line_break_holder] -->            }, 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this._continentCounts.set(index, count);<!-- [et_pb_line_break_holder] -->            return count;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        normalizeLng(lng) {<!-- [et_pb_line_break_holder] -->            const n = ((lng + 180) % 360 + 360) % 360 - 180;<!-- [et_pb_line_break_holder] -->            return n === -180 ? 180 : n;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        lngWithinRange(lng, min, max) {<!-- [et_pb_line_break_holder] -->            const nMin = this.normalizeLng(min);<!-- [et_pb_line_break_holder] -->            const nMax = this.normalizeLng(max);<!-- [et_pb_line_break_holder] -->            if (nMin <= nMax) {<!-- [et_pb_line_break_holder] -->                return lng >= nMin && lng <= nMax;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return lng >= nMin || lng <= nMax;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        startRotation() {<!-- [et_pb_line_break_holder] -->            this._lastElapsed = 0;<!-- [et_pb_line_break_holder] -->            this._lastFrame = 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const rotate = (elapsed) => {<!-- [et_pb_line_break_holder] -->                if (!this.autoRotate || this.mode !== 'globe') return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const deltaMs = elapsed - this._lastElapsed;<!-- [et_pb_line_break_holder] -->                this._lastElapsed = elapsed;<!-- [et_pb_line_break_holder] -->                const deltaSec = Math.max(0, deltaMs) \/ 1000;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const degPerSec = this.rotationSpeed * 60;<!-- [et_pb_line_break_holder] -->                this.rotation[0] += degPerSec * deltaSec;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const FRAME_MS = this.isInteracting ? 16 : 33;<!-- [et_pb_line_break_holder] -->                if (elapsed - this._lastFrame >= FRAME_MS) {<!-- [et_pb_line_break_holder] -->                    this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                    this._lastFrame = elapsed;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.rotationTimer) this.rotationTimer.stop();<!-- [et_pb_line_break_holder] -->            this.rotationTimer = d3.timer(rotate);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupInteractions() {<!-- [et_pb_line_break_holder] -->            \/\/ Disable drag\/zoom; keep hover for tooltip<!-- [et_pb_line_break_holder] -->            this.canvas.addEventListener('mousemove', (event) => this.handleMouseMove(event));<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleMouseMove(event) {<!-- [et_pb_line_break_holder] -->            const rect = this.canvas.getBoundingClientRect();<!-- [et_pb_line_break_holder] -->            const x = event.clientX - rect.left;<!-- [et_pb_line_break_holder] -->            const y = event.clientY - rect.top;<!-- [et_pb_line_break_holder] -->            let hoveredUni = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const projection = (this.mode === 'flat' || this.mode === 'morphing') ? this.flatProjection : this.orthoProjection;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                if (projected &&<!-- [et_pb_line_break_holder] -->                    projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                    projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                    const dx = projected[0] - x;<!-- [et_pb_line_break_holder] -->                    const dy = projected[1] - y;<!-- [et_pb_line_break_holder] -->                    const distance = Math.sqrt(dx * dx + dy * dy);<!-- [et_pb_line_break_holder] -->                    if (distance < 15) {<!-- [et_pb_line_break_holder] -->                        hoveredUni = uni;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (hoveredUni && this.tooltip) {<!-- [et_pb_line_break_holder] -->                this.tooltip.style.display = 'block';<!-- [et_pb_line_break_holder] -->                this.tooltip.style.left = `${x + 10}px`;<!-- [et_pb_line_break_holder] -->                this.tooltip.style.top = `${y + 10}px`;<!-- [et_pb_line_break_holder] -->                this.tooltip.innerHTML = `<strong>${hoveredUni.name}<\/strong><!\u2013- [et_pb_br_holder] -\u2013>${hoveredUni.country}`;<!-- [et_pb_line_break_holder] -->            } else if (this.tooltip) {<!-- [et_pb_line_break_holder] -->                this.tooltip.style.display = 'none';<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    document.addEventListener('DOMContentLoaded', () => {<!-- [et_pb_line_break_holder] -->        const earth = new RotatingEarth('earth-canvas', {<!-- [et_pb_line_break_holder] -->            width: 1080,<!-- [et_pb_line_break_holder] -->            height: 540,<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->        earth.autoplayBtn = document.getElementById('autoplay-btn');<!-- [et_pb_line_break_holder] -->        if (earth.autoplayBtn) {<!-- [et_pb_line_break_holder] -->            earth.autoplayBtn.addEventListener('click', () => earth.toggleAutoPlay());<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ Drive the animation by scroll progress; use the wrapper for full scroll length<!-- [et_pb_line_break_holder] -->        \/\/ startOffset delays the start; endOffset ends a bit before the wrapper ends<!-- [et_pb_line_break_holder] -->        earth.autoRotate = false;<!-- [et_pb_line_break_holder] -->        earth.setupScrollTrigger(document.getElementById('earth-sticky-wrapper'), {<!-- [et_pb_line_break_holder] -->            \/\/ startAtSticky uses the point where the wrapper first pins as the start<!-- [et_pb_line_break_holder] -->            startAtSticky: true,<!-- [et_pb_line_break_holder] -->            endOffset: 0.8<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->    });<!-- [et_pb_line_break_holder] --><\/script><!-- [et_pb_line_break_holder] -->[\/et_pb_fullwidth_code][et_pb_fullwidth_code _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; hover_enabled=&#8221;0&#8243; sticky_enabled=&#8221;0&#8243; disabled_on=&#8221;on|on|on&#8221; disabled=&#8221;on&#8221; admin_label=&#8221;stars without text reveal&#8221;]<script src=\"https:\/\/d3js.org\/d3.v7.min.js\"><\/script><!-- [et_pb_line_break_holder] --><script src=\"https:\/\/cdn.jsdelivr.net\/npm\/topojson-client@3\/dist\/topojson-client.min.js\"><\/script><!-- [et_pb_line_break_holder] --><\/p>\n<style><!-- [et_pb_line_break_holder] -->    html, body {<!-- [et_pb_line_break_holder] -->        margin: 0;<!-- [et_pb_line_break_holder] -->        padding: 0;<!-- [et_pb_line_break_holder] -->        height: 100%;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/* Wrapper defines how long the sticky canvas stays fixed *\/<!-- [et_pb_line_break_holder] -->    .earth-sticky-wrapper {<!-- [et_pb_line_break_holder] -->        position: relative;<!-- [et_pb_line_break_holder] -->        height: 600vh; \/* Extended for continent tours *\/<!-- [et_pb_line_break_holder] -->        max-width: 1080px;<!-- [et_pb_line_break_holder] -->        margin:0 auto;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .earth-container {<!-- [et_pb_line_break_holder] -->        position: sticky;<!-- [et_pb_line_break_holder] -->        top: 150px;<!-- [et_pb_line_break_holder] -->        width: 100%;<!-- [et_pb_line_break_holder] -->        max-width: 1080px;<!-- [et_pb_line_break_holder] -->        height: 75vh;<!-- [et_pb_line_break_holder] -->        margin: 0 auto;<!-- [et_pb_line_break_holder] -->        background: transparent;<!-- [et_pb_line_break_holder] -->        border-radius: 0;<!-- [et_pb_line_break_holder] -->        overflow: visible;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    #earth-canvas {<!-- [et_pb_line_break_holder] -->        width: 100%;<!-- [et_pb_line_break_holder] -->        height: 100%;<!-- [et_pb_line_break_holder] -->        display: block;<!-- [et_pb_line_break_holder] -->        background: transparent;<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        top: 0;<!-- [et_pb_line_break_holder] -->        left: 0;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .loading-overlay {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        top: 0;<!-- [et_pb_line_break_holder] -->        left: 0;<!-- [et_pb_line_break_holder] -->        right: 0;<!-- [et_pb_line_break_holder] -->        bottom: 0;<!-- [et_pb_line_break_holder] -->        background: rgba(0, 0, 0, 0.8);<!-- [et_pb_line_break_holder] -->        display: flex;<!-- [et_pb_line_break_holder] -->        align-items: center;<!-- [et_pb_line_break_holder] -->        justify-content: center;<!-- [et_pb_line_break_holder] -->        border-radius: 16px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .loading-text {<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 16px;<!-- [et_pb_line_break_holder] -->        font-weight: 500;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-state {<!-- [et_pb_line_break_holder] -->        padding: 32px;<!-- [et_pb_line_break_holder] -->        text-align: center;<!-- [et_pb_line_break_holder] -->        color: #ef4444;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-title {<!-- [et_pb_line_break_holder] -->        font-weight: 600;<!-- [et_pb_line_break_holder] -->        margin-bottom: 8px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-message {<!-- [et_pb_line_break_holder] -->        color: #999999;<!-- [et_pb_line_break_holder] -->        font-size: 14px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .spinner {<!-- [et_pb_line_break_holder] -->        width: 24px;<!-- [et_pb_line_break_holder] -->        height: 24px;<!-- [et_pb_line_break_holder] -->        border: 2px solid #333333;<!-- [et_pb_line_break_holder] -->        border-top: 2px solid #ffffff;<!-- [et_pb_line_break_holder] -->        border-radius: 50%;<!-- [et_pb_line_break_holder] -->        animation: spin 1s linear infinite;<!-- [et_pb_line_break_holder] -->        margin-right: 12px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    @keyframes spin {<!-- [et_pb_line_break_holder] -->        0% { transform: rotate(0deg); }<!-- [et_pb_line_break_holder] -->        100% { transform: rotate(360deg); }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .phase-text {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        left: 50%;<!-- [et_pb_line_break_holder] -->        bottom: 36%;<!-- [et_pb_line_break_holder] -->        transform: translate(-50%, 12px);<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 18px;<!-- [et_pb_line_break_holder] -->        font-weight: 700;<!-- [et_pb_line_break_holder] -->        letter-spacing: 0.4px;<!-- [et_pb_line_break_holder] -->        text-shadow: 0 2px 10px rgba(0,0,0,0.55);<!-- [et_pb_line_break_holder] -->        opacity: 0;<!-- [et_pb_line_break_holder] -->        transition: opacity 0.35s ease, transform 0.35s ease;<!-- [et_pb_line_break_holder] -->        pointer-events: none;<!-- [et_pb_line_break_holder] -->        max-width: 84%;<!-- [et_pb_line_break_holder] -->        text-align: center;<!-- [et_pb_line_break_holder] -->        line-height: 1.45;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .phase-text.visible {<!-- [et_pb_line_break_holder] -->        opacity: 1;<!-- [et_pb_line_break_holder] -->        transform: translate(-50%, 0);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .autoplay-btn {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        right: 32px;<!-- [et_pb_line_break_holder] -->        top: 32px;<!-- [et_pb_line_break_holder] -->        padding: 10px 14px;<!-- [et_pb_line_break_holder] -->        border-radius: 12px;<!-- [et_pb_line_break_holder] -->        border: 1px solid rgba(255,255,255,0.3);<!-- [et_pb_line_break_holder] -->        background: rgba(0,0,0,0.45);<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 13px;<!-- [et_pb_line_break_holder] -->        font-weight: 600;<!-- [et_pb_line_break_holder] -->        cursor: pointer;<!-- [et_pb_line_break_holder] -->        backdrop-filter: blur(6px);<!-- [et_pb_line_break_holder] -->        transition: background 0.2s ease, transform 0.2s ease, opacity 0.2s ease;<!-- [et_pb_line_break_holder] -->        z-index: 5;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .autoplay-btn:hover {<!-- [et_pb_line_break_holder] -->        background: rgba(0,0,0,0.65);<!-- [et_pb_line_break_holder] -->        transform: translateY(-1px);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .autoplay-btn:active {<!-- [et_pb_line_break_holder] -->        transform: translateY(0);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><\/style>\n<p><!-- [et_pb_line_break_holder] --><\/p>\n<div class=\"earth-sticky-wrapper\" id=\"earth-sticky-wrapper\"><!-- [et_pb_line_break_holder] -->    <\/p>\n<div class=\"earth-container\"><!-- [et_pb_line_break_holder] -->        <canvas id=\"earth-canvas\"><\/canvas><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"loading-overlay\" class=\"loading-overlay\"><!-- [et_pb_line_break_holder] -->            <\/p>\n<div class=\"spinner\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->            <\/p>\n<div class=\"loading-text\">Loading Earth data&#8230;<\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"tooltip\" style=\"position: absolute; display: none; background: rgba(0,0,0,0.8); color: white; padding: 5px; border-radius: 3px; pointer-events: none; z-index: 1000;\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"phase-text\" class=\"phase-text\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->        <button id=\"autoplay-btn\" class=\"autoplay-btn\">Autoplay<\/button><!-- [et_pb_line_break_holder] -->    <\/div>\n<p><!-- [et_pb_line_break_holder] --><\/div>\n<p><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --><script><!-- [et_pb_line_break_holder] -->    \/\/ Fallback dataset used if the remote universities.json cannot be loaded<!-- [et_pb_line_break_holder] -->    const universityData = [<!-- [et_pb_line_break_holder] -->        {\"name\":\"Vancouver Island University\",\"lat\":\"49.1578851\",\"lng\":\"-123.9656421\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"International College of Management, Sydney (ICMS)\",\"lat\":\"-33.8040340\",\"lng\":\"151.2937461\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of North Carolina Wilmington\",\"lat\":\"34.2249827\",\"lng\":\"-77.8690774\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Griffith College Dublin\",\"lat\":\"53.3311882\",\"lng\":\"-6.2802781\",\"country\":\"Ireland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EUSA University Centre, Seville, Spain\",\"lat\":\"37.3754972\",\"lng\":\"-5.9806833\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Cesine Santander\",\"lat\":\"43.4715066\",\"lng\":\"-3.7915862\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Asia Pacific University of Technology & Innovation (APU)\",\"lat\":\"3.0479178\",\"lng\":\"101.6892466\",\"country\":\"Malaysia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Francisco State University\",\"lat\":\"37.7245167\",\"lng\":\"-122.4799957\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"International Business School Budapest\",\"lat\":\"47.5629570\",\"lng\":\"19.0541941\",\"country\":\"Hungary\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EU Business School Barcelona\",\"lat\":\"41.3825802\",\"lng\":\"2.1770730\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Institut Sup\u00e9rieur de Gestion (ISG) Paris\",\"lat\":\"48.8672799\",\"lng\":\"2.2758497\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Woosong University\",\"lat\":\"36.3392944\",\"lng\":\"127.4506140\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"SolBridge International School of Business\",\"lat\":\"36.3383278\",\"lng\":\"127.4324307\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Europea de Madrid\",\"lat\":\"40.3727017\",\"lng\":\"-3.9185823\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"St. Francis College New York\",\"lat\":\"40.6900603\",\"lng\":\"-73.9865997\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universitat Aut\u00f2noma de Barcelona\",\"lat\":\"41.5015527\",\"lng\":\"2.1062607\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of the Fraser Valley\",\"lat\":\"49.0283443\",\"lng\":\"-122.2849135\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad San Ignacio de Loyola\",\"lat\":\"-12.0734419\",\"lng\":\"-76.9484914\",\"country\":\"Peru\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Francisco de Vitoria Madrid\",\"lat\":\"40.4402647\",\"lng\":\"-3.8339056\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"American College of Thessaloniki\",\"lat\":\"40.5684100\",\"lng\":\"22.9912375\",\"country\":\"Greece\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Hankuk University of Foreign Studies\",\"lat\":\"37.5970815\",\"lng\":\"127.0587413\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Vrije Universiteit Amsterdam\",\"lat\":\"52.3340539\",\"lng\":\"4.8651882\",\"country\":\"Netherlands\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Austral\",\"lat\":\"-34.5934652\",\"lng\":\"-58.3833079\",\"country\":\"Argentina\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad de Alicante\",\"lat\":\"38.3850684\",\"lng\":\"-0.5146732\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universit\u00e0 Europea di Roma\",\"lat\":\"41.8791802\",\"lng\":\"12.3946472\",\"country\":\"Italy\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Cat\u00f3lica San Antonio de Murcia\",\"lat\":\"38.0042077\",\"lng\":\"-1.1774782\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Xi'an Jiaotong-Liverpool University\",\"lat\":\"31.2787148\",\"lng\":\"120.7237629\",\"country\":\"China\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universit\u00e0 Cattolica del Sacro Cuore\",\"lat\":\"45.4609998\",\"lng\":\"9.1769914\",\"country\":\"Italy\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"College of the Desert\",\"lat\":\"33.7324666\",\"lng\":\"-116.3868288\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Santa Barbara City College\",\"lat\":\"34.4058643\",\"lng\":\"-119.6974204\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Victoria University of Wellington\",\"lat\":\"-41.2793942\",\"lng\":\"174.7783525\",\"country\":\"New Zealand\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"City University of Seattle\",\"lat\":\"47.6177084\",\"lng\":\"-122.3444624\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of the Sunshine Coast (UniSC)\",\"lat\":\"-26.7159208\",\"lng\":\"153.0563306\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of Brighton\",\"lat\":\"50.8421128\",\"lng\":\"-0.1194002\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"London South Bank University\",\"lat\":\"51.4991489\",\"lng\":\"-0.0970188\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Dorset College Dublin\",\"lat\":\"53.3582148\",\"lng\":\"-6.2577212\",\"country\":\"Ireland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"BI Norwegian Business School\",\"lat\":\"59.9488700\",\"lng\":\"10.7682070\",\"country\":\"Norway\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EU Business School Geneva\",\"lat\":\"46.2067776\",\"lng\":\"6.1451264\",\"country\":\"Switzerland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"London Metropolitan University\",\"lat\":\"51.5522032\",\"lng\":\"-0.1111950\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Adolfo Ibanez\",\"lat\":\"-33.0196718\",\"lng\":\"-71.5304035\",\"country\":\"Chile\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Jose State University\",\"lat\":\"37.3351902\",\"lng\":\"-121.8812255\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Europea de Valencia\",\"lat\":\"39.4755093\",\"lng\":\"-0.3652411\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Latinoamericana de Cienca y Tecnologia\",\"lat\":\"9.9325427\",\"lng\":\"-84.0795782\",\"country\":\"Costa Rica\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Diego State University\",\"lat\":\"32.7760640\",\"lng\":\"-117.0702300\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Bond University\",\"lat\":\"-28.0724817\",\"lng\":\"153.4157960\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California San Diego\",\"lat\":\"32.8792438\",\"lng\":\"-117.2311247\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Alliant International University\",\"lat\":\"32.8966666\",\"lng\":\"-117.0938567\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"James Cook University Singapore\",\"lat\":\"1.3153627\",\"lng\":\"103.8760268\",\"country\":\"Singapore\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Capilano University\",\"lat\":\"49.3179806\",\"lng\":\"-123.0194770\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"James Cook University Australia\",\"lat\":\"-19.3293891\",\"lng\":\"146.7611734\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Nebrija University\",\"lat\":\"40.4295684\",\"lng\":\"-3.7130672\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California Santa Barbara\",\"lat\":\"34.4145523\",\"lng\":\"-119.8468383\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California Berkeley Extension\",\"lat\":\"37.8708393\",\"lng\":\"-122.2728630\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Westcliff University\",\"lat\":\"33.6853732\",\"lng\":\"-117.8480110\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"The University of Sydney\",\"lat\":\"-33.8854217\",\"lng\":\"151.1890168\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California, Los Angeles (UCLA) Extension\",\"lat\":\"34.0595933\",\"lng\":\"-118.4463883\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Auckland University of Technology\",\"lat\":\"-36.8525224\",\"lng\":\"174.7667714\",\"country\":\"New Zealand\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Pace University\",\"lat\":\"40.7111050\",\"lng\":\"-74.0046134\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Edinburgh Napier University\",\"lat\":\"55.9244726\",\"lng\":\"-3.2888614\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of Westminster\",\"lat\":\"51.5209064\",\"lng\":\"-0.1400730\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"IPAG Business School Paris\",\"lat\":\"48.8544327\",\"lng\":\"2.3312200\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Veritas\",\"lat\":\"9.9254713\",\"lng\":\"-84.0648763\",\"country\":\"Costa Rica\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"IPAG Business School Nice\",\"lat\":\"43.7145200\",\"lng\":\"7.2652823\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Royal Melbourne Institute of Technology Vietnam\",\"lat\":\"10.7755254\",\"lng\":\"106.7021047\",\"country\":\"Viet Nam\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Curtin University Dubai\",\"lat\":\"25.0742823\",\"lng\":\"55.1885387\",\"country\":\"United Arab Emirates\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Curtin University Singapore\",\"lat\":\"1.2884206\",\"lng\":\"103.7795669\",\"country\":\"Singapore\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"LCI Barcelona\",\"lat\":\"41.3994905\",\"lng\":\"2.1902139\",\"country\":\"Spain\"}<!-- [et_pb_line_break_holder] -->    ];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    class RotatingEarth {<!-- [et_pb_line_break_holder] -->        constructor(canvasId, options = {}) {<!-- [et_pb_line_break_holder] -->            this.canvas = document.getElementById(canvasId);<!-- [et_pb_line_break_holder] -->            this.context = this.canvas.getContext('2d');<!-- [et_pb_line_break_holder] -->            this.loadingOverlay = document.getElementById('loading-overlay');<!-- [et_pb_line_break_holder] -->            this.tooltip = document.getElementById('tooltip');<!-- [et_pb_line_break_holder] -->            this.phaseText = document.getElementById('phase-text');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.width = options.width || 800;<!-- [et_pb_line_break_holder] -->            this.height = options.height || 600;<!-- [et_pb_line_break_holder] -->            this.isLoading = true;<!-- [et_pb_line_break_holder] -->            this.error = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.allDots = [];<!-- [et_pb_line_break_holder] -->            this.landFeatures = null;<!-- [et_pb_line_break_holder] -->            this.universities = [];<!-- [et_pb_line_break_holder] -->            this.highlightedFeatureIndices = new Set();<!-- [et_pb_line_break_holder] -->            this.initialRotation = [0, -38];<!-- [et_pb_line_break_holder] -->            this.rotation = [...this.initialRotation];<!-- [et_pb_line_break_holder] -->            this.scrollBaseRotation = this.rotation[0];<!-- [et_pb_line_break_holder] -->            this.autoRotate = true;<!-- [et_pb_line_break_holder] -->            this.rotationSpeed = 0.25;<!-- [et_pb_line_break_holder] -->            this.rotationTimer = null;<!-- [et_pb_line_break_holder] -->            this.logoImage = null;<!-- [et_pb_line_break_holder] -->            this.isInteracting = false;<!-- [et_pb_line_break_holder] -->            this.bgColor = '#005499';<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->            this.autoPlayStart = 0;<!-- [et_pb_line_break_holder] -->            this.autoPlayDuration = 24000; \/\/ ms<!-- [et_pb_line_break_holder] -->            this.autoPlayHandle = null;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'globe';<!-- [et_pb_line_break_holder] -->            this.orthoProjection = null;<!-- [et_pb_line_break_holder] -->            this.flatProjection = null;<!-- [et_pb_line_break_holder] -->            this.path = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.graticuleGeo = d3.geoGraticule().step([15, 15])(); \/\/ MultiLineString<!-- [et_pb_line_break_holder] -->            this.sphereBoundary = this.buildSphereBoundaryCoords();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Continent tour configuration<!-- [et_pb_line_break_holder] -->            this.continentTours = [<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'Europe',<!-- [et_pb_line_break_holder] -->                    lng: 10,<!-- [et_pb_line_break_holder] -->                    lat: 50,<!-- [et_pb_line_break_holder] -->                    zoom: 2.5,<!-- [et_pb_line_break_holder] -->                    latRange: [35, 72],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[-25, 60]],<!-- [et_pb_line_break_holder] -->                    countries: [<!-- [et_pb_line_break_holder] -->                        'austria','belgium','bulgaria','croatia','cyprus','czechia','czech republic',<!-- [et_pb_line_break_holder] -->                        'denmark','estonia','finland','france','germany','greece','hungary','iceland',<!-- [et_pb_line_break_holder] -->                        'ireland','italy','latvia','lithuania','luxembourg','malta','netherlands',<!-- [et_pb_line_break_holder] -->                        'norway','poland','portugal','romania','slovakia','slovenia','spain','sweden',<!-- [et_pb_line_break_holder] -->                        'switzerland','united kingdom','uk','england','scotland','wales','northern ireland'<!-- [et_pb_line_break_holder] -->                    ]<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'North America',<!-- [et_pb_line_break_holder] -->                    lng: -100,<!-- [et_pb_line_break_holder] -->                    lat: 40,<!-- [et_pb_line_break_holder] -->                    zoom: 2.2,<!-- [et_pb_line_break_holder] -->                    latRange: [7, 83],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[-170, -50]],<!-- [et_pb_line_break_holder] -->                    countries: ['canada','united states','united states of america','usa','us']<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'South America',<!-- [et_pb_line_break_holder] -->                    lng: -60,<!-- [et_pb_line_break_holder] -->                    lat: -20,<!-- [et_pb_line_break_holder] -->                    zoom: 2.3,<!-- [et_pb_line_break_holder] -->                    latRange: [-60, 15],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[-90, -30]],<!-- [et_pb_line_break_holder] -->                    countries: ['argentina','bolivia','brazil','chile','colombia','ecuador','guyana','paraguay','peru','suriname','uruguay','venezuela', 'costa rica']<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'Asia',<!-- [et_pb_line_break_holder] -->                    lng: 100,<!-- [et_pb_line_break_holder] -->                    lat: 30,<!-- [et_pb_line_break_holder] -->                    zoom: 2.2,<!-- [et_pb_line_break_holder] -->                    latRange: [-15, 80],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[35, 180], [-180, -100]],<!-- [et_pb_line_break_holder] -->                    countries: [<!-- [et_pb_line_break_holder] -->                        'china','japan','south korea','korea','republic of korea','north korea','taiwan',<!-- [et_pb_line_break_holder] -->                        'hong kong','macau','india','pakistan','bangladesh','sri lanka','nepal','bhutan',<!-- [et_pb_line_break_holder] -->                        'maldives','indonesia','malaysia','philippines','vietnam', 'viet nam', 'thailand','laos','cambodia',<!-- [et_pb_line_break_holder] -->                        'myanmar','brunei','singapore','mongolia','kazakhstan','kyrgyzstan','uzbekistan',<!-- [et_pb_line_break_holder] -->                        'tajikistan','turkmenistan','afghanistan','iran','iraq','saudi arabia','united arab emirates',<!-- [et_pb_line_break_holder] -->                        'uae','qatar','kuwait','bahrain','oman','yemen','jordan','lebanon','israel','palestine',<!-- [et_pb_line_break_holder] -->                        'turkey','georgia','armenia','azerbaijan'<!-- [et_pb_line_break_holder] -->                    ]<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'Oceania',<!-- [et_pb_line_break_holder] -->                    lng: 150,<!-- [et_pb_line_break_holder] -->                    lat: -25,<!-- [et_pb_line_break_holder] -->                    zoom: 2.5,<!-- [et_pb_line_break_holder] -->                    latRange: [-50, 10],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[110, 190], [-190, -140]],<!-- [et_pb_line_break_holder] -->                    countries: ['australia','new zealand','fiji','papua new guinea','samoa','tonga','vanuatu','solomon islands','new caledonia']<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            ];<!-- [et_pb_line_break_holder] -->            this._continentCounts = new Map();<!-- [et_pb_line_break_holder] -->            this.currentTourIndex = -1;<!-- [et_pb_line_break_holder] -->            this.baseFlatScale = null;<!-- [et_pb_line_break_holder] -->            this.showLabels = false;<!-- [et_pb_line_break_holder] -->            this.currentZoom = 1;<!-- [et_pb_line_break_holder] -->            this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->            this.zoomPhaseProgress = 0;<!-- [et_pb_line_break_holder] -->            this.zoomPhaseProgress = 0; \/\/ 0 at zoom start, 1 at end to fade graticule<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Tiny depth stars for globe-only state<!-- [et_pb_line_break_holder] -->            this.starParticles = this.createStarParticles(140);<!-- [et_pb_line_break_holder] -->            this._lastStarUpdate = performance.now ? performance.now() : Date.now();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.init();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        init() {<!-- [et_pb_line_break_holder] -->            this.setupCanvas();<!-- [et_pb_line_break_holder] -->            this.setupProjection();<!-- [et_pb_line_break_holder] -->            this.setupInteractions();<!-- [et_pb_line_break_holder] -->            window.addEventListener('resize', () => this.handleResize(), { passive: true });<!-- [et_pb_line_break_holder] -->            this.loadWorldData();<!-- [et_pb_line_break_holder] -->            this.loadLogo();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        getDefaultContainerSize() {<!-- [et_pb_line_break_holder] -->            const containerWidth = Math.min(this.width, Math.max(320, window.innerWidth - 40));<!-- [et_pb_line_break_holder] -->            const maxHeight = Math.min(<!-- [et_pb_line_break_holder] -->                this.height,<!-- [et_pb_line_break_holder] -->                window.innerHeight - 40,<!-- [et_pb_line_break_holder] -->                containerWidth * 0.55 \/\/ align closer to final flat map height<!-- [et_pb_line_break_holder] -->            );<!-- [et_pb_line_break_holder] -->            const containerHeight = Math.max(320, maxHeight);<!-- [et_pb_line_break_holder] -->            return { containerWidth, containerHeight };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setCanvasSize(containerWidth, containerHeight, dprOverride) {<!-- [et_pb_line_break_holder] -->            const dpr = dprOverride ?? Math.min(3, window.devicePixelRatio || 2);<!-- [et_pb_line_break_holder] -->            this.dpr = dpr;<!-- [et_pb_line_break_holder] -->            \/\/ Smaller globe radius so the start view sits comfortably in the container<!-- [et_pb_line_break_holder] -->            this.radius = Math.min(containerWidth, containerHeight) \/ 3.0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.canvas.width = containerWidth * dpr;<!-- [et_pb_line_break_holder] -->            this.canvas.height = containerHeight * dpr;<!-- [et_pb_line_break_holder] -->            this.canvas.style.width = `${containerWidth}px`;<!-- [et_pb_line_break_holder] -->            this.canvas.style.height = `${containerHeight}px`;<!-- [et_pb_line_break_holder] -->            this.context.setTransform(dpr, 0, 0, dpr, 0, 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.containerWidth = containerWidth;<!-- [et_pb_line_break_holder] -->            this.containerHeight = containerHeight;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.setupProjection();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupCanvas() {<!-- [et_pb_line_break_holder] -->            const { containerWidth, containerHeight } = this.getDefaultContainerSize();<!-- [et_pb_line_break_holder] -->            this.setCanvasSize(containerWidth, containerHeight);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        sizeFactor() {<!-- [et_pb_line_break_holder] -->            return window.innerWidth < 768 ? 0.7 : 1;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleResize() {<!-- [et_pb_line_break_holder] -->            if (this.isRecording) return; \/\/ avoid disrupting active capture<!-- [et_pb_line_break_holder] -->            const { containerWidth, containerHeight } = this.getDefaultContainerSize();<!-- [et_pb_line_break_holder] -->            const oldBaseScale = this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.setCanvasSize(containerWidth, containerHeight);<!-- [et_pb_line_break_holder] -->            \/\/ Preserve relative zoom if we're in a continent tour<!-- [et_pb_line_break_holder] -->            if (oldBaseScale && this.mode === 'flat') {<!-- [et_pb_line_break_holder] -->                const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->                const zoomRatio = currentScale \/ oldBaseScale;<!-- [et_pb_line_break_holder] -->                this.flatProjection.scale(this.baseFlatScale * zoomRatio);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupProjection() {<!-- [et_pb_line_break_holder] -->            this.orthoProjection = d3.geoOrthographic()<!-- [et_pb_line_break_holder] -->                .scale(this.radius)<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2])<!-- [et_pb_line_break_holder] -->                .clipAngle(90)<!-- [et_pb_line_break_holder] -->                .rotate(this.rotation);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const flatScale = (this.containerWidth \/ (2 * Math.PI)) * 0.9;<!-- [et_pb_line_break_holder] -->            this.baseFlatScale = flatScale;<!-- [et_pb_line_break_holder] -->            this.flatProjection = d3.geoEquirectangular()<!-- [et_pb_line_break_holder] -->                .center([0, 0])<!-- [et_pb_line_break_holder] -->                .scale(flatScale)<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.path = d3.geoPath()<!-- [et_pb_line_break_holder] -->                .projection(this.orthoProjection)<!-- [et_pb_line_break_holder] -->                .context(this.context);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        pointInPolygon(point, polygon) {<!-- [et_pb_line_break_holder] -->            const [x, y] = point;<!-- [et_pb_line_break_holder] -->            let inside = false;<!-- [et_pb_line_break_holder] -->            for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {<!-- [et_pb_line_break_holder] -->                const [xi, yi] = polygon[i];<!-- [et_pb_line_break_holder] -->                const [xj, yj] = polygon[j];<!-- [et_pb_line_break_holder] -->                if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) \/ (yj - yi) + xi) {<!-- [et_pb_line_break_holder] -->                    inside = !inside;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return inside;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        pointInFeature(point, feature) {<!-- [et_pb_line_break_holder] -->            const geometry = feature.geometry;<!-- [et_pb_line_break_holder] -->            if (geometry.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                const coordinates = geometry.coordinates;<!-- [et_pb_line_break_holder] -->                if (!this.pointInPolygon(point, coordinates[0])) return false;<!-- [et_pb_line_break_holder] -->                for (let i = 1; i < coordinates.length; i++) {<!-- [et_pb_line_break_holder] -->                    if (this.pointInPolygon(point, coordinates[i])) return false;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                return true;<!-- [et_pb_line_break_holder] -->            } else if (geometry.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                for (const polygon of geometry.coordinates) {<!-- [et_pb_line_break_holder] -->                    if (this.pointInPolygon(point, polygon[0])) {<!-- [et_pb_line_break_holder] -->                        let inHole = false;<!-- [et_pb_line_break_holder] -->                        for (let i = 1; i < polygon.length; i++) {<!-- [et_pb_line_break_holder] -->                            if (this.pointInPolygon(point, polygon[i])) {<!-- [et_pb_line_break_holder] -->                                inHole = true;<!-- [et_pb_line_break_holder] -->                                break;<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                        if (!inHole) return true;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                return false;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return false;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        generateDotsInPolygon(feature, dotSpacing = 16) {<!-- [et_pb_line_break_holder] -->            \/\/ More robust sampler that handles antimeridian-crossing countries (e.g., Russia)<!-- [et_pb_line_break_holder] -->            const dots = [];<!-- [et_pb_line_break_holder] -->            const stepSize = Math.max(0.5, dotSpacing * 0.08); \/\/ cap max step to keep large countries filled<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const normLng = (lng) => {<!-- [et_pb_line_break_holder] -->                const n = ((lng + 180) % 360 + 360) % 360 - 180;<!-- [et_pb_line_break_holder] -->                return n === -180 ? 180 : n; \/\/ keep boundaries consistent<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const samplePolygon = (rings) => {<!-- [et_pb_line_break_holder] -->                const lons = rings.flat().map(([lng]) => normLng(lng));<!-- [et_pb_line_break_holder] -->                const lats = rings.flat().map(([, lat]) => lat);<!-- [et_pb_line_break_holder] -->                const minLng = Math.min(...lons);<!-- [et_pb_line_break_holder] -->                const maxLng = Math.max(...lons);<!-- [et_pb_line_break_holder] -->                const minLat = Math.min(...lats);<!-- [et_pb_line_break_holder] -->                const maxLat = Math.max(...lats);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const crossesDateline = maxLng - minLng > 180;<!-- [et_pb_line_break_holder] -->                const ranges = crossesDateline ? [[-180, 0], [0, 180]] : [[minLng, maxLng]];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                ranges.forEach(([lngStart, lngEnd]) => {<!-- [et_pb_line_break_holder] -->                    for (let lng = lngStart; lng <= lngEnd; lng += stepSize) {<!-- [et_pb_line_break_holder] -->                        for (let lat = minLat; lat <= maxLat; lat += stepSize) {<!-- [et_pb_line_break_holder] -->                            \/\/ Try with normalized and shifted longitudes so dateline polygons still hit<!-- [et_pb_line_break_holder] -->                            const candidates = [<!-- [et_pb_line_break_holder] -->                                [lng, lat],<!-- [et_pb_line_break_holder] -->                                [lng + 360, lat],<!-- [et_pb_line_break_holder] -->                                [lng - 360, lat]<!-- [et_pb_line_break_holder] -->                            ];<!-- [et_pb_line_break_holder] -->                            if (candidates.some((p) => d3.geoContains(feature, p))) {<!-- [et_pb_line_break_holder] -->                                dots.push([lng, lat]);<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->            if (!geom) return dots;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                samplePolygon(geom.coordinates);<!-- [et_pb_line_break_holder] -->            } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                geom.coordinates.forEach((poly) => samplePolygon(poly));<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            return dots;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        render() {<!-- [et_pb_line_break_holder] -->            this.context.clearRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            this.context.save();<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = this.bgColor || '#ffffff';<!-- [et_pb_line_break_holder] -->            this.context.fillRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            this.context.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.mode === 'globe') {<!-- [et_pb_line_break_holder] -->                this.renderGlobe();<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                this.renderFlat();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        isZoomingFlat() {<!-- [et_pb_line_break_holder] -->            return this.mode === 'flat' && this.zoomPhaseProgress > 0.01;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        angularDistance(lon1, lat1, lon2, lat2) {<!-- [et_pb_line_break_holder] -->            const toRad = (d) => (d * Math.PI) \/ 180;<!-- [et_pb_line_break_holder] -->            const \u03c61 = toRad(lat1);<!-- [et_pb_line_break_holder] -->            const \u03c62 = toRad(lat2);<!-- [et_pb_line_break_holder] -->            const \u0394\u03bb = toRad(lon2 - lon1);<!-- [et_pb_line_break_holder] -->            const sinDLat = Math.sin((\u03c62 - \u03c61) \/ 2);<!-- [et_pb_line_break_holder] -->            const sinDLon = Math.sin(\u0394\u03bb \/ 2);<!-- [et_pb_line_break_holder] -->            const a = sinDLat * sinDLat + Math.cos(\u03c61) * Math.cos(\u03c62) * sinDLon * sinDLon;<!-- [et_pb_line_break_holder] -->            return 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderGlobe() {<!-- [et_pb_line_break_holder] -->            const projection = this.orthoProjection;<!-- [et_pb_line_break_holder] -->            this.path.projection(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentScale = projection.scale();<!-- [et_pb_line_break_holder] -->            const scaleFactor = currentScale \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] -->            const centerX = this.containerWidth \/ 2;<!-- [et_pb_line_break_holder] -->            const centerY = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] -->            const centerCoord = projection.invert([centerX, centerY]);<!-- [et_pb_line_break_holder] -->            const isFront = (lon, lat) => d3.geoDistance([lon, lat], centerCoord) <= Math.PI \/ 2;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Stars sit behind the earth and only show in globe mode<!-- [et_pb_line_break_holder] -->            this.renderStars(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            projection.clipAngle(180);<!-- [et_pb_line_break_holder] -->            this.context.save();<!-- [et_pb_line_break_holder] -->            this.context.filter = 'blur(1px)';<!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.globalAlpha = 0.20;<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            this.context.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            projection.clipAngle(90);<!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->            this.context.lineWidth = 2 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->            this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                projection.clipAngle(180);<!-- [et_pb_line_break_holder] -->                const graticuleBack = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticuleBack());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.14 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.30;<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    this.context.beginPath();<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                    this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    this.context.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.18;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                projection.clipAngle(90);<!-- [et_pb_line_break_holder] -->                const graticuleFront = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticuleFront());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.18 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const dotRadius = 0.6 * sizeFactor; \/\/ keep dots small and consistent<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected &&<!-- [et_pb_line_break_holder] -->                        projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                        projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                        if (isFront(dot.lng, dot.lat)) {<!-- [et_pb_line_break_holder] -->                            const dx = projected[0] - centerX;<!-- [et_pb_line_break_holder] -->                            const dy = projected[1] - centerY;<!-- [et_pb_line_break_holder] -->                            const dist = Math.sqrt(dx * dx + dy * dy);<!-- [et_pb_line_break_holder] -->                            if (dist <= currentScale) {<!-- [et_pb_line_break_holder] -->                                const opacity = (90 - Math.abs(dot.lat)) \/ 90;<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = opacity;<!-- [et_pb_line_break_holder] -->                                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                                this.context.arc(projected[0], projected[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                                this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                                this.context.fill();<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.universities.forEach((uni) => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected &&<!-- [et_pb_line_break_holder] -->                        projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                        projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                        this.context.shadowColor = 'rgba(0,106,179,0.6)';<!-- [et_pb_line_break_holder] -->                        this.context.shadowBlur = 3 * scaleFactor;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetX = 2 * scaleFactor;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetY = 2 * scaleFactor;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                        this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetX = 0;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetY = 0;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.logoImage && this.logoImage.complete) {<!-- [et_pb_line_break_holder] -->                this.context.save();<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.context.arc(this.containerWidth \/ 2, this.containerHeight \/ 2, projection.scale(), 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                this.context.clip();<!-- [et_pb_line_break_holder] -->                if (this.isInteracting) {<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = 0.25;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                const logoSize = 2.2 * projection.scale();<!-- [et_pb_line_break_holder] -->                const x = this.containerWidth \/ 2 - logoSize \/ 2;<!-- [et_pb_line_break_holder] -->                const y = this.containerHeight \/ 2 - logoSize \/ 2;<!-- [et_pb_line_break_holder] -->                this.context.drawImage(this.logoImage, x, y, logoSize, logoSize);<!-- [et_pb_line_break_holder] -->                this.context.restore();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderFlat() {<!-- [et_pb_line_break_holder] -->            const projection = this.flatProjection;<!-- [et_pb_line_break_holder] -->            this.path.projection(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentScale = projection.scale();<!-- [et_pb_line_break_holder] -->            const scaleFactor = currentScale \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            \/\/ Only draw border when not in zooming flat tour phase<!-- [et_pb_line_break_holder] -->            if (!this.isZoomingFlat()) {<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                const graticule = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticule());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.8 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.4 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Fills with highlight like morph<!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.35;<!-- [et_pb_line_break_holder] -->                this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    this.context.beginPath();<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    this.context.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const dotRadius = 0.6 * sizeFactor; \/\/ consistent size<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Draw markers (consistent size across animation)<!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                const markerBoxes = [];<!-- [et_pb_line_break_holder] -->                const placements = [];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Draw markers and collect their boxes<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                        \/\/ store small box for overlap checks<!-- [et_pb_line_break_holder] -->                        const r = markerRadius + 2;<!-- [et_pb_line_break_holder] -->                        const box = {<!-- [et_pb_line_break_holder] -->                            x: projected[0] - r,<!-- [et_pb_line_break_holder] -->                            y: projected[1] - r,<!-- [et_pb_line_break_holder] -->                            w: r * 2,<!-- [et_pb_line_break_holder] -->                            h: r * 2<!-- [et_pb_line_break_holder] -->                        };<!-- [et_pb_line_break_holder] -->                        markerBoxes.push(box);<!-- [et_pb_line_break_holder] -->                        placements.push({<!-- [et_pb_line_break_holder] -->                            uni,<!-- [et_pb_line_break_holder] -->                            cx: projected[0],<!-- [et_pb_line_break_holder] -->                            cy: projected[1],<!-- [et_pb_line_break_holder] -->                            markerBox: box<!-- [et_pb_line_break_holder] -->                        });<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ University markers (labels removed per request)<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Country labels for highlighted countries (only during zoom\/tour)<!-- [et_pb_line_break_holder] -->                if (this.showLabels && this.activeContinentIdx != null) {<!-- [et_pb_line_break_holder] -->                    const countryFontSize = 12 * sizeFactor;<!-- [et_pb_line_break_holder] -->                    this.context.font = `600 ${countryFontSize}px \"Inter\", \"Segoe UI\", sans-serif`;<!-- [et_pb_line_break_holder] -->                    this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    this.context.textBaseline = 'middle';<!-- [et_pb_line_break_holder] -->                    this.context.textAlign = 'left';<!-- [et_pb_line_break_holder] -->                    this.context.shadowColor = 'rgba(0,0,0,0.55)';<!-- [et_pb_line_break_holder] -->                    this.context.shadowBlur = 6;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const placedCountry = [...markerBoxes]; \/\/ avoid overlapping markers<!-- [et_pb_line_break_holder] -->                    const intersects = (a, b) => !(<!-- [et_pb_line_break_holder] -->                        a.x + a.w < b.x ||<!-- [et_pb_line_break_holder] -->                        b.x + b.w < a.x ||<!-- [et_pb_line_break_holder] -->                        a.y + a.h < b.y ||<!-- [et_pb_line_break_holder] -->                        b.y + b.h < a.y<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const gaps = [0, 12, 24, 36, 48, 60];<!-- [et_pb_line_break_holder] -->                    const directions = [<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: 0 },   \/\/ right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: 0 },  \/\/ left<!-- [et_pb_line_break_holder] -->                        { dx: 0, dy: -1 },  \/\/ up<!-- [et_pb_line_break_holder] -->                        { dx: 0, dy: 1 },   \/\/ down<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: -1 },  \/\/ up-right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: -1 }, \/\/ up-left<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: 1 },   \/\/ down-right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: 1 },  \/\/ down-left<!-- [et_pb_line_break_holder] -->                    ];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const margin = 8;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const activeContinent = this.continentTours[this.activeContinentIdx];<!-- [et_pb_line_break_holder] -->                    const maxDistRad = Math.PI * 0.35; \/\/ tighten: ~63 degrees cone per continent<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    if (this.highlightedFeatureIndices && this.landFeatures?.features?.length && activeContinent) {<!-- [et_pb_line_break_holder] -->                        this.highlightedFeatureIndices.forEach(idx => {<!-- [et_pb_line_break_holder] -->                            const feature = this.landFeatures.features[idx];<!-- [et_pb_line_break_holder] -->                            if (!feature) return;<!-- [et_pb_line_break_holder] -->                            const name = feature.properties?.name;<!-- [et_pb_line_break_holder] -->                            if (!name) return;<!-- [et_pb_line_break_holder] -->                            const centroid = d3.geoCentroid(feature);<!-- [et_pb_line_break_holder] -->                            const projected = projection(centroid);<!-- [et_pb_line_break_holder] -->                            if (!projected) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            \/\/ Skip if centroid far from active continent center<!-- [et_pb_line_break_holder] -->                            const angDist = this.angularDistance(<!-- [et_pb_line_break_holder] -->                                activeContinent.lng, activeContinent.lat,<!-- [et_pb_line_break_holder] -->                                centroid[0], centroid[1]<!-- [et_pb_line_break_holder] -->                            );<!-- [et_pb_line_break_holder] -->                            if (angDist > maxDistRad) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        \/\/ Skip if the feature\u2019s projected bounds are off-screen<!-- [et_pb_line_break_holder] -->                        const b = d3.geoPath().projection(projection).bounds(feature);<!-- [et_pb_line_break_holder] -->                        const [minX, minY] = b[0];<!-- [et_pb_line_break_holder] -->                        const [maxX, maxY] = b[1];<!-- [et_pb_line_break_holder] -->                        const intersectsViewport = !(<!-- [et_pb_line_break_holder] -->                            maxX < 0 || maxY < 0 ||<!-- [et_pb_line_break_holder] -->                            minX > this.containerWidth || minY > this.containerHeight<!-- [et_pb_line_break_holder] -->                        );<!-- [et_pb_line_break_holder] -->                        if (!intersectsViewport) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            const width = this.context.measureText(name).width;<!-- [et_pb_line_break_holder] -->                            const height = countryFontSize * 1.2;<!-- [et_pb_line_break_holder] -->                            const cx = projected[0];<!-- [et_pb_line_break_holder] -->                            const cy = projected[1];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            let chosen = null;<!-- [et_pb_line_break_holder] -->                            let bestOverlap = Infinity;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            gaps.forEach((gap) => {<!-- [et_pb_line_break_holder] -->                                directions.forEach(({ dx, dy }) => {<!-- [et_pb_line_break_holder] -->                                    const offsetX = dx * gap;<!-- [et_pb_line_break_holder] -->                                    const offsetY = dy * gap;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    let x = cx + (dx >= 0 ? 8 + offsetX : -(width + 8 - offsetX));<!-- [et_pb_line_break_holder] -->                                    let y = cy - height \/ 2 + offsetY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    if (dx === 0) x = cx - width \/ 2 + offsetX;<!-- [et_pb_line_break_holder] -->                                    if (dy === 0) y = cy - height \/ 2 + offsetY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    \/\/ Clamp to stay within canvas<!-- [et_pb_line_break_holder] -->                                    x = Math.max(margin, Math.min(x, this.containerWidth - width - margin));<!-- [et_pb_line_break_holder] -->                                    y = Math.max(margin, Math.min(y, this.containerHeight - height - margin));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    const box = { x, y, w: width, h: height };<!-- [et_pb_line_break_holder] -->                                    const overlap = placedCountry.reduce((acc, b) => acc + (intersects(b, box) ? 1 : 0), 0);<!-- [et_pb_line_break_holder] -->                                    if (overlap < bestOverlap) {<!-- [et_pb_line_break_holder] -->                                        bestOverlap = overlap;<!-- [et_pb_line_break_holder] -->                                        chosen = { x, y, box, dx, dy, gap };<!-- [et_pb_line_break_holder] -->                                        if (overlap === 0) return;<!-- [et_pb_line_break_holder] -->                                    }<!-- [et_pb_line_break_holder] -->                                });<!-- [et_pb_line_break_holder] -->                            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            if (!chosen) return;<!-- [et_pb_line_break_holder] -->                            placedCountry.push(chosen.box);<!-- [et_pb_line_break_holder] -->                            const textY = chosen.y + height \/ 2;<!-- [et_pb_line_break_holder] -->                            this.context.fillText(name, chosen.x, textY);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            \/\/ Leader line if offset<!-- [et_pb_line_break_holder] -->                            const distX = chosen.x - cx;<!-- [et_pb_line_break_holder] -->                            const distY = textY - cy;<!-- [et_pb_line_break_holder] -->                            const leaderDist = Math.sqrt(distX * distX + distY * distY);<!-- [et_pb_line_break_holder] -->                            if (leaderDist > 10) {<!-- [et_pb_line_break_holder] -->                                let targetX = chosen.x;<!-- [et_pb_line_break_holder] -->                                let targetY = textY;<!-- [et_pb_line_break_holder] -->                                if (chosen.dx > 0) targetX = chosen.x - 4;<!-- [et_pb_line_break_holder] -->                                else if (chosen.dx < 0) targetX = chosen.x + chosen.box.w + 4;<!-- [et_pb_line_break_holder] -->                                else targetX = cx;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                if (chosen.dy > 0) targetY = chosen.y - 4;<!-- [et_pb_line_break_holder] -->                                else if (chosen.dy < 0) targetY = chosen.y + chosen.box.h + 4;<!-- [et_pb_line_break_holder] -->                                else targetY = textY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                                this.context.moveTo(cx, cy);<!-- [et_pb_line_break_holder] -->                                this.context.lineTo(targetX, targetY);<!-- [et_pb_line_break_holder] -->                                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                                this.context.lineWidth = 1;<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = 0.85;<!-- [et_pb_line_break_holder] -->                                this.context.stroke();<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        });<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                    this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/**<!-- [et_pb_line_break_holder] -->         * Scroll-driven animation:<!-- [et_pb_line_break_holder] -->         * 0 \u2192 0.15: Globe rotation<!-- [et_pb_line_break_holder] -->         * 0.15 \u2192 0.25: Morph to flat map<!-- [et_pb_line_break_holder] -->         * 0.25 \u2192 0.35: Show full flat map (pause)<!-- [et_pb_line_break_holder] -->         * 0.35 \u2192 0.85: Continent tours (zoom\/pan with pauses)<!-- [et_pb_line_break_holder] -->         * 0.85 \u2192 1.00: Fold back to globe (loop-friendly)<!-- [et_pb_line_break_holder] -->         *\/<!-- [et_pb_line_break_holder] -->        applyScrollProgress(progress) {<!-- [et_pb_line_break_holder] -->            const p = Math.max(0, Math.min(1, progress));<!-- [et_pb_line_break_holder] -->            const rotatePortion = 0.15;<!-- [et_pb_line_break_holder] -->            const morphPortion = 0.10;<!-- [et_pb_line_break_holder] -->            const flatHoldPortion = 0.10;<!-- [et_pb_line_break_holder] -->            const foldBackPortion = 0.15;<!-- [et_pb_line_break_holder] -->            const morphEnd = rotatePortion + morphPortion;<!-- [et_pb_line_break_holder] -->            const flatHoldEnd = morphEnd + flatHoldPortion;<!-- [et_pb_line_break_holder] -->            const foldBackStart = 1 - foldBackPortion;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Stop any auto motion when scroll is in control<!-- [et_pb_line_break_holder] -->            if (this.autoRotate) {<!-- [et_pb_line_break_holder] -->                this.autoRotate = false;<!-- [et_pb_line_break_holder] -->                if (this.rotationTimer) {<!-- [et_pb_line_break_holder] -->                    this.rotationTimer.stop();<!-- [et_pb_line_break_holder] -->                    this.rotationTimer = null;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Default graticule fade state<!-- [et_pb_line_break_holder] -->                this.zoomPhaseProgress = 0;<!-- [et_pb_line_break_holder] -->            this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (p <= rotatePortion) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 1: Globe rotation<!-- [et_pb_line_break_holder] -->                const t = p \/ rotatePortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeOutCubic(t);<!-- [et_pb_line_break_holder] -->                const angle = this.scrollBaseRotation + eased * 360;<!-- [et_pb_line_break_holder] -->                this.rotation[0] = angle;<!-- [et_pb_line_break_holder] -->                this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                this.mode = 'globe';<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Study Abroad. Find your perfect university now!');<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            } else if (p <= morphEnd) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 2: Morph to flat<!-- [et_pb_line_break_holder] -->                const unfoldT = (p - rotatePortion) \/ morphPortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeInOutCubic(unfoldT);<!-- [et_pb_line_break_holder] -->                this.mode = 'morphing';<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Semester abroad, Bachelor \/ Master Abroad, Double Degree, Gap Year, Summer Sessions, and many more!');<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                if (eased >= 0.98) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    this.renderMorph(eased);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            } else if (p <= flatHoldEnd) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 2.5: Show full flat map before zooming<!-- [et_pb_line_break_holder] -->                this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                this.showLabels = false; \/\/ hide country labels on full map<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('72 Universtities in 25 Countries');<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            } else if (p < foldBackStart) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 3: Continent tours<!-- [et_pb_line_break_holder] -->                const tourProgress = (p - flatHoldEnd) \/ (foldBackStart - flatHoldEnd);<!-- [et_pb_line_break_holder] -->                \/\/ Ensure we're in flat mode and projection is reset before starting tours<!-- [et_pb_line_break_holder] -->                if (this.mode !== 'flat' && tourProgress < 0.01) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                    this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                this.zoomPhaseProgress = Math.max(0, Math.min(1, tourProgress));<!-- [et_pb_line_break_holder] -->                const fastFadePortion = 0.15; \/\/ fade out early in the tour phase<!-- [et_pb_line_break_holder] -->                const fadeT = Math.max(0, Math.min(1, tourProgress \/ fastFadePortion));<!-- [et_pb_line_break_holder] -->                this.graticuleFade = Math.max(0, 1 - fadeT);<!-- [et_pb_line_break_holder] -->                this.showLabels = true;<!-- [et_pb_line_break_holder] -->                this.handleContinentTour(tourProgress);<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 4: Fold back to globe<!-- [et_pb_line_break_holder] -->                const foldT = (p - foldBackStart) \/ foldBackPortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeInOutCubic(foldT);<!-- [et_pb_line_break_holder] -->                this.mode = 'morphing';<!-- [et_pb_line_break_holder] -->                \/\/ Fade graticules back in as we fold up<!-- [et_pb_line_break_holder] -->                this.graticuleFade = Math.min(1, eased);<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->                \/\/ Reverse morph: use (1 - eased) to move from flat -> globe<!-- [et_pb_line_break_holder] -->                this.renderMorph(1 - eased);<!-- [et_pb_line_break_holder] -->                if (foldT >= 0.98) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'globe';<!-- [et_pb_line_break_holder] -->                    this.rotation = [...this.initialRotation];<!-- [et_pb_line_break_holder] -->                    this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                    this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->                    this.showLabels = false;<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleContinentTour(progress) {<!-- [et_pb_line_break_holder] -->            \/\/ Each continent gets: transition in (15%), hold (25%), transition out (15%)<!-- [et_pb_line_break_holder] -->            \/\/ Total per continent: 55%, with transitions between them<!-- [et_pb_line_break_holder] -->            const transitionIn = 0.15;<!-- [et_pb_line_break_holder] -->            const holdDuration = 0.25;<!-- [et_pb_line_break_holder] -->            const transitionOut = 0.15;<!-- [et_pb_line_break_holder] -->            const continentDuration = transitionIn + holdDuration + transitionOut;<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            const numContinents = this.continentTours.length;<!-- [et_pb_line_break_holder] -->            const returnToMapDuration = 0.20;<!-- [et_pb_line_break_holder] -->            const totalDuration = numContinents * continentDuration + returnToMapDuration;<!-- [et_pb_line_break_holder] -->            const normalizedProgress = Math.min(1, Math.max(0, progress)) * totalDuration;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Ensure we start from full map when entering tour phase<!-- [et_pb_line_break_holder] -->            if (normalizedProgress < 0.001) {<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            let accumulated = 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            for (let i = 0; i < numContinents; i++) {<!-- [et_pb_line_break_holder] -->                const continentStart = accumulated;<!-- [et_pb_line_break_holder] -->                const transitionInEnd = continentStart + transitionIn;<!-- [et_pb_line_break_holder] -->                const holdEnd = transitionInEnd + holdDuration;<!-- [et_pb_line_break_holder] -->                const transitionOutEnd = holdEnd + transitionOut;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (normalizedProgress >= continentStart && normalizedProgress < transitionInEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Transitioning into this continent<!-- [et_pb_line_break_holder] -->                    const t = (normalizedProgress - continentStart) \/ transitionIn;<!-- [et_pb_line_break_holder] -->                    const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                    \/\/ If this is the first continent and we're just starting, ensure we begin from full map<!-- [et_pb_line_break_holder] -->                    if (i === 0 && normalizedProgress < continentStart + 0.01) {<!-- [et_pb_line_break_holder] -->                        this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    this.animateToContinent(i, easedT);<!-- [et_pb_line_break_holder] -->                    const c = this.continentTours[i];<!-- [et_pb_line_break_holder] -->                    const count = this.getContinentUniversityCount(i);<!-- [et_pb_line_break_holder] -->                    this.updatePhaseText(`${count} Universities in ${c.name}`);<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                } else if (normalizedProgress >= transitionInEnd && normalizedProgress < holdEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Holding at this continent<!-- [et_pb_line_break_holder] -->                    this.animateToContinent(i, 1);<!-- [et_pb_line_break_holder] -->                    const c = this.continentTours[i];<!-- [et_pb_line_break_holder] -->                    const count = this.getContinentUniversityCount(i);<!-- [et_pb_line_break_holder] -->                    this.updatePhaseText(`${count} Universities in ${c.name}`);<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                } else if (normalizedProgress >= holdEnd && normalizedProgress < transitionOutEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Transitioning out of this continent<!-- [et_pb_line_break_holder] -->                    const t = (normalizedProgress - holdEnd) \/ transitionOut;<!-- [et_pb_line_break_holder] -->                    const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                    const nextIndex = i < numContinents - 1 ? i + 1 : -1;<!-- [et_pb_line_break_holder] -->                    if (nextIndex >= 0) {<!-- [et_pb_line_break_holder] -->                        \/\/ Transition to next continent<!-- [et_pb_line_break_holder] -->                        this.transitionBetweenContinents(i, nextIndex, easedT);<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        \/\/ Transition back to full map<!-- [et_pb_line_break_holder] -->                        this.transitionToFullMap(easedT);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                accumulated = transitionOutEnd;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Final: return to full map<!-- [et_pb_line_break_holder] -->            const returnStart = accumulated;<!-- [et_pb_line_break_holder] -->            if (normalizedProgress >= returnStart) {<!-- [et_pb_line_break_holder] -->                const t = Math.min(1, (normalizedProgress - returnStart) \/ returnToMapDuration);<!-- [et_pb_line_break_holder] -->                const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                this.transitionToFullMap(easedT);<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Apply now to start your study abroad journey!\\n\\nworldofstudents.org');<!-- [et_pb_line_break_holder] -->            } else if (normalizedProgress < 0.001) {<!-- [et_pb_line_break_holder] -->                \/\/ At the very start, show full map<!-- [et_pb_line_break_holder] -->                this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        animateToContinent(index, t) {<!-- [et_pb_line_break_holder] -->            const continent = this.continentTours[index];<!-- [et_pb_line_break_holder] -->            const targetScale = this.baseFlatScale * continent.zoom;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const targetCenter = [continent.lng, continent.lat];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Interpolate from current state<!-- [et_pb_line_break_holder] -->            const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->            const currentCenter = this.flatProjection.center();<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            const scale = currentScale + (targetScale - currentScale) * t;<!-- [et_pb_line_break_holder] -->            const centerLng = currentCenter[0] + (targetCenter[0] - currentCenter[0]) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = currentCenter[1] + (targetCenter[1] - currentCenter[1]) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.currentZoom = scale \/ this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = index;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        transitionBetweenContinents(fromIndex, toIndex, t) {<!-- [et_pb_line_break_holder] -->            const from = this.continentTours[fromIndex];<!-- [et_pb_line_break_holder] -->            const to = this.continentTours[toIndex];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const fromScale = this.baseFlatScale * from.zoom;<!-- [et_pb_line_break_holder] -->            const toScale = this.baseFlatScale * to.zoom;<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            \/\/ Interpolate scale<!-- [et_pb_line_break_holder] -->            const scale = fromScale + (toScale - fromScale) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const centerLng = from.lng + (to.lng - from.lng) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = from.lat + (to.lat - from.lat) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.currentZoom = scale \/ this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = toIndex;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        transitionToFullMap(t) {<!-- [et_pb_line_break_holder] -->            const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->            const targetScale = this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            const scale = currentScale + (targetScale - currentScale) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentCenter = this.flatProjection.center();<!-- [et_pb_line_break_holder] -->            const targetCenter = [0, 0];<!-- [et_pb_line_break_holder] -->            const centerLng = currentCenter[0] + (targetCenter[0] - currentCenter[0]) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = currentCenter[1] + (targetCenter[1] - currentCenter[1]) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        resetFlatProjection() {<!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(this.baseFlatScale)<!-- [et_pb_line_break_holder] -->                .center([0, 0])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] -->            this.currentZoom = 1;<!-- [et_pb_line_break_holder] -->            this.showLabels = false;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        startAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = true;<!-- [et_pb_line_break_holder] -->            this.autoPlayStart = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const step = (ts) => {<!-- [et_pb_line_break_holder] -->                if (!this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->                if (this.autoPlayStart === null) this.autoPlayStart = ts;<!-- [et_pb_line_break_holder] -->                const elapsed = ts - this.autoPlayStart;<!-- [et_pb_line_break_holder] -->                const t = Math.min(1, elapsed \/ this.autoPlayDuration);<!-- [et_pb_line_break_holder] -->                this.applyScrollProgress(t);<!-- [et_pb_line_break_holder] -->                if (t >= 1) {<!-- [et_pb_line_break_holder] -->                    this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->                    this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                this.autoPlayHandle = requestAnimationFrame(step);<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->            this.autoPlayHandle = requestAnimationFrame(step);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        stopAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (!this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->            if (this.autoPlayHandle) {<!-- [et_pb_line_break_holder] -->                cancelAnimationFrame(this.autoPlayHandle);<!-- [et_pb_line_break_holder] -->                this.autoPlayHandle = null;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        toggleAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (this.isAutoPlaying) this.stopAutoPlay();<!-- [et_pb_line_break_holder] -->            else this.startAutoPlay();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        updateAutoPlayButton() {<!-- [et_pb_line_break_holder] -->            if (!this.autoplayBtn) return;<!-- [et_pb_line_break_holder] -->            this.autoplayBtn.textContent = this.isAutoPlaying ? 'Stop' : 'Autoplay';<!-- [et_pb_line_break_holder] -->            this.autoplayBtn.style.opacity = this.isAutoPlaying ? '0.95' : '1';<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        updatePhaseText(text) {<!-- [et_pb_line_break_holder] -->            if (!this.phaseText) return;<!-- [et_pb_line_break_holder] -->            if (this._currentPhaseText === text) return;<!-- [et_pb_line_break_holder] -->            this._currentPhaseText = text;<!-- [et_pb_line_break_holder] -->            this.phaseText.textContent = text || '';<!-- [et_pb_line_break_holder] -->            if (text) {<!-- [et_pb_line_break_holder] -->                this.phaseText.classList.add('visible');<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                this.phaseText.classList.remove('visible');<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupScrollTrigger(targetEl, options = {}) {<!-- [et_pb_line_break_holder] -->            const el = targetEl || this.canvas;<!-- [et_pb_line_break_holder] -->            this.scrollBaseRotation = this.rotation[0];<!-- [et_pb_line_break_holder] -->            const startOffsetOpt = options.startOffset;<!-- [et_pb_line_break_holder] -->            const startAtSticky = options.startAtSticky !== false; \/\/ default true<!-- [et_pb_line_break_holder] -->            const endOffset = options.endOffset ?? 0.8; \/\/ finish slightly before sticky ends<!-- [et_pb_line_break_holder] -->            const clamp01 = (v) => Math.max(0, Math.min(1, v));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const onScroll = () => {<!-- [et_pb_line_break_holder] -->                const viewport = window.innerHeight || 1;<!-- [et_pb_line_break_holder] -->                const rect = el.getBoundingClientRect();<!-- [et_pb_line_break_holder] -->                const wrapperHeight = el.offsetHeight || 1;<!-- [et_pb_line_break_holder] -->                const wrapperTopAbs = rect.top + (window.scrollY || window.pageYOffset || 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ How far we've scrolled since the wrapper reached the top of the viewport<!-- [et_pb_line_break_holder] -->                const scrolledInside = (window.scrollY || window.pageYOffset || 0) - wrapperTopAbs;<!-- [et_pb_line_break_holder] -->                const stickyRange = Math.max(1, wrapperHeight - viewport); \/\/ duration of stickiness<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ raw progress: 0 when top first sticks, 1 when sticky duration ends<!-- [et_pb_line_break_holder] -->                const raw = clamp01(scrolledInside \/ stickyRange);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ startOffset: either provided or aligned to stick start<!-- [et_pb_line_break_holder] -->                const startOffset = startOffsetOpt != null ? startOffsetOpt : (startAtSticky ? 0 : 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Map raw progress into [startOffset, endOffset] range<!-- [et_pb_line_break_holder] -->                const normalized = clamp01((raw - startOffset) \/ Math.max(0.0001, endOffset - startOffset));<!-- [et_pb_line_break_holder] -->                this.applyScrollProgress(normalized);<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            ['scroll', 'resize'].forEach(evt =><!-- [et_pb_line_break_holder] -->                window.addEventListener(evt, onScroll, { passive: true })<!-- [et_pb_line_break_holder] -->            );<!-- [et_pb_line_break_holder] -->            onScroll();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ ---- Morph helpers ----<!-- [et_pb_line_break_holder] -->        easeInOutCubic(t) {<!-- [et_pb_line_break_holder] -->            return t < 0.5<!-- [et_pb_line_break_holder] -->                ? 4 * t * t * t<!-- [et_pb_line_break_holder] -->                : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ Faster start, slower end<!-- [et_pb_line_break_holder] -->        easeOutCubic(t) {<!-- [et_pb_line_break_holder] -->            const clamped = Math.min(1, Math.max(0, t));<!-- [et_pb_line_break_holder] -->            return 1 - Math.pow(1 - clamped, 3);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        createForwardInterpolatedProjection(t) {<!-- [et_pb_line_break_holder] -->            \/\/ Use ease-out so the unfold decelerates into the final flat view<!-- [et_pb_line_break_holder] -->            const tt = this.easeOutCubic(t);<!-- [et_pb_line_break_holder] -->            const startProj = this.orthoProjection;<!-- [et_pb_line_break_holder] -->            const endProj = this.flatProjection;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            return (coords) => {<!-- [et_pb_line_break_holder] -->                const p0 = startProj(coords);<!-- [et_pb_line_break_holder] -->                const p1 = endProj(coords);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (!p0 && !p1) return null;<!-- [et_pb_line_break_holder] -->                const x0 = p0 ? p0[0] : p1[0];<!-- [et_pb_line_break_holder] -->                const y0 = p0 ? p0[1] : p1[1];<!-- [et_pb_line_break_holder] -->                const x1 = p1 ? p1[0] : p0[0];<!-- [et_pb_line_break_holder] -->                const y1 = p1 ? p1[1] : p0[1];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                return [<!-- [et_pb_line_break_holder] -->                    x0 + (x1 - x0) * tt,<!-- [et_pb_line_break_holder] -->                    y0 + (y1 - y0) * tt<!-- [et_pb_line_break_holder] -->                ];<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ ---- Starfield helpers (globe-only) ----<!-- [et_pb_line_break_holder] -->        createStarParticles(count) {<!-- [et_pb_line_break_holder] -->            const stars = [];<!-- [et_pb_line_break_holder] -->            for (let i = 0; i < count; i++) {<!-- [et_pb_line_break_holder] -->                stars.push({<!-- [et_pb_line_break_holder] -->                    lng: (Math.random() * 360) - 180,<!-- [et_pb_line_break_holder] -->                    lat: (Math.random() * 160) - 80, \/\/ avoid exact poles<!-- [et_pb_line_break_holder] -->                    depth: 1 + Math.random() * 0.4, \/\/ farther \u2192 smaller\/fainter<!-- [et_pb_line_break_holder] -->                    size: 0.8 + Math.random() * 0.6,<!-- [et_pb_line_break_holder] -->                    alpha: 0.35 + Math.random() * 0.45,<!-- [et_pb_line_break_holder] -->                    drift: (Math.random() * 4 + 1) * (Math.random() < 0.5 ? -1 : 1) \/\/ deg\/sec<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return stars;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        advanceStars(deltaSec) {<!-- [et_pb_line_break_holder] -->            const clampLat = (lat) => Math.max(-88, Math.min(88, lat));<!-- [et_pb_line_break_holder] -->            this.starParticles.forEach((s) => {<!-- [et_pb_line_break_holder] -->                s.lng = this.normalizeLng(s.lng + s.drift * deltaSec * 0.25);<!-- [et_pb_line_break_holder] -->                \/\/ tiny vertical wander to keep motion subtle<!-- [et_pb_line_break_holder] -->                s.lat = clampLat(s.lat + Math.sin((s.lng + s.lat) * 0.05) * 0.03);<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderStars(projection) {<!-- [et_pb_line_break_holder] -->            const cx = this.containerWidth \/ 2;<!-- [et_pb_line_break_holder] -->            const cy = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.context.save();<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->            this.starParticles.forEach((s) => {<!-- [et_pb_line_break_holder] -->                const base = projection([s.lng, s.lat]);<!-- [et_pb_line_break_holder] -->                if (!base) return;<!-- [et_pb_line_break_holder] -->                const dx = base[0] - cx;<!-- [et_pb_line_break_holder] -->                const dy = base[1] - cy;<!-- [et_pb_line_break_holder] -->                const depth = s.depth;<!-- [et_pb_line_break_holder] -->                const px = cx + dx * depth;<!-- [et_pb_line_break_holder] -->                const py = cy + dy * depth;<!-- [et_pb_line_break_holder] -->                const r = (s.size * sizeFactor) \/ (depth * 1.4);<!-- [et_pb_line_break_holder] -->                const alpha = Math.min(1, s.alpha \/ depth);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = alpha;<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.context.arc(px, py, r, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                this.context.fill();<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->            this.context.restore();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        buildSphereBoundaryCoords() {<!-- [et_pb_line_break_holder] -->            const coords = [];<!-- [et_pb_line_break_holder] -->            const step = 5;<!-- [et_pb_line_break_holder] -->            for (let lon = -180; lon <= 180; lon += step) {<!-- [et_pb_line_break_holder] -->                coords.push([lon, -89]);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            for (let lon = 180; lon >= -180; lon -= step) {<!-- [et_pb_line_break_holder] -->                coords.push([lon, 89]);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return coords;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ \u2705 NEW: break path when projected x-jump is large (no wrap lines)<!-- [et_pb_line_break_holder] -->        drawLineString(ctx, coords, proj) {<!-- [et_pb_line_break_holder] -->            let prevP = null;<!-- [et_pb_line_break_holder] -->            let prevC = null;<!-- [et_pb_line_break_holder] -->            const thresholdX = this.containerWidth \/ 3; \/\/ stricter to catch dateline jumps<!-- [et_pb_line_break_holder] -->            const thresholdY = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            coords.forEach((c) => {<!-- [et_pb_line_break_holder] -->                const p = proj(c);<!-- [et_pb_line_break_holder] -->                if (!p) return;<!-- [et_pb_line_break_holder] -->                const shouldBreak =<!-- [et_pb_line_break_holder] -->                    !!prevP &&<!-- [et_pb_line_break_holder] -->                    (<!-- [et_pb_line_break_holder] -->                        Math.abs(p[0] - prevP[0]) > thresholdX ||<!-- [et_pb_line_break_holder] -->                        Math.abs(p[1] - prevP[1]) > thresholdY ||<!-- [et_pb_line_break_holder] -->                        (prevC && Math.abs(c[0] - prevC[0]) > 170) \/\/ large lon jump \u2192 new subpath<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (!prevP || shouldBreak) {<!-- [et_pb_line_break_holder] -->                    ctx.moveTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    ctx.lineTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                prevP = p;<!-- [et_pb_line_break_holder] -->                prevC = c;<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        drawPolygon(ctx, coords, proj) {<!-- [et_pb_line_break_holder] -->            const thresholdX = this.containerWidth \/ 3;<!-- [et_pb_line_break_holder] -->            coords.forEach((ring) => {<!-- [et_pb_line_break_holder] -->                let prevP = null;<!-- [et_pb_line_break_holder] -->                let prevC = null;<!-- [et_pb_line_break_holder] -->                let firstP = null;<!-- [et_pb_line_break_holder] -->                let firstC = null;<!-- [et_pb_line_break_holder] -->                ring.forEach((c) => {<!-- [et_pb_line_break_holder] -->                    const p = proj(c);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    const shouldBreak =<!-- [et_pb_line_break_holder] -->                        !!prevP &&<!-- [et_pb_line_break_holder] -->                        (<!-- [et_pb_line_break_holder] -->                            Math.abs(p[0] - prevP[0]) > thresholdX ||<!-- [et_pb_line_break_holder] -->                            (prevC && Math.abs(c[0] - prevC[0]) > 170)<!-- [et_pb_line_break_holder] -->                        );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    if (!prevP || shouldBreak) {<!-- [et_pb_line_break_holder] -->                        ctx.moveTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                        firstP = firstP || p;<!-- [et_pb_line_break_holder] -->                        firstC = firstC || c;<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        ctx.lineTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    prevP = p;<!-- [et_pb_line_break_holder] -->                    prevC = c;<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                \/\/ Close ring only when it would not span the dateline<!-- [et_pb_line_break_holder] -->                if (firstP && prevP) {<!-- [et_pb_line_break_holder] -->                    const dxClose = Math.abs(firstP[0] - prevP[0]);<!-- [et_pb_line_break_holder] -->                    const lonJump = firstC && prevC ? Math.abs(firstC[0] - prevC[0]) : 0;<!-- [et_pb_line_break_holder] -->                    if (dxClose <= thresholdX &#038;&#038; lonJump <= 170) {<!-- [et_pb_line_break_holder] -->                        ctx.lineTo(firstP[0], firstP[1]);<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        ctx.moveTo(firstP[0], firstP[1]);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderMorph(t) {<!-- [et_pb_line_break_holder] -->            const proj = this.createForwardInterpolatedProjection(t);<!-- [et_pb_line_break_holder] -->            const ctx = this.context;<!-- [et_pb_line_break_holder] -->            const morphScale = this.flatProjection.scale() \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] -->            const dotRadius = 0.5 * morphScale * sizeFactor;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            ctx.clearRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            ctx.save();<!-- [et_pb_line_break_holder] -->            ctx.fillStyle = this.bgColor || '#ffffff';<!-- [et_pb_line_break_holder] -->            ctx.fillRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            ctx.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Ocean plate<!-- [et_pb_line_break_holder] -->            ctx.beginPath();<!-- [et_pb_line_break_holder] -->            this.drawLineString(ctx, this.sphereBoundary, proj);<!-- [et_pb_line_break_holder] -->            ctx.closePath();<!-- [et_pb_line_break_holder] -->            ctx.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            ctx.fill();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                \/\/ Graticule<!-- [et_pb_line_break_holder] -->                if (this.graticuleGeo && this.graticuleGeo.type === 'MultiLineString') {<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    this.graticuleGeo.coordinates.forEach(line => {<!-- [et_pb_line_break_holder] -->                        this.drawLineString(ctx, line, proj);<!-- [et_pb_line_break_holder] -->                    });<!-- [et_pb_line_break_holder] -->                    ctx.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = 0.4 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                    ctx.lineWidth = 0.8 * sizeFactor;<!-- [et_pb_line_break_holder] -->                    ctx.stroke();<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = 1;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Fills<!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.35;<!-- [et_pb_line_break_holder] -->                ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->                    if (!geom) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                        this.drawPolygon(ctx, geom.coordinates, proj);<!-- [et_pb_line_break_holder] -->                    } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                        geom.coordinates.forEach(poly => this.drawPolygon(ctx, poly, proj));<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                ctx.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Borders (draw after fills so every country gets stroked)<!-- [et_pb_line_break_holder] -->                ctx.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->                    if (!geom) return;<!-- [et_pb_line_break_holder] -->                    if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                        this.drawPolygon(ctx, geom.coordinates, proj);<!-- [et_pb_line_break_holder] -->                    } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                        geom.coordinates.forEach(poly => this.drawPolygon(ctx, poly, proj));<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                ctx.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                ctx.lineWidth = 0.7 * sizeFactor;<!-- [et_pb_line_break_holder] -->                ctx.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Dots<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const p = proj([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    ctx.arc(p[0], p[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                    ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ University markers (consistent size through morph)<!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const p = proj([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    ctx.arc(p[0], p[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                    ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        async loadWorldData() {<!-- [et_pb_line_break_holder] -->            try {<!-- [et_pb_line_break_holder] -->                this.isLoading = true;<!-- [et_pb_line_break_holder] -->                \/\/ Lightweight country TopoJSON (much smaller than full GeoJSON)<!-- [et_pb_line_break_holder] -->                let response = await fetch(<!-- [et_pb_line_break_holder] -->                    'https:\/\/cdn.jsdelivr.net\/npm\/world-atlas@2\/countries-110m.json'<!-- [et_pb_line_break_holder] -->                );<!-- [et_pb_line_break_holder] -->                if (!response.ok) {<!-- [et_pb_line_break_holder] -->                    \/\/ fallback to lightweight land polygons if countries fail<!-- [et_pb_line_break_holder] -->                    response = await fetch(<!-- [et_pb_line_break_holder] -->                        'https:\/\/raw.githubusercontent.com\/martynafford\/natural-earth-geojson\/master\/110m\/physical\/ne_110m_land.json'<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] -->                    if (!response.ok) throw new Error('Failed to load country\/land data');<!-- [et_pb_line_break_holder] -->                    this.landFeatures = await response.json();<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    const topo = await response.json();<!-- [et_pb_line_break_holder] -->                    this.landFeatures = topojson.feature(topo, topo.objects.countries);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                const [[minLng, minLat], [maxLng, maxLat]] = d3.geoBounds(feature);<!-- [et_pb_line_break_holder] -->                const lonSpan = maxLng - minLng;<!-- [et_pb_line_break_holder] -->                \/\/ Reduced density overall; still a bit denser for very wide countries<!-- [et_pb_line_break_holder] -->                const spacing = lonSpan > 120 ? 16 : 22;<!-- [et_pb_line_break_holder] -->                const dots = this.generateDotsInPolygon(feature, spacing);<!-- [et_pb_line_break_holder] -->                dots.forEach(([lng, lat]) => {<!-- [et_pb_line_break_holder] -->                    this.allDots.push({ lng, lat, visible: true });<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                await this.loadUniversitiesData();<!-- [et_pb_line_break_holder] -->                this.computeHighlightedFeatures();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->                this.isLoading = false;<!-- [et_pb_line_break_holder] -->                this.loadingOverlay.style.display = 'none';<!-- [et_pb_line_break_holder] -->                if (this.autoRotate) {<!-- [et_pb_line_break_holder] -->                    this.startRotation();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            } catch (err) {<!-- [et_pb_line_break_holder] -->                this.showError('Failed to load land map data');<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        async loadUniversitiesData() {<!-- [et_pb_line_break_holder] -->            try {<!-- [et_pb_line_break_holder] -->                const res = await fetch('https:\/\/worldofstudents.org\/universities.json', {<!-- [et_pb_line_break_holder] -->                    cache: 'no-cache'<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                if (!res.ok) throw new Error('Failed to fetch universities.json');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const data = await res.json();<!-- [et_pb_line_break_holder] -->                const items = data?.mainEntity?.itemListElement || [];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const parsed = items<!-- [et_pb_line_break_holder] -->                    .map(entry => entry?.item)<!-- [et_pb_line_break_holder] -->                    .map(item => {<!-- [et_pb_line_break_holder] -->                        const lat = parseFloat(item?.geo?.latitude);<!-- [et_pb_line_break_holder] -->                        const lng = parseFloat(item?.geo?.longitude);<!-- [et_pb_line_break_holder] -->                        if (!item?.name || !Number.isFinite(lat) || !Number.isFinite(lng)) {<!-- [et_pb_line_break_holder] -->                            return null;<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                        return {<!-- [et_pb_line_break_holder] -->                            name: item.name,<!-- [et_pb_line_break_holder] -->                            lat,<!-- [et_pb_line_break_holder] -->                            lng,<!-- [et_pb_line_break_holder] -->                            country: item?.address?.countryName || item?.address?.addressCountry || ''<!-- [et_pb_line_break_holder] -->                        };<!-- [et_pb_line_break_holder] -->                    })<!-- [et_pb_line_break_holder] -->                    .filter(Boolean);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.universities = parsed.length<!-- [et_pb_line_break_holder] -->                    ? parsed<!-- [et_pb_line_break_holder] -->                    : universityData.map(item => ({<!-- [et_pb_line_break_holder] -->                        name: item.name,<!-- [et_pb_line_break_holder] -->                        lat: parseFloat(item.lat),<!-- [et_pb_line_break_holder] -->                        lng: parseFloat(item.lng),<!-- [et_pb_line_break_holder] -->                        country: item.country<!-- [et_pb_line_break_holder] -->                    }));<!-- [et_pb_line_break_holder] -->            } catch (err) {<!-- [et_pb_line_break_holder] -->                console.error('Failed to load universities.json, using fallback data', err);<!-- [et_pb_line_break_holder] -->                this.universities = universityData.map(item => ({<!-- [et_pb_line_break_holder] -->                    name: item.name,<!-- [et_pb_line_break_holder] -->                    lat: parseFloat(item.lat),<!-- [et_pb_line_break_holder] -->                    lng: parseFloat(item.lng),<!-- [et_pb_line_break_holder] -->                    country: item.country<!-- [et_pb_line_break_holder] -->                }));<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            \/\/ Invalidate cached continent counts when the dataset updates<!-- [et_pb_line_break_holder] -->            this._continentCounts.clear();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        computeHighlightedFeatures() {<!-- [et_pb_line_break_holder] -->            this.highlightedFeatureIndices = new Set();<!-- [et_pb_line_break_holder] -->            if (!this.landFeatures?.features?.length || !this.universities?.length) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                const bounds = d3.geoBounds(feature);<!-- [et_pb_line_break_holder] -->                const [[minLng, minLat], [maxLng, maxLat]] = bounds;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const hasUni = this.universities.some(uni => {<!-- [et_pb_line_break_holder] -->                    \/\/ quick bbox reject<!-- [et_pb_line_break_holder] -->                    if (uni.lng < minLng || uni.lng > maxLng || uni.lat < minLat || uni.lat > maxLat) {<!-- [et_pb_line_break_holder] -->                        return false;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    return this.pointInFeature([uni.lng, uni.lat], feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (hasUni) this.highlightedFeatureIndices.add(idx);<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        loadLogo() {<!-- [et_pb_line_break_holder] -->            this.logoImage = new Image();<!-- [et_pb_line_break_holder] -->            this.logoImage.crossOrigin = 'anonymous';<!-- [et_pb_line_break_holder] -->            this.logoImage.src = 'https:\/\/worldofstudents.org\/wp-content\/uploads\/2025\/09\/WOS_Logo_white.png';<!-- [et_pb_line_break_holder] -->            this.logoImage.onload = () => {<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        showError(message) {<!-- [et_pb_line_break_holder] -->            this.error = message;<!-- [et_pb_line_break_holder] -->            this.isLoading = false;<!-- [et_pb_line_break_holder] -->            this.loadingOverlay.innerHTML = `<!-- [et_pb_line_break_holder] -->                <\/p>\n<div class=\"error-state\"><!-- [et_pb_line_break_holder] -->                    <\/p>\n<div class=\"error-title\">Error loading Earth visualization<\/div>\n<p><!-- [et_pb_line_break_holder] -->                    <\/p>\n<div class=\"error-message\">${message}<\/div>\n<p><!-- [et_pb_line_break_holder] -->                <\/div>\n<p><!-- [et_pb_line_break_holder] -->            `;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        getContinentUniversityCount(index) {<!-- [et_pb_line_break_holder] -->            if (this._continentCounts.has(index)) {<!-- [et_pb_line_break_holder] -->                return this._continentCounts.get(index);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const tour = this.continentTours[index];<!-- [et_pb_line_break_holder] -->            if (!tour || !Array.isArray(this.universities)) return 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const latRange = tour.latRange || [-90, 90];<!-- [et_pb_line_break_holder] -->            const lngRanges = tour.lngRanges || [[-180, 180]];<!-- [et_pb_line_break_holder] -->            const countrySet = new Set((tour.countries || []).map(c => c.toLowerCase()));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const inLatRange = (lat) => lat >= latRange[0] && lat <= latRange[1];<!-- [et_pb_line_break_holder] -->            const inLngRange = (lng) => {<!-- [et_pb_line_break_holder] -->                const normLng = this.normalizeLng(lng);<!-- [et_pb_line_break_holder] -->                return lngRanges.some(([min, max]) => this.lngWithinRange(normLng, min, max));<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] -->            const inCountrySet = (country) => {<!-- [et_pb_line_break_holder] -->                if (!country) return false;<!-- [et_pb_line_break_holder] -->                const c = country.toLowerCase().trim();<!-- [et_pb_line_break_holder] -->                return countrySet.has(c);<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const count = this.universities.reduce((acc, uni) => {<!-- [et_pb_line_break_holder] -->                if (!Number.isFinite(uni.lat) || !Number.isFinite(uni.lng)) return acc;<!-- [et_pb_line_break_holder] -->                const countryOk = countrySet.size ? inCountrySet(uni.country) : true;<!-- [et_pb_line_break_holder] -->                const geoOk = inLatRange(uni.lat) && inLngRange(uni.lng);<!-- [et_pb_line_break_holder] -->                if (countryOk && geoOk) return acc + 1;<!-- [et_pb_line_break_holder] -->                return acc;<!-- [et_pb_line_break_holder] -->            }, 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this._continentCounts.set(index, count);<!-- [et_pb_line_break_holder] -->            return count;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        normalizeLng(lng) {<!-- [et_pb_line_break_holder] -->            const n = ((lng + 180) % 360 + 360) % 360 - 180;<!-- [et_pb_line_break_holder] -->            return n === -180 ? 180 : n;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        lngWithinRange(lng, min, max) {<!-- [et_pb_line_break_holder] -->            const nMin = this.normalizeLng(min);<!-- [et_pb_line_break_holder] -->            const nMax = this.normalizeLng(max);<!-- [et_pb_line_break_holder] -->            if (nMin <= nMax) {<!-- [et_pb_line_break_holder] -->                return lng >= nMin && lng <= nMax;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return lng >= nMin || lng <= nMax;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        startRotation() {<!-- [et_pb_line_break_holder] -->            this._lastElapsed = 0;<!-- [et_pb_line_break_holder] -->            this._lastFrame = 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const rotate = (elapsed) => {<!-- [et_pb_line_break_holder] -->                if (!this.autoRotate || this.mode !== 'globe') return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const deltaMs = elapsed - this._lastElapsed;<!-- [et_pb_line_break_holder] -->                this._lastElapsed = elapsed;<!-- [et_pb_line_break_holder] -->                const deltaSec = Math.max(0, deltaMs) \/ 1000;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ gently drift stars while globe rotates<!-- [et_pb_line_break_holder] -->                this.advanceStars(deltaSec);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const degPerSec = this.rotationSpeed * 60;<!-- [et_pb_line_break_holder] -->                this.rotation[0] += degPerSec * deltaSec;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const FRAME_MS = this.isInteracting ? 16 : 33;<!-- [et_pb_line_break_holder] -->                if (elapsed - this._lastFrame >= FRAME_MS) {<!-- [et_pb_line_break_holder] -->                    this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                    this._lastFrame = elapsed;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.rotationTimer) this.rotationTimer.stop();<!-- [et_pb_line_break_holder] -->            this.rotationTimer = d3.timer(rotate);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupInteractions() {<!-- [et_pb_line_break_holder] -->            \/\/ Disable drag\/zoom; keep hover for tooltip<!-- [et_pb_line_break_holder] -->            this.canvas.addEventListener('mousemove', (event) => this.handleMouseMove(event));<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleMouseMove(event) {<!-- [et_pb_line_break_holder] -->            const rect = this.canvas.getBoundingClientRect();<!-- [et_pb_line_break_holder] -->            const x = event.clientX - rect.left;<!-- [et_pb_line_break_holder] -->            const y = event.clientY - rect.top;<!-- [et_pb_line_break_holder] -->            let hoveredUni = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const projection = (this.mode === 'flat' || this.mode === 'morphing') ? this.flatProjection : this.orthoProjection;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                if (projected &&<!-- [et_pb_line_break_holder] -->                    projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                    projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                    const dx = projected[0] - x;<!-- [et_pb_line_break_holder] -->                    const dy = projected[1] - y;<!-- [et_pb_line_break_holder] -->                    const distance = Math.sqrt(dx * dx + dy * dy);<!-- [et_pb_line_break_holder] -->                    if (distance < 15) {<!-- [et_pb_line_break_holder] -->                        hoveredUni = uni;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (hoveredUni && this.tooltip) {<!-- [et_pb_line_break_holder] -->                this.tooltip.style.display = 'block';<!-- [et_pb_line_break_holder] -->                this.tooltip.style.left = `${x + 10}px`;<!-- [et_pb_line_break_holder] -->                this.tooltip.style.top = `${y + 10}px`;<!-- [et_pb_line_break_holder] -->                this.tooltip.innerHTML = `<strong>${hoveredUni.name}<\/strong><!\u2013- [et_pb_br_holder] -\u2013>${hoveredUni.country}`;<!-- [et_pb_line_break_holder] -->            } else if (this.tooltip) {<!-- [et_pb_line_break_holder] -->                this.tooltip.style.display = 'none';<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    document.addEventListener('DOMContentLoaded', () => {<!-- [et_pb_line_break_holder] -->        const earth = new RotatingEarth('earth-canvas', {<!-- [et_pb_line_break_holder] -->            width: 1080,<!-- [et_pb_line_break_holder] -->            height: 540,<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->        earth.autoplayBtn = document.getElementById('autoplay-btn');<!-- [et_pb_line_break_holder] -->        if (earth.autoplayBtn) {<!-- [et_pb_line_break_holder] -->            earth.autoplayBtn.addEventListener('click', () => earth.toggleAutoPlay());<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ Drive the animation by scroll progress; use the wrapper for full scroll length<!-- [et_pb_line_break_holder] -->        \/\/ startOffset delays the start; endOffset ends a bit before the wrapper ends<!-- [et_pb_line_break_holder] -->        earth.autoRotate = false;<!-- [et_pb_line_break_holder] -->        earth.setupScrollTrigger(document.getElementById('earth-sticky-wrapper'), {<!-- [et_pb_line_break_holder] -->            \/\/ startAtSticky uses the point where the wrapper first pins as the start<!-- [et_pb_line_break_holder] -->            startAtSticky: true,<!-- [et_pb_line_break_holder] -->            endOffset: 0.8<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->    });<!-- [et_pb_line_break_holder] --><\/script><!-- [et_pb_line_break_holder] -->[\/et_pb_fullwidth_code][et_pb_fullwidth_code _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; admin_label=&#8221;stars and text reveal&#8221; hover_enabled=&#8221;0&#8243; sticky_enabled=&#8221;0&#8243;]<script src=\"https:\/\/d3js.org\/d3.v7.min.js\"><\/script><!-- [et_pb_line_break_holder] --><script src=\"https:\/\/cdn.jsdelivr.net\/npm\/topojson-client@3\/dist\/topojson-client.min.js\"><\/script><!-- [et_pb_line_break_holder] --><\/p>\n<style><!-- [et_pb_line_break_holder] -->    html, body {<!-- [et_pb_line_break_holder] -->        margin: 0;<!-- [et_pb_line_break_holder] -->        padding: 0;<!-- [et_pb_line_break_holder] -->        height: 100%;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    \/* Wrapper defines how long the sticky canvas stays fixed *\/<!-- [et_pb_line_break_holder] -->    .earth-sticky-wrapper {<!-- [et_pb_line_break_holder] -->        position: relative;<!-- [et_pb_line_break_holder] -->        height: 600vh; \/* Extended for continent tours *\/<!-- [et_pb_line_break_holder] -->        max-width: 1080px;<!-- [et_pb_line_break_holder] -->        margin:0 auto;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .earth-container {<!-- [et_pb_line_break_holder] -->        position: sticky;<!-- [et_pb_line_break_holder] -->        top: 150px;<!-- [et_pb_line_break_holder] -->        width: 100%;<!-- [et_pb_line_break_holder] -->        max-width: 1080px;<!-- [et_pb_line_break_holder] -->        height: 75vh;<!-- [et_pb_line_break_holder] -->        margin: 0 auto;<!-- [et_pb_line_break_holder] -->        background: transparent;<!-- [et_pb_line_break_holder] -->        border-radius: 0;<!-- [et_pb_line_break_holder] -->        overflow: visible;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    #earth-canvas {<!-- [et_pb_line_break_holder] -->        width: 100%;<!-- [et_pb_line_break_holder] -->        height: 100%;<!-- [et_pb_line_break_holder] -->        display: block;<!-- [et_pb_line_break_holder] -->        background: transparent;<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        top: 0;<!-- [et_pb_line_break_holder] -->        left: 0;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .loading-overlay {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        top: 0;<!-- [et_pb_line_break_holder] -->        left: 0;<!-- [et_pb_line_break_holder] -->        right: 0;<!-- [et_pb_line_break_holder] -->        bottom: 0;<!-- [et_pb_line_break_holder] -->        background: rgba(0, 0, 0, 0.8);<!-- [et_pb_line_break_holder] -->        display: flex;<!-- [et_pb_line_break_holder] -->        align-items: center;<!-- [et_pb_line_break_holder] -->        justify-content: center;<!-- [et_pb_line_break_holder] -->        border-radius: 16px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .loading-text {<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 16px;<!-- [et_pb_line_break_holder] -->        font-weight: 500;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-state {<!-- [et_pb_line_break_holder] -->        padding: 32px;<!-- [et_pb_line_break_holder] -->        text-align: center;<!-- [et_pb_line_break_holder] -->        color: #ef4444;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-title {<!-- [et_pb_line_break_holder] -->        font-weight: 600;<!-- [et_pb_line_break_holder] -->        margin-bottom: 8px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .error-message {<!-- [et_pb_line_break_holder] -->        color: #999999;<!-- [et_pb_line_break_holder] -->        font-size: 14px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .spinner {<!-- [et_pb_line_break_holder] -->        width: 24px;<!-- [et_pb_line_break_holder] -->        height: 24px;<!-- [et_pb_line_break_holder] -->        border: 2px solid #333333;<!-- [et_pb_line_break_holder] -->        border-top: 2px solid #ffffff;<!-- [et_pb_line_break_holder] -->        border-radius: 50%;<!-- [et_pb_line_break_holder] -->        animation: spin 1s linear infinite;<!-- [et_pb_line_break_holder] -->        margin-right: 12px;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    @keyframes spin {<!-- [et_pb_line_break_holder] -->        0% { transform: rotate(0deg); }<!-- [et_pb_line_break_holder] -->        100% { transform: rotate(360deg); }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .phase-text {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        left: 50%;<!-- [et_pb_line_break_holder] -->        bottom: 36%;<!-- [et_pb_line_break_holder] -->        transform: translate(-50%, 14px) scale(0.98);<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 18px;<!-- [et_pb_line_break_holder] -->        font-weight: 700;<!-- [et_pb_line_break_holder] -->        letter-spacing: 0.4px;<!-- [et_pb_line_break_holder] -->        text-shadow: 0 2px 10px rgba(0,0,0,0.55);<!-- [et_pb_line_break_holder] -->        opacity: 0;<!-- [et_pb_line_break_holder] -->        filter: blur(8px);<!-- [et_pb_line_break_holder] -->        clip-path: inset(32% 0 32% 0);<!-- [et_pb_line_break_holder] -->        transition: opacity 0.2s ease;<!-- [et_pb_line_break_holder] -->        will-change: transform, opacity, filter, clip-path;<!-- [et_pb_line_break_holder] -->        pointer-events: none;<!-- [et_pb_line_break_holder] -->        max-width: 84%;<!-- [et_pb_line_break_holder] -->        text-align: center;<!-- [et_pb_line_break_holder] -->        line-height: 1.45;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .phase-text.visible {<!-- [et_pb_line_break_holder] -->        animation: phaseReveal 650ms ease forwards;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    @keyframes phaseReveal {<!-- [et_pb_line_break_holder] -->        0% {<!-- [et_pb_line_break_holder] -->            opacity: 0;<!-- [et_pb_line_break_holder] -->            transform: translate(-50%, 14px) scale(0.96);<!-- [et_pb_line_break_holder] -->            filter: blur(8px);<!-- [et_pb_line_break_holder] -->            clip-path: inset(32% 0 32% 0);<!-- [et_pb_line_break_holder] -->            letter-spacing: 0.8px;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] -->        60% {<!-- [et_pb_line_break_holder] -->            opacity: 1;<!-- [et_pb_line_break_holder] -->            transform: translate(-50%, -4px) scale(1.02);<!-- [et_pb_line_break_holder] -->            filter: blur(0);<!-- [et_pb_line_break_holder] -->            clip-path: inset(0 0 0 0);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] -->        100% {<!-- [et_pb_line_break_holder] -->            opacity: 1;<!-- [et_pb_line_break_holder] -->            transform: translate(-50%, 0) scale(1);<!-- [et_pb_line_break_holder] -->            filter: blur(0);<!-- [et_pb_line_break_holder] -->            clip-path: inset(0 0 0 0);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    .autoplay-btn {<!-- [et_pb_line_break_holder] -->        position: absolute;<!-- [et_pb_line_break_holder] -->        right: 32px;<!-- [et_pb_line_break_holder] -->        top: 32px;<!-- [et_pb_line_break_holder] -->        padding: 10px 14px;<!-- [et_pb_line_break_holder] -->        border-radius: 12px;<!-- [et_pb_line_break_holder] -->        border: 1px solid rgba(255,255,255,0.3);<!-- [et_pb_line_break_holder] -->        background: rgba(0,0,0,0.45);<!-- [et_pb_line_break_holder] -->        color: #ffffff;<!-- [et_pb_line_break_holder] -->        font-size: 13px;<!-- [et_pb_line_break_holder] -->        font-weight: 600;<!-- [et_pb_line_break_holder] -->        cursor: pointer;<!-- [et_pb_line_break_holder] -->        backdrop-filter: blur(6px);<!-- [et_pb_line_break_holder] -->        transition: background 0.2s ease, transform 0.2s ease, opacity 0.2s ease;<!-- [et_pb_line_break_holder] -->        z-index: 5;<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .autoplay-btn:hover {<!-- [et_pb_line_break_holder] -->        background: rgba(0,0,0,0.65);<!-- [et_pb_line_break_holder] -->        transform: translateY(-1px);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] -->    .autoplay-btn:active {<!-- [et_pb_line_break_holder] -->        transform: translateY(0);<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><\/style>\n<p><!-- [et_pb_line_break_holder] --><\/p>\n<div class=\"earth-sticky-wrapper\" id=\"earth-sticky-wrapper\"><!-- [et_pb_line_break_holder] -->    <\/p>\n<div class=\"earth-container\"><!-- [et_pb_line_break_holder] -->        <canvas id=\"earth-canvas\"><\/canvas><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"loading-overlay\" class=\"loading-overlay\"><!-- [et_pb_line_break_holder] -->            <\/p>\n<div class=\"spinner\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->            <\/p>\n<div class=\"loading-text\">Loading Earth data&#8230;<\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"tooltip\" style=\"position: absolute; display: none; background: rgba(0,0,0,0.8); color: white; padding: 5px; border-radius: 3px; pointer-events: none; z-index: 1000;\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->        <\/p>\n<div id=\"phase-text\" class=\"phase-text\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->        <button id=\"autoplay-btn\" class=\"autoplay-btn\">Autoplay<\/button><!-- [et_pb_line_break_holder] -->    <\/div>\n<p><!-- [et_pb_line_break_holder] --><\/div>\n<p><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --><script><!-- [et_pb_line_break_holder] -->    \/\/ Fallback dataset used if the remote universities.json cannot be loaded<!-- [et_pb_line_break_holder] -->    const universityData = [<!-- [et_pb_line_break_holder] -->        {\"name\":\"Vancouver Island University\",\"lat\":\"49.1578851\",\"lng\":\"-123.9656421\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"International College of Management, Sydney (ICMS)\",\"lat\":\"-33.8040340\",\"lng\":\"151.2937461\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of North Carolina Wilmington\",\"lat\":\"34.2249827\",\"lng\":\"-77.8690774\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Griffith College Dublin\",\"lat\":\"53.3311882\",\"lng\":\"-6.2802781\",\"country\":\"Ireland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EUSA University Centre, Seville, Spain\",\"lat\":\"37.3754972\",\"lng\":\"-5.9806833\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Cesine Santander\",\"lat\":\"43.4715066\",\"lng\":\"-3.7915862\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Asia Pacific University of Technology & Innovation (APU)\",\"lat\":\"3.0479178\",\"lng\":\"101.6892466\",\"country\":\"Malaysia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Francisco State University\",\"lat\":\"37.7245167\",\"lng\":\"-122.4799957\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"International Business School Budapest\",\"lat\":\"47.5629570\",\"lng\":\"19.0541941\",\"country\":\"Hungary\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EU Business School Barcelona\",\"lat\":\"41.3825802\",\"lng\":\"2.1770730\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Institut Sup\u00e9rieur de Gestion (ISG) Paris\",\"lat\":\"48.8672799\",\"lng\":\"2.2758497\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Woosong University\",\"lat\":\"36.3392944\",\"lng\":\"127.4506140\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"SolBridge International School of Business\",\"lat\":\"36.3383278\",\"lng\":\"127.4324307\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Europea de Madrid\",\"lat\":\"40.3727017\",\"lng\":\"-3.9185823\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"St. Francis College New York\",\"lat\":\"40.6900603\",\"lng\":\"-73.9865997\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universitat Aut\u00f2noma de Barcelona\",\"lat\":\"41.5015527\",\"lng\":\"2.1062607\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of the Fraser Valley\",\"lat\":\"49.0283443\",\"lng\":\"-122.2849135\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad San Ignacio de Loyola\",\"lat\":\"-12.0734419\",\"lng\":\"-76.9484914\",\"country\":\"Peru\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Francisco de Vitoria Madrid\",\"lat\":\"40.4402647\",\"lng\":\"-3.8339056\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"American College of Thessaloniki\",\"lat\":\"40.5684100\",\"lng\":\"22.9912375\",\"country\":\"Greece\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Hankuk University of Foreign Studies\",\"lat\":\"37.5970815\",\"lng\":\"127.0587413\",\"country\":\"Korea\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Vrije Universiteit Amsterdam\",\"lat\":\"52.3340539\",\"lng\":\"4.8651882\",\"country\":\"Netherlands\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Austral\",\"lat\":\"-34.5934652\",\"lng\":\"-58.3833079\",\"country\":\"Argentina\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad de Alicante\",\"lat\":\"38.3850684\",\"lng\":\"-0.5146732\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universit\u00e0 Europea di Roma\",\"lat\":\"41.8791802\",\"lng\":\"12.3946472\",\"country\":\"Italy\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Cat\u00f3lica San Antonio de Murcia\",\"lat\":\"38.0042077\",\"lng\":\"-1.1774782\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Xi'an Jiaotong-Liverpool University\",\"lat\":\"31.2787148\",\"lng\":\"120.7237629\",\"country\":\"China\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universit\u00e0 Cattolica del Sacro Cuore\",\"lat\":\"45.4609998\",\"lng\":\"9.1769914\",\"country\":\"Italy\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"College of the Desert\",\"lat\":\"33.7324666\",\"lng\":\"-116.3868288\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Santa Barbara City College\",\"lat\":\"34.4058643\",\"lng\":\"-119.6974204\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Victoria University of Wellington\",\"lat\":\"-41.2793942\",\"lng\":\"174.7783525\",\"country\":\"New Zealand\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"City University of Seattle\",\"lat\":\"47.6177084\",\"lng\":\"-122.3444624\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of the Sunshine Coast (UniSC)\",\"lat\":\"-26.7159208\",\"lng\":\"153.0563306\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of Brighton\",\"lat\":\"50.8421128\",\"lng\":\"-0.1194002\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"London South Bank University\",\"lat\":\"51.4991489\",\"lng\":\"-0.0970188\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Dorset College Dublin\",\"lat\":\"53.3582148\",\"lng\":\"-6.2577212\",\"country\":\"Ireland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"BI Norwegian Business School\",\"lat\":\"59.9488700\",\"lng\":\"10.7682070\",\"country\":\"Norway\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"EU Business School Geneva\",\"lat\":\"46.2067776\",\"lng\":\"6.1451264\",\"country\":\"Switzerland\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"London Metropolitan University\",\"lat\":\"51.5522032\",\"lng\":\"-0.1111950\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Adolfo Ibanez\",\"lat\":\"-33.0196718\",\"lng\":\"-71.5304035\",\"country\":\"Chile\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Jose State University\",\"lat\":\"37.3351902\",\"lng\":\"-121.8812255\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Europea de Valencia\",\"lat\":\"39.4755093\",\"lng\":\"-0.3652411\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Latinoamericana de Cienca y Tecnologia\",\"lat\":\"9.9325427\",\"lng\":\"-84.0795782\",\"country\":\"Costa Rica\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"San Diego State University\",\"lat\":\"32.7760640\",\"lng\":\"-117.0702300\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Bond University\",\"lat\":\"-28.0724817\",\"lng\":\"153.4157960\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California San Diego\",\"lat\":\"32.8792438\",\"lng\":\"-117.2311247\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Alliant International University\",\"lat\":\"32.8966666\",\"lng\":\"-117.0938567\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"James Cook University Singapore\",\"lat\":\"1.3153627\",\"lng\":\"103.8760268\",\"country\":\"Singapore\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Capilano University\",\"lat\":\"49.3179806\",\"lng\":\"-123.0194770\",\"country\":\"Canada\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"James Cook University Australia\",\"lat\":\"-19.3293891\",\"lng\":\"146.7611734\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Nebrija University\",\"lat\":\"40.4295684\",\"lng\":\"-3.7130672\",\"country\":\"Spain\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California Santa Barbara\",\"lat\":\"34.4145523\",\"lng\":\"-119.8468383\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California Berkeley Extension\",\"lat\":\"37.8708393\",\"lng\":\"-122.2728630\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Westcliff University\",\"lat\":\"33.6853732\",\"lng\":\"-117.8480110\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"The University of Sydney\",\"lat\":\"-33.8854217\",\"lng\":\"151.1890168\",\"country\":\"Australia\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of California, Los Angeles (UCLA) Extension\",\"lat\":\"34.0595933\",\"lng\":\"-118.4463883\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Auckland University of Technology\",\"lat\":\"-36.8525224\",\"lng\":\"174.7667714\",\"country\":\"New Zealand\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Pace University\",\"lat\":\"40.7111050\",\"lng\":\"-74.0046134\",\"country\":\"USA\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Edinburgh Napier University\",\"lat\":\"55.9244726\",\"lng\":\"-3.2888614\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"University of Westminster\",\"lat\":\"51.5209064\",\"lng\":\"-0.1400730\",\"country\":\"United Kingdom\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"IPAG Business School Paris\",\"lat\":\"48.8544327\",\"lng\":\"2.3312200\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Universidad Veritas\",\"lat\":\"9.9254713\",\"lng\":\"-84.0648763\",\"country\":\"Costa Rica\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"IPAG Business School Nice\",\"lat\":\"43.7145200\",\"lng\":\"7.2652823\",\"country\":\"France\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Royal Melbourne Institute of Technology Vietnam\",\"lat\":\"10.7755254\",\"lng\":\"106.7021047\",\"country\":\"Viet Nam\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Curtin University Dubai\",\"lat\":\"25.0742823\",\"lng\":\"55.1885387\",\"country\":\"United Arab Emirates\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"Curtin University Singapore\",\"lat\":\"1.2884206\",\"lng\":\"103.7795669\",\"country\":\"Singapore\"},<!-- [et_pb_line_break_holder] -->        {\"name\":\"LCI Barcelona\",\"lat\":\"41.3994905\",\"lng\":\"2.1902139\",\"country\":\"Spain\"}<!-- [et_pb_line_break_holder] -->    ];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    class RotatingEarth {<!-- [et_pb_line_break_holder] -->        constructor(canvasId, options = {}) {<!-- [et_pb_line_break_holder] -->            this.canvas = document.getElementById(canvasId);<!-- [et_pb_line_break_holder] -->            this.context = this.canvas.getContext('2d');<!-- [et_pb_line_break_holder] -->            this.loadingOverlay = document.getElementById('loading-overlay');<!-- [et_pb_line_break_holder] -->            this.tooltip = document.getElementById('tooltip');<!-- [et_pb_line_break_holder] -->            this.phaseText = document.getElementById('phase-text');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.width = options.width || 800;<!-- [et_pb_line_break_holder] -->            this.height = options.height || 600;<!-- [et_pb_line_break_holder] -->            this.isLoading = true;<!-- [et_pb_line_break_holder] -->            this.error = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.allDots = [];<!-- [et_pb_line_break_holder] -->            this.landFeatures = null;<!-- [et_pb_line_break_holder] -->            this.universities = [];<!-- [et_pb_line_break_holder] -->            this.highlightedFeatureIndices = new Set();<!-- [et_pb_line_break_holder] -->            this.initialRotation = [0, -38];<!-- [et_pb_line_break_holder] -->            this.rotation = [...this.initialRotation];<!-- [et_pb_line_break_holder] -->            this.scrollBaseRotation = this.rotation[0];<!-- [et_pb_line_break_holder] -->            this.autoRotate = true;<!-- [et_pb_line_break_holder] -->            this.rotationSpeed = 0.25;<!-- [et_pb_line_break_holder] -->            this.rotationTimer = null;<!-- [et_pb_line_break_holder] -->            this.logoImage = null;<!-- [et_pb_line_break_holder] -->            this.isInteracting = false;<!-- [et_pb_line_break_holder] -->            this.bgColor = '#005499';<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->            this.autoPlayStart = 0;<!-- [et_pb_line_break_holder] -->            this.autoPlayDuration = 24000; \/\/ ms<!-- [et_pb_line_break_holder] -->            this.autoPlayHandle = null;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'globe';<!-- [et_pb_line_break_holder] -->            this.orthoProjection = null;<!-- [et_pb_line_break_holder] -->            this.flatProjection = null;<!-- [et_pb_line_break_holder] -->            this.path = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.graticuleGeo = d3.geoGraticule().step([15, 15])(); \/\/ MultiLineString<!-- [et_pb_line_break_holder] -->            this.sphereBoundary = this.buildSphereBoundaryCoords();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Continent tour configuration<!-- [et_pb_line_break_holder] -->            this.continentTours = [<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'Europe',<!-- [et_pb_line_break_holder] -->                    lng: 10,<!-- [et_pb_line_break_holder] -->                    lat: 50,<!-- [et_pb_line_break_holder] -->                    zoom: 2.5,<!-- [et_pb_line_break_holder] -->                    latRange: [35, 72],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[-25, 60]],<!-- [et_pb_line_break_holder] -->                    countries: [<!-- [et_pb_line_break_holder] -->                        'austria','belgium','bulgaria','croatia','cyprus','czechia','czech republic',<!-- [et_pb_line_break_holder] -->                        'denmark','estonia','finland','france','germany','greece','hungary','iceland',<!-- [et_pb_line_break_holder] -->                        'ireland','italy','latvia','lithuania','luxembourg','malta','netherlands',<!-- [et_pb_line_break_holder] -->                        'norway','poland','portugal','romania','slovakia','slovenia','spain','sweden',<!-- [et_pb_line_break_holder] -->                        'switzerland','united kingdom','uk','england','scotland','wales','northern ireland'<!-- [et_pb_line_break_holder] -->                    ]<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'North America',<!-- [et_pb_line_break_holder] -->                    lng: -100,<!-- [et_pb_line_break_holder] -->                    lat: 40,<!-- [et_pb_line_break_holder] -->                    zoom: 2.2,<!-- [et_pb_line_break_holder] -->                    latRange: [7, 83],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[-170, -50]],<!-- [et_pb_line_break_holder] -->                    countries: ['canada','united states','united states of america','usa','us']<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'South America',<!-- [et_pb_line_break_holder] -->                    lng: -60,<!-- [et_pb_line_break_holder] -->                    lat: -20,<!-- [et_pb_line_break_holder] -->                    zoom: 2.3,<!-- [et_pb_line_break_holder] -->                    latRange: [-60, 15],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[-90, -30]],<!-- [et_pb_line_break_holder] -->                    countries: ['argentina','bolivia','brazil','chile','colombia','ecuador','guyana','paraguay','peru','suriname','uruguay','venezuela', 'costa rica']<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'Asia',<!-- [et_pb_line_break_holder] -->                    lng: 100,<!-- [et_pb_line_break_holder] -->                    lat: 30,<!-- [et_pb_line_break_holder] -->                    zoom: 2.2,<!-- [et_pb_line_break_holder] -->                    latRange: [-15, 80],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[35, 180], [-180, -100]],<!-- [et_pb_line_break_holder] -->                    countries: [<!-- [et_pb_line_break_holder] -->                        'china','japan','south korea','korea','republic of korea','north korea','taiwan',<!-- [et_pb_line_break_holder] -->                        'hong kong','macau','india','pakistan','bangladesh','sri lanka','nepal','bhutan',<!-- [et_pb_line_break_holder] -->                        'maldives','indonesia','malaysia','philippines','vietnam', 'viet nam', 'thailand','laos','cambodia',<!-- [et_pb_line_break_holder] -->                        'myanmar','brunei','singapore','mongolia','kazakhstan','kyrgyzstan','uzbekistan',<!-- [et_pb_line_break_holder] -->                        'tajikistan','turkmenistan','afghanistan','iran','iraq','saudi arabia','united arab emirates',<!-- [et_pb_line_break_holder] -->                        'uae','qatar','kuwait','bahrain','oman','yemen','jordan','lebanon','israel','palestine',<!-- [et_pb_line_break_holder] -->                        'turkey','georgia','armenia','azerbaijan'<!-- [et_pb_line_break_holder] -->                    ]<!-- [et_pb_line_break_holder] -->                },<!-- [et_pb_line_break_holder] -->                {<!-- [et_pb_line_break_holder] -->                    name: 'Oceania',<!-- [et_pb_line_break_holder] -->                    lng: 150,<!-- [et_pb_line_break_holder] -->                    lat: -25,<!-- [et_pb_line_break_holder] -->                    zoom: 2.5,<!-- [et_pb_line_break_holder] -->                    latRange: [-50, 10],<!-- [et_pb_line_break_holder] -->                    lngRanges: [[110, 190], [-190, -140]],<!-- [et_pb_line_break_holder] -->                    countries: ['australia','new zealand','fiji','papua new guinea','samoa','tonga','vanuatu','solomon islands','new caledonia']<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            ];<!-- [et_pb_line_break_holder] -->            this._continentCounts = new Map();<!-- [et_pb_line_break_holder] -->            this.currentTourIndex = -1;<!-- [et_pb_line_break_holder] -->            this.baseFlatScale = null;<!-- [et_pb_line_break_holder] -->            this.showLabels = false;<!-- [et_pb_line_break_holder] -->            this.currentZoom = 1;<!-- [et_pb_line_break_holder] -->            this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->            this.zoomPhaseProgress = 0;<!-- [et_pb_line_break_holder] -->            this.zoomPhaseProgress = 0; \/\/ 0 at zoom start, 1 at end to fade graticule<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Tiny depth stars for globe-only state<!-- [et_pb_line_break_holder] -->            this.starParticles = this.createStarParticles(140);<!-- [et_pb_line_break_holder] -->            this._lastStarUpdate = performance.now ? performance.now() : Date.now();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.init();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        init() {<!-- [et_pb_line_break_holder] -->            this.setupCanvas();<!-- [et_pb_line_break_holder] -->            this.setupProjection();<!-- [et_pb_line_break_holder] -->            this.setupInteractions();<!-- [et_pb_line_break_holder] -->            window.addEventListener('resize', () => this.handleResize(), { passive: true });<!-- [et_pb_line_break_holder] -->            this.loadWorldData();<!-- [et_pb_line_break_holder] -->            this.loadLogo();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        getDefaultContainerSize() {<!-- [et_pb_line_break_holder] -->            const containerWidth = Math.min(this.width, Math.max(320, window.innerWidth - 40));<!-- [et_pb_line_break_holder] -->            const maxHeight = Math.min(<!-- [et_pb_line_break_holder] -->                this.height,<!-- [et_pb_line_break_holder] -->                window.innerHeight - 40,<!-- [et_pb_line_break_holder] -->                containerWidth * 0.55 \/\/ align closer to final flat map height<!-- [et_pb_line_break_holder] -->            );<!-- [et_pb_line_break_holder] -->            const containerHeight = Math.max(320, maxHeight);<!-- [et_pb_line_break_holder] -->            return { containerWidth, containerHeight };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setCanvasSize(containerWidth, containerHeight, dprOverride) {<!-- [et_pb_line_break_holder] -->            const dpr = dprOverride ?? Math.min(3, window.devicePixelRatio || 2);<!-- [et_pb_line_break_holder] -->            this.dpr = dpr;<!-- [et_pb_line_break_holder] -->            \/\/ Smaller globe radius so the start view sits comfortably in the container<!-- [et_pb_line_break_holder] -->            this.radius = Math.min(containerWidth, containerHeight) \/ 3.0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.canvas.width = containerWidth * dpr;<!-- [et_pb_line_break_holder] -->            this.canvas.height = containerHeight * dpr;<!-- [et_pb_line_break_holder] -->            this.canvas.style.width = `${containerWidth}px`;<!-- [et_pb_line_break_holder] -->            this.canvas.style.height = `${containerHeight}px`;<!-- [et_pb_line_break_holder] -->            this.context.setTransform(dpr, 0, 0, dpr, 0, 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.containerWidth = containerWidth;<!-- [et_pb_line_break_holder] -->            this.containerHeight = containerHeight;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.setupProjection();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupCanvas() {<!-- [et_pb_line_break_holder] -->            const { containerWidth, containerHeight } = this.getDefaultContainerSize();<!-- [et_pb_line_break_holder] -->            this.setCanvasSize(containerWidth, containerHeight);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        sizeFactor() {<!-- [et_pb_line_break_holder] -->            return window.innerWidth < 768 ? 0.7 : 1;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleResize() {<!-- [et_pb_line_break_holder] -->            if (this.isRecording) return; \/\/ avoid disrupting active capture<!-- [et_pb_line_break_holder] -->            const { containerWidth, containerHeight } = this.getDefaultContainerSize();<!-- [et_pb_line_break_holder] -->            const oldBaseScale = this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.setCanvasSize(containerWidth, containerHeight);<!-- [et_pb_line_break_holder] -->            \/\/ Preserve relative zoom if we're in a continent tour<!-- [et_pb_line_break_holder] -->            if (oldBaseScale && this.mode === 'flat') {<!-- [et_pb_line_break_holder] -->                const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->                const zoomRatio = currentScale \/ oldBaseScale;<!-- [et_pb_line_break_holder] -->                this.flatProjection.scale(this.baseFlatScale * zoomRatio);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupProjection() {<!-- [et_pb_line_break_holder] -->            this.orthoProjection = d3.geoOrthographic()<!-- [et_pb_line_break_holder] -->                .scale(this.radius)<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2])<!-- [et_pb_line_break_holder] -->                .clipAngle(90)<!-- [et_pb_line_break_holder] -->                .rotate(this.rotation);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const flatScale = (this.containerWidth \/ (2 * Math.PI)) * 0.9;<!-- [et_pb_line_break_holder] -->            this.baseFlatScale = flatScale;<!-- [et_pb_line_break_holder] -->            this.flatProjection = d3.geoEquirectangular()<!-- [et_pb_line_break_holder] -->                .center([0, 0])<!-- [et_pb_line_break_holder] -->                .scale(flatScale)<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.path = d3.geoPath()<!-- [et_pb_line_break_holder] -->                .projection(this.orthoProjection)<!-- [et_pb_line_break_holder] -->                .context(this.context);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        pointInPolygon(point, polygon) {<!-- [et_pb_line_break_holder] -->            const [x, y] = point;<!-- [et_pb_line_break_holder] -->            let inside = false;<!-- [et_pb_line_break_holder] -->            for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {<!-- [et_pb_line_break_holder] -->                const [xi, yi] = polygon[i];<!-- [et_pb_line_break_holder] -->                const [xj, yj] = polygon[j];<!-- [et_pb_line_break_holder] -->                if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) \/ (yj - yi) + xi) {<!-- [et_pb_line_break_holder] -->                    inside = !inside;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return inside;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        pointInFeature(point, feature) {<!-- [et_pb_line_break_holder] -->            const geometry = feature.geometry;<!-- [et_pb_line_break_holder] -->            if (geometry.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                const coordinates = geometry.coordinates;<!-- [et_pb_line_break_holder] -->                if (!this.pointInPolygon(point, coordinates[0])) return false;<!-- [et_pb_line_break_holder] -->                for (let i = 1; i < coordinates.length; i++) {<!-- [et_pb_line_break_holder] -->                    if (this.pointInPolygon(point, coordinates[i])) return false;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                return true;<!-- [et_pb_line_break_holder] -->            } else if (geometry.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                for (const polygon of geometry.coordinates) {<!-- [et_pb_line_break_holder] -->                    if (this.pointInPolygon(point, polygon[0])) {<!-- [et_pb_line_break_holder] -->                        let inHole = false;<!-- [et_pb_line_break_holder] -->                        for (let i = 1; i < polygon.length; i++) {<!-- [et_pb_line_break_holder] -->                            if (this.pointInPolygon(point, polygon[i])) {<!-- [et_pb_line_break_holder] -->                                inHole = true;<!-- [et_pb_line_break_holder] -->                                break;<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                        if (!inHole) return true;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                return false;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return false;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        generateDotsInPolygon(feature, dotSpacing = 16) {<!-- [et_pb_line_break_holder] -->            \/\/ More robust sampler that handles antimeridian-crossing countries (e.g., Russia)<!-- [et_pb_line_break_holder] -->            const dots = [];<!-- [et_pb_line_break_holder] -->            const stepSize = Math.max(0.5, dotSpacing * 0.08); \/\/ cap max step to keep large countries filled<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const normLng = (lng) => {<!-- [et_pb_line_break_holder] -->                const n = ((lng + 180) % 360 + 360) % 360 - 180;<!-- [et_pb_line_break_holder] -->                return n === -180 ? 180 : n; \/\/ keep boundaries consistent<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const samplePolygon = (rings) => {<!-- [et_pb_line_break_holder] -->                const lons = rings.flat().map(([lng]) => normLng(lng));<!-- [et_pb_line_break_holder] -->                const lats = rings.flat().map(([, lat]) => lat);<!-- [et_pb_line_break_holder] -->                const minLng = Math.min(...lons);<!-- [et_pb_line_break_holder] -->                const maxLng = Math.max(...lons);<!-- [et_pb_line_break_holder] -->                const minLat = Math.min(...lats);<!-- [et_pb_line_break_holder] -->                const maxLat = Math.max(...lats);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const crossesDateline = maxLng - minLng > 180;<!-- [et_pb_line_break_holder] -->                const ranges = crossesDateline ? [[-180, 0], [0, 180]] : [[minLng, maxLng]];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                ranges.forEach(([lngStart, lngEnd]) => {<!-- [et_pb_line_break_holder] -->                    for (let lng = lngStart; lng <= lngEnd; lng += stepSize) {<!-- [et_pb_line_break_holder] -->                        for (let lat = minLat; lat <= maxLat; lat += stepSize) {<!-- [et_pb_line_break_holder] -->                            \/\/ Try with normalized and shifted longitudes so dateline polygons still hit<!-- [et_pb_line_break_holder] -->                            const candidates = [<!-- [et_pb_line_break_holder] -->                                [lng, lat],<!-- [et_pb_line_break_holder] -->                                [lng + 360, lat],<!-- [et_pb_line_break_holder] -->                                [lng - 360, lat]<!-- [et_pb_line_break_holder] -->                            ];<!-- [et_pb_line_break_holder] -->                            if (candidates.some((p) => d3.geoContains(feature, p))) {<!-- [et_pb_line_break_holder] -->                                dots.push([lng, lat]);<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->            if (!geom) return dots;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                samplePolygon(geom.coordinates);<!-- [et_pb_line_break_holder] -->            } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                geom.coordinates.forEach((poly) => samplePolygon(poly));<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            return dots;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        render() {<!-- [et_pb_line_break_holder] -->            this.context.clearRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            this.context.save();<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = this.bgColor || '#ffffff';<!-- [et_pb_line_break_holder] -->            this.context.fillRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            this.context.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.mode === 'globe') {<!-- [et_pb_line_break_holder] -->                this.renderGlobe();<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                this.renderFlat();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        isZoomingFlat() {<!-- [et_pb_line_break_holder] -->            return this.mode === 'flat' && this.zoomPhaseProgress > 0.01;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        angularDistance(lon1, lat1, lon2, lat2) {<!-- [et_pb_line_break_holder] -->            const toRad = (d) => (d * Math.PI) \/ 180;<!-- [et_pb_line_break_holder] -->            const \u03c61 = toRad(lat1);<!-- [et_pb_line_break_holder] -->            const \u03c62 = toRad(lat2);<!-- [et_pb_line_break_holder] -->            const \u0394\u03bb = toRad(lon2 - lon1);<!-- [et_pb_line_break_holder] -->            const sinDLat = Math.sin((\u03c62 - \u03c61) \/ 2);<!-- [et_pb_line_break_holder] -->            const sinDLon = Math.sin(\u0394\u03bb \/ 2);<!-- [et_pb_line_break_holder] -->            const a = sinDLat * sinDLat + Math.cos(\u03c61) * Math.cos(\u03c62) * sinDLon * sinDLon;<!-- [et_pb_line_break_holder] -->            return 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderGlobe() {<!-- [et_pb_line_break_holder] -->            const projection = this.orthoProjection;<!-- [et_pb_line_break_holder] -->            this.path.projection(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentScale = projection.scale();<!-- [et_pb_line_break_holder] -->            const scaleFactor = currentScale \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] -->            const centerX = this.containerWidth \/ 2;<!-- [et_pb_line_break_holder] -->            const centerY = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] -->            const centerCoord = projection.invert([centerX, centerY]);<!-- [et_pb_line_break_holder] -->            const isFront = (lon, lat) => d3.geoDistance([lon, lat], centerCoord) <= Math.PI \/ 2;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Stars sit behind the earth and only show in globe mode<!-- [et_pb_line_break_holder] -->            this.renderStars(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            projection.clipAngle(180);<!-- [et_pb_line_break_holder] -->            this.context.save();<!-- [et_pb_line_break_holder] -->            this.context.filter = 'blur(1px)';<!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.globalAlpha = 0.20;<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            this.context.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            projection.clipAngle(90);<!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->            this.context.lineWidth = 2 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->            this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                projection.clipAngle(180);<!-- [et_pb_line_break_holder] -->                const graticuleBack = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticuleBack());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.14 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.30;<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    this.context.beginPath();<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                    this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    this.context.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.18;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                projection.clipAngle(90);<!-- [et_pb_line_break_holder] -->                const graticuleFront = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticuleFront());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.18 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * scaleFactor * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const dotRadius = 0.6 * sizeFactor; \/\/ keep dots small and consistent<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected &&<!-- [et_pb_line_break_holder] -->                        projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                        projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                        if (isFront(dot.lng, dot.lat)) {<!-- [et_pb_line_break_holder] -->                            const dx = projected[0] - centerX;<!-- [et_pb_line_break_holder] -->                            const dy = projected[1] - centerY;<!-- [et_pb_line_break_holder] -->                            const dist = Math.sqrt(dx * dx + dy * dy);<!-- [et_pb_line_break_holder] -->                            if (dist <= currentScale) {<!-- [et_pb_line_break_holder] -->                                const opacity = (90 - Math.abs(dot.lat)) \/ 90;<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = opacity;<!-- [et_pb_line_break_holder] -->                                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                                this.context.arc(projected[0], projected[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                                this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                                this.context.fill();<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.universities.forEach((uni) => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected &&<!-- [et_pb_line_break_holder] -->                        projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                        projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                        this.context.shadowColor = 'rgba(0,106,179,0.6)';<!-- [et_pb_line_break_holder] -->                        this.context.shadowBlur = 3 * scaleFactor;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetX = 2 * scaleFactor;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetY = 2 * scaleFactor;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                        this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetX = 0;<!-- [et_pb_line_break_holder] -->                        this.context.shadowOffsetY = 0;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.logoImage && this.logoImage.complete) {<!-- [et_pb_line_break_holder] -->                this.context.save();<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.context.arc(this.containerWidth \/ 2, this.containerHeight \/ 2, projection.scale(), 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                this.context.clip();<!-- [et_pb_line_break_holder] -->                if (this.isInteracting) {<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = 0.25;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                const logoSize = 2.2 * projection.scale();<!-- [et_pb_line_break_holder] -->                const x = this.containerWidth \/ 2 - logoSize \/ 2;<!-- [et_pb_line_break_holder] -->                const y = this.containerHeight \/ 2 - logoSize \/ 2;<!-- [et_pb_line_break_holder] -->                this.context.drawImage(this.logoImage, x, y, logoSize, logoSize);<!-- [et_pb_line_break_holder] -->                this.context.restore();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderFlat() {<!-- [et_pb_line_break_holder] -->            const projection = this.flatProjection;<!-- [et_pb_line_break_holder] -->            this.path.projection(projection);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentScale = projection.scale();<!-- [et_pb_line_break_holder] -->            const scaleFactor = currentScale \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.context.beginPath();<!-- [et_pb_line_break_holder] -->            this.path({ type: \"Sphere\" });<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            this.context.fill();<!-- [et_pb_line_break_holder] -->            \/\/ Only draw border when not in zooming flat tour phase<!-- [et_pb_line_break_holder] -->            if (!this.isZoomingFlat()) {<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 1 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                const graticule = d3.geoGraticule().step([15, 15]);<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.path(graticule());<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.8 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 0.4 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Fills with highlight like morph<!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.35;<!-- [et_pb_line_break_holder] -->                this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    this.context.beginPath();<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                    this.context.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    this.context.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    this.path(feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.context.lineWidth = 0.7 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.context.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const dotRadius = 0.6 * sizeFactor; \/\/ consistent size<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Draw markers (consistent size across animation)<!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                const markerBoxes = [];<!-- [et_pb_line_break_holder] -->                const placements = [];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Draw markers and collect their boxes<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                        \/\/ store small box for overlap checks<!-- [et_pb_line_break_holder] -->                        const r = markerRadius + 2;<!-- [et_pb_line_break_holder] -->                        const box = {<!-- [et_pb_line_break_holder] -->                            x: projected[0] - r,<!-- [et_pb_line_break_holder] -->                            y: projected[1] - r,<!-- [et_pb_line_break_holder] -->                            w: r * 2,<!-- [et_pb_line_break_holder] -->                            h: r * 2<!-- [et_pb_line_break_holder] -->                        };<!-- [et_pb_line_break_holder] -->                        markerBoxes.push(box);<!-- [et_pb_line_break_holder] -->                        placements.push({<!-- [et_pb_line_break_holder] -->                            uni,<!-- [et_pb_line_break_holder] -->                            cx: projected[0],<!-- [et_pb_line_break_holder] -->                            cy: projected[1],<!-- [et_pb_line_break_holder] -->                            markerBox: box<!-- [et_pb_line_break_holder] -->                        });<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ University markers (labels removed per request)<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (projected) {<!-- [et_pb_line_break_holder] -->                        this.context.beginPath();<!-- [et_pb_line_break_holder] -->                        this.context.arc(projected[0], projected[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                        this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                        this.context.fill();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Country labels for highlighted countries (only during zoom\/tour)<!-- [et_pb_line_break_holder] -->                if (this.showLabels && this.activeContinentIdx != null) {<!-- [et_pb_line_break_holder] -->                    const countryFontSize = 12 * sizeFactor;<!-- [et_pb_line_break_holder] -->                    this.context.font = `600 ${countryFontSize}px \"Inter\", \"Segoe UI\", sans-serif`;<!-- [et_pb_line_break_holder] -->                    this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    this.context.textBaseline = 'middle';<!-- [et_pb_line_break_holder] -->                    this.context.textAlign = 'left';<!-- [et_pb_line_break_holder] -->                    this.context.shadowColor = 'rgba(0,0,0,0.55)';<!-- [et_pb_line_break_holder] -->                    this.context.shadowBlur = 6;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const placedCountry = [...markerBoxes]; \/\/ avoid overlapping markers<!-- [et_pb_line_break_holder] -->                    const intersects = (a, b) => !(<!-- [et_pb_line_break_holder] -->                        a.x + a.w < b.x ||<!-- [et_pb_line_break_holder] -->                        b.x + b.w < a.x ||<!-- [et_pb_line_break_holder] -->                        a.y + a.h < b.y ||<!-- [et_pb_line_break_holder] -->                        b.y + b.h < a.y<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const gaps = [0, 12, 24, 36, 48, 60];<!-- [et_pb_line_break_holder] -->                    const directions = [<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: 0 },   \/\/ right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: 0 },  \/\/ left<!-- [et_pb_line_break_holder] -->                        { dx: 0, dy: -1 },  \/\/ up<!-- [et_pb_line_break_holder] -->                        { dx: 0, dy: 1 },   \/\/ down<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: -1 },  \/\/ up-right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: -1 }, \/\/ up-left<!-- [et_pb_line_break_holder] -->                        { dx: 1, dy: 1 },   \/\/ down-right<!-- [et_pb_line_break_holder] -->                        { dx: -1, dy: 1 },  \/\/ down-left<!-- [et_pb_line_break_holder] -->                    ];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const margin = 8;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    const activeContinent = this.continentTours[this.activeContinentIdx];<!-- [et_pb_line_break_holder] -->                    const maxDistRad = Math.PI * 0.35; \/\/ tighten: ~63 degrees cone per continent<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    if (this.highlightedFeatureIndices && this.landFeatures?.features?.length && activeContinent) {<!-- [et_pb_line_break_holder] -->                        this.highlightedFeatureIndices.forEach(idx => {<!-- [et_pb_line_break_holder] -->                            const feature = this.landFeatures.features[idx];<!-- [et_pb_line_break_holder] -->                            if (!feature) return;<!-- [et_pb_line_break_holder] -->                            const name = feature.properties?.name;<!-- [et_pb_line_break_holder] -->                            if (!name) return;<!-- [et_pb_line_break_holder] -->                            const centroid = d3.geoCentroid(feature);<!-- [et_pb_line_break_holder] -->                            const projected = projection(centroid);<!-- [et_pb_line_break_holder] -->                            if (!projected) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            \/\/ Skip if centroid far from active continent center<!-- [et_pb_line_break_holder] -->                            const angDist = this.angularDistance(<!-- [et_pb_line_break_holder] -->                                activeContinent.lng, activeContinent.lat,<!-- [et_pb_line_break_holder] -->                                centroid[0], centroid[1]<!-- [et_pb_line_break_holder] -->                            );<!-- [et_pb_line_break_holder] -->                            if (angDist > maxDistRad) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                        \/\/ Skip if the feature\u2019s projected bounds are off-screen<!-- [et_pb_line_break_holder] -->                        const b = d3.geoPath().projection(projection).bounds(feature);<!-- [et_pb_line_break_holder] -->                        const [minX, minY] = b[0];<!-- [et_pb_line_break_holder] -->                        const [maxX, maxY] = b[1];<!-- [et_pb_line_break_holder] -->                        const intersectsViewport = !(<!-- [et_pb_line_break_holder] -->                            maxX < 0 || maxY < 0 ||<!-- [et_pb_line_break_holder] -->                            minX > this.containerWidth || minY > this.containerHeight<!-- [et_pb_line_break_holder] -->                        );<!-- [et_pb_line_break_holder] -->                        if (!intersectsViewport) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            const width = this.context.measureText(name).width;<!-- [et_pb_line_break_holder] -->                            const height = countryFontSize * 1.2;<!-- [et_pb_line_break_holder] -->                            const cx = projected[0];<!-- [et_pb_line_break_holder] -->                            const cy = projected[1];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            let chosen = null;<!-- [et_pb_line_break_holder] -->                            let bestOverlap = Infinity;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            gaps.forEach((gap) => {<!-- [et_pb_line_break_holder] -->                                directions.forEach(({ dx, dy }) => {<!-- [et_pb_line_break_holder] -->                                    const offsetX = dx * gap;<!-- [et_pb_line_break_holder] -->                                    const offsetY = dy * gap;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    let x = cx + (dx >= 0 ? 8 + offsetX : -(width + 8 - offsetX));<!-- [et_pb_line_break_holder] -->                                    let y = cy - height \/ 2 + offsetY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    if (dx === 0) x = cx - width \/ 2 + offsetX;<!-- [et_pb_line_break_holder] -->                                    if (dy === 0) y = cy - height \/ 2 + offsetY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    \/\/ Clamp to stay within canvas<!-- [et_pb_line_break_holder] -->                                    x = Math.max(margin, Math.min(x, this.containerWidth - width - margin));<!-- [et_pb_line_break_holder] -->                                    y = Math.max(margin, Math.min(y, this.containerHeight - height - margin));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                    const box = { x, y, w: width, h: height };<!-- [et_pb_line_break_holder] -->                                    const overlap = placedCountry.reduce((acc, b) => acc + (intersects(b, box) ? 1 : 0), 0);<!-- [et_pb_line_break_holder] -->                                    if (overlap < bestOverlap) {<!-- [et_pb_line_break_holder] -->                                        bestOverlap = overlap;<!-- [et_pb_line_break_holder] -->                                        chosen = { x, y, box, dx, dy, gap };<!-- [et_pb_line_break_holder] -->                                        if (overlap === 0) return;<!-- [et_pb_line_break_holder] -->                                    }<!-- [et_pb_line_break_holder] -->                                });<!-- [et_pb_line_break_holder] -->                            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            if (!chosen) return;<!-- [et_pb_line_break_holder] -->                            placedCountry.push(chosen.box);<!-- [et_pb_line_break_holder] -->                            const textY = chosen.y + height \/ 2;<!-- [et_pb_line_break_holder] -->                            this.context.fillText(name, chosen.x, textY);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                            \/\/ Leader line if offset<!-- [et_pb_line_break_holder] -->                            const distX = chosen.x - cx;<!-- [et_pb_line_break_holder] -->                            const distY = textY - cy;<!-- [et_pb_line_break_holder] -->                            const leaderDist = Math.sqrt(distX * distX + distY * distY);<!-- [et_pb_line_break_holder] -->                            if (leaderDist > 10) {<!-- [et_pb_line_break_holder] -->                                let targetX = chosen.x;<!-- [et_pb_line_break_holder] -->                                let targetY = textY;<!-- [et_pb_line_break_holder] -->                                if (chosen.dx > 0) targetX = chosen.x - 4;<!-- [et_pb_line_break_holder] -->                                else if (chosen.dx < 0) targetX = chosen.x + chosen.box.w + 4;<!-- [et_pb_line_break_holder] -->                                else targetX = cx;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                if (chosen.dy > 0) targetY = chosen.y - 4;<!-- [et_pb_line_break_holder] -->                                else if (chosen.dy < 0) targetY = chosen.y + chosen.box.h + 4;<!-- [et_pb_line_break_holder] -->                                else targetY = textY;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                                this.context.moveTo(cx, cy);<!-- [et_pb_line_break_holder] -->                                this.context.lineTo(targetX, targetY);<!-- [et_pb_line_break_holder] -->                                this.context.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                                this.context.lineWidth = 1;<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = 0.85;<!-- [et_pb_line_break_holder] -->                                this.context.stroke();<!-- [et_pb_line_break_holder] -->                                this.context.globalAlpha = 1;<!-- [et_pb_line_break_holder] -->                            }<!-- [et_pb_line_break_holder] -->                        });<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                    this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.shadowColor = 'transparent';<!-- [et_pb_line_break_holder] -->                this.context.shadowBlur = 0;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/**<!-- [et_pb_line_break_holder] -->         * Scroll-driven animation:<!-- [et_pb_line_break_holder] -->         * 0 \u2192 0.15: Globe rotation<!-- [et_pb_line_break_holder] -->         * 0.15 \u2192 0.25: Morph to flat map<!-- [et_pb_line_break_holder] -->         * 0.25 \u2192 0.35: Show full flat map (pause)<!-- [et_pb_line_break_holder] -->         * 0.35 \u2192 0.85: Continent tours (zoom\/pan with pauses)<!-- [et_pb_line_break_holder] -->         * 0.85 \u2192 1.00: Fold back to globe (loop-friendly)<!-- [et_pb_line_break_holder] -->         *\/<!-- [et_pb_line_break_holder] -->        applyScrollProgress(progress) {<!-- [et_pb_line_break_holder] -->            const p = Math.max(0, Math.min(1, progress));<!-- [et_pb_line_break_holder] -->            const rotatePortion = 0.15;<!-- [et_pb_line_break_holder] -->            const morphPortion = 0.10;<!-- [et_pb_line_break_holder] -->            const flatHoldPortion = 0.10;<!-- [et_pb_line_break_holder] -->            const foldBackPortion = 0.15;<!-- [et_pb_line_break_holder] -->            const morphEnd = rotatePortion + morphPortion;<!-- [et_pb_line_break_holder] -->            const flatHoldEnd = morphEnd + flatHoldPortion;<!-- [et_pb_line_break_holder] -->            const foldBackStart = 1 - foldBackPortion;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Stop any auto motion when scroll is in control<!-- [et_pb_line_break_holder] -->            if (this.autoRotate) {<!-- [et_pb_line_break_holder] -->                this.autoRotate = false;<!-- [et_pb_line_break_holder] -->                if (this.rotationTimer) {<!-- [et_pb_line_break_holder] -->                    this.rotationTimer.stop();<!-- [et_pb_line_break_holder] -->                    this.rotationTimer = null;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Default graticule fade state<!-- [et_pb_line_break_holder] -->                this.zoomPhaseProgress = 0;<!-- [et_pb_line_break_holder] -->            this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (p <= rotatePortion) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 1: Globe rotation<!-- [et_pb_line_break_holder] -->                const t = p \/ rotatePortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeOutCubic(t);<!-- [et_pb_line_break_holder] -->                const angle = this.scrollBaseRotation + eased * 360;<!-- [et_pb_line_break_holder] -->                this.rotation[0] = angle;<!-- [et_pb_line_break_holder] -->                this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                this.mode = 'globe';<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Study Abroad. Find your perfect university now!');<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            } else if (p <= morphEnd) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 2: Morph to flat<!-- [et_pb_line_break_holder] -->                const unfoldT = (p - rotatePortion) \/ morphPortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeInOutCubic(unfoldT);<!-- [et_pb_line_break_holder] -->                this.mode = 'morphing';<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Semester abroad, Bachelor \/ Master Abroad, Double Degree, Gap Year, Summer Sessions, and many more!');<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                if (eased >= 0.98) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    this.renderMorph(eased);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            } else if (p <= flatHoldEnd) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 2.5: Show full flat map before zooming<!-- [et_pb_line_break_holder] -->                this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                this.showLabels = false; \/\/ hide country labels on full map<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('72 Universtities in 25 Countries');<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            } else if (p < foldBackStart) {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 3: Continent tours<!-- [et_pb_line_break_holder] -->                const tourProgress = (p - flatHoldEnd) \/ (foldBackStart - flatHoldEnd);<!-- [et_pb_line_break_holder] -->                \/\/ Ensure we're in flat mode and projection is reset before starting tours<!-- [et_pb_line_break_holder] -->                if (this.mode !== 'flat' && tourProgress < 0.01) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                    this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                this.zoomPhaseProgress = Math.max(0, Math.min(1, tourProgress));<!-- [et_pb_line_break_holder] -->                const fastFadePortion = 0.15; \/\/ fade out early in the tour phase<!-- [et_pb_line_break_holder] -->                const fadeT = Math.max(0, Math.min(1, tourProgress \/ fastFadePortion));<!-- [et_pb_line_break_holder] -->                this.graticuleFade = Math.max(0, 1 - fadeT);<!-- [et_pb_line_break_holder] -->                this.showLabels = true;<!-- [et_pb_line_break_holder] -->                this.handleContinentTour(tourProgress);<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                \/\/ Phase 4: Fold back to globe<!-- [et_pb_line_break_holder] -->                const foldT = (p - foldBackStart) \/ foldBackPortion;<!-- [et_pb_line_break_holder] -->                const eased = this.easeInOutCubic(foldT);<!-- [et_pb_line_break_holder] -->                this.mode = 'morphing';<!-- [et_pb_line_break_holder] -->                \/\/ Fade graticules back in as we fold up<!-- [et_pb_line_break_holder] -->                this.graticuleFade = Math.min(1, eased);<!-- [et_pb_line_break_holder] -->                this.showLabels = false;<!-- [et_pb_line_break_holder] -->                this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->                \/\/ Reverse morph: use (1 - eased) to move from flat -> globe<!-- [et_pb_line_break_holder] -->                this.renderMorph(1 - eased);<!-- [et_pb_line_break_holder] -->                if (foldT >= 0.98) {<!-- [et_pb_line_break_holder] -->                    this.mode = 'globe';<!-- [et_pb_line_break_holder] -->                    this.rotation = [...this.initialRotation];<!-- [et_pb_line_break_holder] -->                    this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                    this.graticuleFade = 1;<!-- [et_pb_line_break_holder] -->                    this.showLabels = false;<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleContinentTour(progress) {<!-- [et_pb_line_break_holder] -->            \/\/ Each continent gets: transition in (15%), hold (25%), transition out (15%)<!-- [et_pb_line_break_holder] -->            \/\/ Total per continent: 55%, with transitions between them<!-- [et_pb_line_break_holder] -->            const transitionIn = 0.15;<!-- [et_pb_line_break_holder] -->            const holdDuration = 0.25;<!-- [et_pb_line_break_holder] -->            const transitionOut = 0.15;<!-- [et_pb_line_break_holder] -->            const continentDuration = transitionIn + holdDuration + transitionOut;<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            const numContinents = this.continentTours.length;<!-- [et_pb_line_break_holder] -->            const returnToMapDuration = 0.20;<!-- [et_pb_line_break_holder] -->            const totalDuration = numContinents * continentDuration + returnToMapDuration;<!-- [et_pb_line_break_holder] -->            const normalizedProgress = Math.min(1, Math.max(0, progress)) * totalDuration;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Ensure we start from full map when entering tour phase<!-- [et_pb_line_break_holder] -->            if (normalizedProgress < 0.001) {<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            let accumulated = 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            for (let i = 0; i < numContinents; i++) {<!-- [et_pb_line_break_holder] -->                const continentStart = accumulated;<!-- [et_pb_line_break_holder] -->                const transitionInEnd = continentStart + transitionIn;<!-- [et_pb_line_break_holder] -->                const holdEnd = transitionInEnd + holdDuration;<!-- [et_pb_line_break_holder] -->                const transitionOutEnd = holdEnd + transitionOut;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (normalizedProgress >= continentStart && normalizedProgress < transitionInEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Transitioning into this continent<!-- [et_pb_line_break_holder] -->                    const t = (normalizedProgress - continentStart) \/ transitionIn;<!-- [et_pb_line_break_holder] -->                    const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                    \/\/ If this is the first continent and we're just starting, ensure we begin from full map<!-- [et_pb_line_break_holder] -->                    if (i === 0 && normalizedProgress < continentStart + 0.01) {<!-- [et_pb_line_break_holder] -->                        this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    this.animateToContinent(i, easedT);<!-- [et_pb_line_break_holder] -->                    const c = this.continentTours[i];<!-- [et_pb_line_break_holder] -->                    const count = this.getContinentUniversityCount(i);<!-- [et_pb_line_break_holder] -->                    this.updatePhaseText(`${count} Universities in ${c.name}`);<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                } else if (normalizedProgress >= transitionInEnd && normalizedProgress < holdEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Holding at this continent<!-- [et_pb_line_break_holder] -->                    this.animateToContinent(i, 1);<!-- [et_pb_line_break_holder] -->                    const c = this.continentTours[i];<!-- [et_pb_line_break_holder] -->                    const count = this.getContinentUniversityCount(i);<!-- [et_pb_line_break_holder] -->                    this.updatePhaseText(`${count} Universities in ${c.name}`);<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                } else if (normalizedProgress >= holdEnd && normalizedProgress < transitionOutEnd) {<!-- [et_pb_line_break_holder] -->                    \/\/ Transitioning out of this continent<!-- [et_pb_line_break_holder] -->                    const t = (normalizedProgress - holdEnd) \/ transitionOut;<!-- [et_pb_line_break_holder] -->                    const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                    const nextIndex = i < numContinents - 1 ? i + 1 : -1;<!-- [et_pb_line_break_holder] -->                    if (nextIndex >= 0) {<!-- [et_pb_line_break_holder] -->                        \/\/ Transition to next continent<!-- [et_pb_line_break_holder] -->                        this.transitionBetweenContinents(i, nextIndex, easedT);<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        \/\/ Transition back to full map<!-- [et_pb_line_break_holder] -->                        this.transitionToFullMap(easedT);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                accumulated = transitionOutEnd;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Final: return to full map<!-- [et_pb_line_break_holder] -->            const returnStart = accumulated;<!-- [et_pb_line_break_holder] -->            if (normalizedProgress >= returnStart) {<!-- [et_pb_line_break_holder] -->                const t = Math.min(1, (normalizedProgress - returnStart) \/ returnToMapDuration);<!-- [et_pb_line_break_holder] -->                const easedT = this.easeInOutCubic(t);<!-- [et_pb_line_break_holder] -->                this.transitionToFullMap(easedT);<!-- [et_pb_line_break_holder] -->                this.updatePhaseText('Apply now to start your study abroad journey!\\n\\nworldofstudents.org');<!-- [et_pb_line_break_holder] -->            } else if (normalizedProgress < 0.001) {<!-- [et_pb_line_break_holder] -->                \/\/ At the very start, show full map<!-- [et_pb_line_break_holder] -->                this.mode = 'flat';<!-- [et_pb_line_break_holder] -->                this.resetFlatProjection();<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        animateToContinent(index, t) {<!-- [et_pb_line_break_holder] -->            const continent = this.continentTours[index];<!-- [et_pb_line_break_holder] -->            const targetScale = this.baseFlatScale * continent.zoom;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const targetCenter = [continent.lng, continent.lat];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Interpolate from current state<!-- [et_pb_line_break_holder] -->            const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->            const currentCenter = this.flatProjection.center();<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            const scale = currentScale + (targetScale - currentScale) * t;<!-- [et_pb_line_break_holder] -->            const centerLng = currentCenter[0] + (targetCenter[0] - currentCenter[0]) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = currentCenter[1] + (targetCenter[1] - currentCenter[1]) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.currentZoom = scale \/ this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = index;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        transitionBetweenContinents(fromIndex, toIndex, t) {<!-- [et_pb_line_break_holder] -->            const from = this.continentTours[fromIndex];<!-- [et_pb_line_break_holder] -->            const to = this.continentTours[toIndex];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const fromScale = this.baseFlatScale * from.zoom;<!-- [et_pb_line_break_holder] -->            const toScale = this.baseFlatScale * to.zoom;<!-- [et_pb_line_break_holder] -->            <!-- [et_pb_line_break_holder] -->            \/\/ Interpolate scale<!-- [et_pb_line_break_holder] -->            const scale = fromScale + (toScale - fromScale) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const centerLng = from.lng + (to.lng - from.lng) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = from.lat + (to.lat - from.lat) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.currentZoom = scale \/ this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = toIndex;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        transitionToFullMap(t) {<!-- [et_pb_line_break_holder] -->            const currentScale = this.flatProjection.scale();<!-- [et_pb_line_break_holder] -->            const targetScale = this.baseFlatScale;<!-- [et_pb_line_break_holder] -->            const scale = currentScale + (targetScale - currentScale) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const currentCenter = this.flatProjection.center();<!-- [et_pb_line_break_holder] -->            const targetCenter = [0, 0];<!-- [et_pb_line_break_holder] -->            const centerLng = currentCenter[0] + (targetCenter[0] - currentCenter[0]) * t;<!-- [et_pb_line_break_holder] -->            const centerLat = currentCenter[1] + (targetCenter[1] - currentCenter[1]) * t;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(scale)<!-- [et_pb_line_break_holder] -->                .center([centerLng, centerLat])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.mode = 'flat';<!-- [et_pb_line_break_holder] -->            this.showLabels = true;<!-- [et_pb_line_break_holder] -->            this.activeContinentIdx = null;<!-- [et_pb_line_break_holder] -->            this.render();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        resetFlatProjection() {<!-- [et_pb_line_break_holder] -->            this.flatProjection<!-- [et_pb_line_break_holder] -->                .scale(this.baseFlatScale)<!-- [et_pb_line_break_holder] -->                .center([0, 0])<!-- [et_pb_line_break_holder] -->                .translate([this.containerWidth \/ 2, this.containerHeight \/ 2]);<!-- [et_pb_line_break_holder] -->            this.currentZoom = 1;<!-- [et_pb_line_break_holder] -->            this.showLabels = false;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        startAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = true;<!-- [et_pb_line_break_holder] -->            this.autoPlayStart = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const step = (ts) => {<!-- [et_pb_line_break_holder] -->                if (!this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->                if (this.autoPlayStart === null) this.autoPlayStart = ts;<!-- [et_pb_line_break_holder] -->                const elapsed = ts - this.autoPlayStart;<!-- [et_pb_line_break_holder] -->                const t = Math.min(1, elapsed \/ this.autoPlayDuration);<!-- [et_pb_line_break_holder] -->                this.applyScrollProgress(t);<!-- [et_pb_line_break_holder] -->                if (t >= 1) {<!-- [et_pb_line_break_holder] -->                    this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->                    this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                this.autoPlayHandle = requestAnimationFrame(step);<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->            this.autoPlayHandle = requestAnimationFrame(step);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        stopAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (!this.isAutoPlaying) return;<!-- [et_pb_line_break_holder] -->            this.isAutoPlaying = false;<!-- [et_pb_line_break_holder] -->            if (this.autoPlayHandle) {<!-- [et_pb_line_break_holder] -->                cancelAnimationFrame(this.autoPlayHandle);<!-- [et_pb_line_break_holder] -->                this.autoPlayHandle = null;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            this.updateAutoPlayButton();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        toggleAutoPlay() {<!-- [et_pb_line_break_holder] -->            if (this.isAutoPlaying) this.stopAutoPlay();<!-- [et_pb_line_break_holder] -->            else this.startAutoPlay();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        updateAutoPlayButton() {<!-- [et_pb_line_break_holder] -->            if (!this.autoplayBtn) return;<!-- [et_pb_line_break_holder] -->            this.autoplayBtn.textContent = this.isAutoPlaying ? 'Stop' : 'Autoplay';<!-- [et_pb_line_break_holder] -->            this.autoplayBtn.style.opacity = this.isAutoPlaying ? '0.95' : '1';<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        updatePhaseText(text) {<!-- [et_pb_line_break_holder] -->            if (!this.phaseText) return;<!-- [et_pb_line_break_holder] -->            if (this._currentPhaseText === text) return;<!-- [et_pb_line_break_holder] -->            this._currentPhaseText = text;<!-- [et_pb_line_break_holder] -->            \/\/ retrigger animation by toggling class<!-- [et_pb_line_break_holder] -->            this.phaseText.classList.remove('visible');<!-- [et_pb_line_break_holder] -->            \/\/ force reflow to restart keyframes<!-- [et_pb_line_break_holder] -->            void this.phaseText.offsetWidth;<!-- [et_pb_line_break_holder] -->            this.phaseText.textContent = text || '';<!-- [et_pb_line_break_holder] -->            if (text) {<!-- [et_pb_line_break_holder] -->                this.phaseText.classList.add('visible');<!-- [et_pb_line_break_holder] -->            } else {<!-- [et_pb_line_break_holder] -->                this.phaseText.classList.remove('visible');<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupScrollTrigger(targetEl, options = {}) {<!-- [et_pb_line_break_holder] -->            const el = targetEl || this.canvas;<!-- [et_pb_line_break_holder] -->            this.scrollBaseRotation = this.rotation[0];<!-- [et_pb_line_break_holder] -->            const startOffsetOpt = options.startOffset;<!-- [et_pb_line_break_holder] -->            const startAtSticky = options.startAtSticky !== false; \/\/ default true<!-- [et_pb_line_break_holder] -->            const endOffset = options.endOffset ?? 0.8; \/\/ finish slightly before sticky ends<!-- [et_pb_line_break_holder] -->            const clamp01 = (v) => Math.max(0, Math.min(1, v));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const onScroll = () => {<!-- [et_pb_line_break_holder] -->                const viewport = window.innerHeight || 1;<!-- [et_pb_line_break_holder] -->                const rect = el.getBoundingClientRect();<!-- [et_pb_line_break_holder] -->                const wrapperHeight = el.offsetHeight || 1;<!-- [et_pb_line_break_holder] -->                const wrapperTopAbs = rect.top + (window.scrollY || window.pageYOffset || 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ How far we've scrolled since the wrapper reached the top of the viewport<!-- [et_pb_line_break_holder] -->                const scrolledInside = (window.scrollY || window.pageYOffset || 0) - wrapperTopAbs;<!-- [et_pb_line_break_holder] -->                const stickyRange = Math.max(1, wrapperHeight - viewport); \/\/ duration of stickiness<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ raw progress: 0 when top first sticks, 1 when sticky duration ends<!-- [et_pb_line_break_holder] -->                const raw = clamp01(scrolledInside \/ stickyRange);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ startOffset: either provided or aligned to stick start<!-- [et_pb_line_break_holder] -->                const startOffset = startOffsetOpt != null ? startOffsetOpt : (startAtSticky ? 0 : 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Map raw progress into [startOffset, endOffset] range<!-- [et_pb_line_break_holder] -->                const normalized = clamp01((raw - startOffset) \/ Math.max(0.0001, endOffset - startOffset));<!-- [et_pb_line_break_holder] -->                this.applyScrollProgress(normalized);<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            ['scroll', 'resize'].forEach(evt =><!-- [et_pb_line_break_holder] -->                window.addEventListener(evt, onScroll, { passive: true })<!-- [et_pb_line_break_holder] -->            );<!-- [et_pb_line_break_holder] -->            onScroll();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ ---- Morph helpers ----<!-- [et_pb_line_break_holder] -->        easeInOutCubic(t) {<!-- [et_pb_line_break_holder] -->            return t < 0.5<!-- [et_pb_line_break_holder] -->                ? 4 * t * t * t<!-- [et_pb_line_break_holder] -->                : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ Faster start, slower end<!-- [et_pb_line_break_holder] -->        easeOutCubic(t) {<!-- [et_pb_line_break_holder] -->            const clamped = Math.min(1, Math.max(0, t));<!-- [et_pb_line_break_holder] -->            return 1 - Math.pow(1 - clamped, 3);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        createForwardInterpolatedProjection(t) {<!-- [et_pb_line_break_holder] -->            \/\/ Use ease-out so the unfold decelerates into the final flat view<!-- [et_pb_line_break_holder] -->            const tt = this.easeOutCubic(t);<!-- [et_pb_line_break_holder] -->            const startProj = this.orthoProjection;<!-- [et_pb_line_break_holder] -->            const endProj = this.flatProjection;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            return (coords) => {<!-- [et_pb_line_break_holder] -->                const p0 = startProj(coords);<!-- [et_pb_line_break_holder] -->                const p1 = endProj(coords);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (!p0 && !p1) return null;<!-- [et_pb_line_break_holder] -->                const x0 = p0 ? p0[0] : p1[0];<!-- [et_pb_line_break_holder] -->                const y0 = p0 ? p0[1] : p1[1];<!-- [et_pb_line_break_holder] -->                const x1 = p1 ? p1[0] : p0[0];<!-- [et_pb_line_break_holder] -->                const y1 = p1 ? p1[1] : p0[1];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                return [<!-- [et_pb_line_break_holder] -->                    x0 + (x1 - x0) * tt,<!-- [et_pb_line_break_holder] -->                    y0 + (y1 - y0) * tt<!-- [et_pb_line_break_holder] -->                ];<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ ---- Lightweight cache helpers (localStorage with TTL) ----<!-- [et_pb_line_break_holder] -->        setCacheItem(key, value, ttlMs = 1000 * 60 * 60 * 24 * 7) { \/\/ default 7 days<!-- [et_pb_line_break_holder] -->            try {<!-- [et_pb_line_break_holder] -->                const expires = Date.now() + ttlMs;<!-- [et_pb_line_break_holder] -->                localStorage.setItem(key, JSON.stringify({ expires, value }));<!-- [et_pb_line_break_holder] -->            } catch (e) {<!-- [et_pb_line_break_holder] -->                \/\/ ignore storage errors (private mode\/full)<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        getCacheItem(key) {<!-- [et_pb_line_break_holder] -->            try {<!-- [et_pb_line_break_holder] -->                const raw = localStorage.getItem(key);<!-- [et_pb_line_break_holder] -->                if (!raw) return null;<!-- [et_pb_line_break_holder] -->                const parsed = JSON.parse(raw);<!-- [et_pb_line_break_holder] -->                if (!parsed || typeof parsed.expires !== 'number') return null;<!-- [et_pb_line_break_holder] -->                if (Date.now() > parsed.expires) {<!-- [et_pb_line_break_holder] -->                    localStorage.removeItem(key);<!-- [et_pb_line_break_holder] -->                    return null;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                return parsed.value;<!-- [et_pb_line_break_holder] -->            } catch (e) {<!-- [et_pb_line_break_holder] -->                return null;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ ---- Starfield helpers (globe-only) ----<!-- [et_pb_line_break_holder] -->        createStarParticles(count) {<!-- [et_pb_line_break_holder] -->            const stars = [];<!-- [et_pb_line_break_holder] -->            for (let i = 0; i < count; i++) {<!-- [et_pb_line_break_holder] -->                stars.push({<!-- [et_pb_line_break_holder] -->                    lng: (Math.random() * 360) - 180,<!-- [et_pb_line_break_holder] -->                    lat: (Math.random() * 160) - 80, \/\/ avoid exact poles<!-- [et_pb_line_break_holder] -->                    depth: 1 + Math.random() * 0.4, \/\/ farther \u2192 smaller\/fainter<!-- [et_pb_line_break_holder] -->                    size: 0.8 + Math.random() * 0.6,<!-- [et_pb_line_break_holder] -->                    alpha: 0.35 + Math.random() * 0.45,<!-- [et_pb_line_break_holder] -->                    drift: (Math.random() * 4 + 1) * (Math.random() < 0.5 ? -1 : 1) \/\/ deg\/sec<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return stars;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        advanceStars(deltaSec) {<!-- [et_pb_line_break_holder] -->            const clampLat = (lat) => Math.max(-88, Math.min(88, lat));<!-- [et_pb_line_break_holder] -->            this.starParticles.forEach((s) => {<!-- [et_pb_line_break_holder] -->                s.lng = this.normalizeLng(s.lng + s.drift * deltaSec * 0.25);<!-- [et_pb_line_break_holder] -->                \/\/ tiny vertical wander to keep motion subtle<!-- [et_pb_line_break_holder] -->                s.lat = clampLat(s.lat + Math.sin((s.lng + s.lat) * 0.05) * 0.03);<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderStars(projection) {<!-- [et_pb_line_break_holder] -->            const cx = this.containerWidth \/ 2;<!-- [et_pb_line_break_holder] -->            const cy = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.context.save();<!-- [et_pb_line_break_holder] -->            this.context.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->            this.starParticles.forEach((s) => {<!-- [et_pb_line_break_holder] -->                const base = projection([s.lng, s.lat]);<!-- [et_pb_line_break_holder] -->                if (!base) return;<!-- [et_pb_line_break_holder] -->                const dx = base[0] - cx;<!-- [et_pb_line_break_holder] -->                const dy = base[1] - cy;<!-- [et_pb_line_break_holder] -->                const depth = s.depth;<!-- [et_pb_line_break_holder] -->                const px = cx + dx * depth;<!-- [et_pb_line_break_holder] -->                const py = cy + dy * depth;<!-- [et_pb_line_break_holder] -->                const r = (s.size * sizeFactor) \/ (depth * 1.4);<!-- [et_pb_line_break_holder] -->                const alpha = Math.min(1, s.alpha \/ depth);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.context.globalAlpha = alpha;<!-- [et_pb_line_break_holder] -->                this.context.beginPath();<!-- [et_pb_line_break_holder] -->                this.context.arc(px, py, r, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                this.context.fill();<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->            this.context.restore();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        buildSphereBoundaryCoords() {<!-- [et_pb_line_break_holder] -->            const coords = [];<!-- [et_pb_line_break_holder] -->            const step = 5;<!-- [et_pb_line_break_holder] -->            for (let lon = -180; lon <= 180; lon += step) {<!-- [et_pb_line_break_holder] -->                coords.push([lon, -89]);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            for (let lon = 180; lon >= -180; lon -= step) {<!-- [et_pb_line_break_holder] -->                coords.push([lon, 89]);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return coords;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ \u2705 NEW: break path when projected x-jump is large (no wrap lines)<!-- [et_pb_line_break_holder] -->        drawLineString(ctx, coords, proj) {<!-- [et_pb_line_break_holder] -->            let prevP = null;<!-- [et_pb_line_break_holder] -->            let prevC = null;<!-- [et_pb_line_break_holder] -->            const thresholdX = this.containerWidth \/ 3; \/\/ stricter to catch dateline jumps<!-- [et_pb_line_break_holder] -->            const thresholdY = this.containerHeight \/ 2;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            coords.forEach((c) => {<!-- [et_pb_line_break_holder] -->                const p = proj(c);<!-- [et_pb_line_break_holder] -->                if (!p) return;<!-- [et_pb_line_break_holder] -->                const shouldBreak =<!-- [et_pb_line_break_holder] -->                    !!prevP &&<!-- [et_pb_line_break_holder] -->                    (<!-- [et_pb_line_break_holder] -->                        Math.abs(p[0] - prevP[0]) > thresholdX ||<!-- [et_pb_line_break_holder] -->                        Math.abs(p[1] - prevP[1]) > thresholdY ||<!-- [et_pb_line_break_holder] -->                        (prevC && Math.abs(c[0] - prevC[0]) > 170) \/\/ large lon jump \u2192 new subpath<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (!prevP || shouldBreak) {<!-- [et_pb_line_break_holder] -->                    ctx.moveTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    ctx.lineTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->                prevP = p;<!-- [et_pb_line_break_holder] -->                prevC = c;<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        drawPolygon(ctx, coords, proj) {<!-- [et_pb_line_break_holder] -->            const thresholdX = this.containerWidth \/ 3;<!-- [et_pb_line_break_holder] -->            coords.forEach((ring) => {<!-- [et_pb_line_break_holder] -->                let prevP = null;<!-- [et_pb_line_break_holder] -->                let prevC = null;<!-- [et_pb_line_break_holder] -->                let firstP = null;<!-- [et_pb_line_break_holder] -->                let firstC = null;<!-- [et_pb_line_break_holder] -->                ring.forEach((c) => {<!-- [et_pb_line_break_holder] -->                    const p = proj(c);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    const shouldBreak =<!-- [et_pb_line_break_holder] -->                        !!prevP &&<!-- [et_pb_line_break_holder] -->                        (<!-- [et_pb_line_break_holder] -->                            Math.abs(p[0] - prevP[0]) > thresholdX ||<!-- [et_pb_line_break_holder] -->                            (prevC && Math.abs(c[0] - prevC[0]) > 170)<!-- [et_pb_line_break_holder] -->                        );<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                    if (!prevP || shouldBreak) {<!-- [et_pb_line_break_holder] -->                        ctx.moveTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                        firstP = firstP || p;<!-- [et_pb_line_break_holder] -->                        firstC = firstC || c;<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        ctx.lineTo(p[0], p[1]);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    prevP = p;<!-- [et_pb_line_break_holder] -->                    prevC = c;<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                \/\/ Close ring only when it would not span the dateline<!-- [et_pb_line_break_holder] -->                if (firstP && prevP) {<!-- [et_pb_line_break_holder] -->                    const dxClose = Math.abs(firstP[0] - prevP[0]);<!-- [et_pb_line_break_holder] -->                    const lonJump = firstC && prevC ? Math.abs(firstC[0] - prevC[0]) : 0;<!-- [et_pb_line_break_holder] -->                    if (dxClose <= thresholdX &#038;&#038; lonJump <= 170) {<!-- [et_pb_line_break_holder] -->                        ctx.lineTo(firstP[0], firstP[1]);<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        ctx.moveTo(firstP[0], firstP[1]);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        renderMorph(t) {<!-- [et_pb_line_break_holder] -->            const proj = this.createForwardInterpolatedProjection(t);<!-- [et_pb_line_break_holder] -->            const ctx = this.context;<!-- [et_pb_line_break_holder] -->            const morphScale = this.flatProjection.scale() \/ this.radius;<!-- [et_pb_line_break_holder] -->            const sizeFactor = this.sizeFactor();<!-- [et_pb_line_break_holder] -->            const dotRadius = 0.5 * morphScale * sizeFactor;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            ctx.clearRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            ctx.save();<!-- [et_pb_line_break_holder] -->            ctx.fillStyle = this.bgColor || '#ffffff';<!-- [et_pb_line_break_holder] -->            ctx.fillRect(0, 0, this.containerWidth, this.containerHeight);<!-- [et_pb_line_break_holder] -->            ctx.restore();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            \/\/ Ocean plate<!-- [et_pb_line_break_holder] -->            ctx.beginPath();<!-- [et_pb_line_break_holder] -->            this.drawLineString(ctx, this.sphereBoundary, proj);<!-- [et_pb_line_break_holder] -->            ctx.closePath();<!-- [et_pb_line_break_holder] -->            ctx.fillStyle = '#005499';<!-- [et_pb_line_break_holder] -->            ctx.fill();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.landFeatures) {<!-- [et_pb_line_break_holder] -->                \/\/ Graticule<!-- [et_pb_line_break_holder] -->                if (this.graticuleGeo && this.graticuleGeo.type === 'MultiLineString') {<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    this.graticuleGeo.coordinates.forEach(line => {<!-- [et_pb_line_break_holder] -->                        this.drawLineString(ctx, line, proj);<!-- [et_pb_line_break_holder] -->                    });<!-- [et_pb_line_break_holder] -->                    ctx.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = 0.4 * this.graticuleFade;<!-- [et_pb_line_break_holder] -->                    ctx.lineWidth = 0.8 * sizeFactor;<!-- [et_pb_line_break_holder] -->                    ctx.stroke();<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = 1;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Fills<!-- [et_pb_line_break_holder] -->                const baseFillAlpha = 0.05;<!-- [et_pb_line_break_holder] -->                const highlightAlpha = 0.35;<!-- [et_pb_line_break_holder] -->                ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                    const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->                    if (!geom) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                        this.drawPolygon(ctx, geom.coordinates, proj);<!-- [et_pb_line_break_holder] -->                    } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                        geom.coordinates.forEach(poly => this.drawPolygon(ctx, poly, proj));<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    ctx.globalAlpha = this.highlightedFeatureIndices.has(idx)<!-- [et_pb_line_break_holder] -->                        ? highlightAlpha<!-- [et_pb_line_break_holder] -->                        : baseFillAlpha;<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                ctx.globalAlpha = 1;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Borders (draw after fills so every country gets stroked)<!-- [et_pb_line_break_holder] -->                ctx.beginPath();<!-- [et_pb_line_break_holder] -->                this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                    const geom = feature.geometry;<!-- [et_pb_line_break_holder] -->                    if (!geom) return;<!-- [et_pb_line_break_holder] -->                    if (geom.type === 'Polygon') {<!-- [et_pb_line_break_holder] -->                        this.drawPolygon(ctx, geom.coordinates, proj);<!-- [et_pb_line_break_holder] -->                    } else if (geom.type === 'MultiPolygon') {<!-- [et_pb_line_break_holder] -->                        geom.coordinates.forEach(poly => this.drawPolygon(ctx, poly, proj));<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                ctx.strokeStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                ctx.lineWidth = 0.7 * sizeFactor;<!-- [et_pb_line_break_holder] -->                ctx.stroke();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ Dots<!-- [et_pb_line_break_holder] -->                this.allDots.forEach(dot => {<!-- [et_pb_line_break_holder] -->                    const p = proj([dot.lng, dot.lat]);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    ctx.arc(p[0], p[1], dotRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                    ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ University markers (consistent size through morph)<!-- [et_pb_line_break_holder] -->                const markerRadius = 3 * sizeFactor;<!-- [et_pb_line_break_holder] -->                this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                    const p = proj([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                    if (!p) return;<!-- [et_pb_line_break_holder] -->                    ctx.beginPath();<!-- [et_pb_line_break_holder] -->                    ctx.arc(p[0], p[1], markerRadius, 0, 2 * Math.PI);<!-- [et_pb_line_break_holder] -->                    ctx.fillStyle = '#ffffff';<!-- [et_pb_line_break_holder] -->                    ctx.fill();<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        async loadWorldData() {<!-- [et_pb_line_break_holder] -->            try {<!-- [et_pb_line_break_holder] -->                this.isLoading = true;<!-- [et_pb_line_break_holder] -->                const cacheKey = 'wos_world_countries_v1';<!-- [et_pb_line_break_holder] -->                const cached = this.getCacheItem(cacheKey);<!-- [et_pb_line_break_holder] -->                if (cached?.features) {<!-- [et_pb_line_break_holder] -->                    this.landFeatures = cached;<!-- [et_pb_line_break_holder] -->                } else {<!-- [et_pb_line_break_holder] -->                    \/\/ Lightweight country TopoJSON (much smaller than full GeoJSON)<!-- [et_pb_line_break_holder] -->                    let response = await fetch(<!-- [et_pb_line_break_holder] -->                        'https:\/\/cdn.jsdelivr.net\/npm\/world-atlas@2\/countries-110m.json',<!-- [et_pb_line_break_holder] -->                        { cache: 'force-cache' }<!-- [et_pb_line_break_holder] -->                    );<!-- [et_pb_line_break_holder] -->                    if (!response.ok) {<!-- [et_pb_line_break_holder] -->                        \/\/ fallback to lightweight land polygons if countries fail<!-- [et_pb_line_break_holder] -->                        response = await fetch(<!-- [et_pb_line_break_holder] -->                            'https:\/\/raw.githubusercontent.com\/martynafford\/natural-earth-geojson\/master\/110m\/physical\/ne_110m_land.json',<!-- [et_pb_line_break_holder] -->                            { cache: 'force-cache' }<!-- [et_pb_line_break_holder] -->                        );<!-- [et_pb_line_break_holder] -->                        if (!response.ok) throw new Error('Failed to load country\/land data');<!-- [et_pb_line_break_holder] -->                        this.landFeatures = await response.json();<!-- [et_pb_line_break_holder] -->                    } else {<!-- [et_pb_line_break_holder] -->                        const topo = await response.json();<!-- [et_pb_line_break_holder] -->                        this.landFeatures = topojson.feature(topo, topo.objects.countries);<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    \/\/ cache parsed GeoJSON<!-- [et_pb_line_break_holder] -->                    this.setCacheItem(cacheKey, this.landFeatures);<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.landFeatures.features.forEach(feature => {<!-- [et_pb_line_break_holder] -->                const [[minLng, minLat], [maxLng, maxLat]] = d3.geoBounds(feature);<!-- [et_pb_line_break_holder] -->                const lonSpan = maxLng - minLng;<!-- [et_pb_line_break_holder] -->                \/\/ Reduced density overall; still a bit denser for very wide countries<!-- [et_pb_line_break_holder] -->                const spacing = lonSpan > 120 ? 16 : 22;<!-- [et_pb_line_break_holder] -->                const dots = this.generateDotsInPolygon(feature, spacing);<!-- [et_pb_line_break_holder] -->                dots.forEach(([lng, lat]) => {<!-- [et_pb_line_break_holder] -->                    this.allDots.push({ lng, lat, visible: true });<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                await this.loadUniversitiesData();<!-- [et_pb_line_break_holder] -->                this.computeHighlightedFeatures();<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->                this.isLoading = false;<!-- [et_pb_line_break_holder] -->                this.loadingOverlay.style.display = 'none';<!-- [et_pb_line_break_holder] -->                if (this.autoRotate) {<!-- [et_pb_line_break_holder] -->                    this.startRotation();<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            } catch (err) {<!-- [et_pb_line_break_holder] -->                this.showError('Failed to load land map data');<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        async loadUniversitiesData() {<!-- [et_pb_line_break_holder] -->            try {<!-- [et_pb_line_break_holder] -->                const cacheKey = 'wos_universities_v1';<!-- [et_pb_line_break_holder] -->                const cached = this.getCacheItem(cacheKey);<!-- [et_pb_line_break_holder] -->                if (Array.isArray(cached) && cached.length) {<!-- [et_pb_line_break_holder] -->                    this.universities = cached;<!-- [et_pb_line_break_holder] -->                    return;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const res = await fetch('https:\/\/worldofstudents.org\/universities.json', {<!-- [et_pb_line_break_holder] -->                    cache: 'force-cache'<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] -->                if (!res.ok) throw new Error('Failed to fetch universities.json');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const data = await res.json();<!-- [et_pb_line_break_holder] -->                const items = data?.mainEntity?.itemListElement || [];<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const parsed = items<!-- [et_pb_line_break_holder] -->                    .map(entry => entry?.item)<!-- [et_pb_line_break_holder] -->                    .map(item => {<!-- [et_pb_line_break_holder] -->                        const lat = parseFloat(item?.geo?.latitude);<!-- [et_pb_line_break_holder] -->                        const lng = parseFloat(item?.geo?.longitude);<!-- [et_pb_line_break_holder] -->                        if (!item?.name || !Number.isFinite(lat) || !Number.isFinite(lng)) {<!-- [et_pb_line_break_holder] -->                            return null;<!-- [et_pb_line_break_holder] -->                        }<!-- [et_pb_line_break_holder] -->                        return {<!-- [et_pb_line_break_holder] -->                            name: item.name,<!-- [et_pb_line_break_holder] -->                            lat,<!-- [et_pb_line_break_holder] -->                            lng,<!-- [et_pb_line_break_holder] -->                            country: item?.address?.countryName || item?.address?.addressCountry || ''<!-- [et_pb_line_break_holder] -->                        };<!-- [et_pb_line_break_holder] -->                    })<!-- [et_pb_line_break_holder] -->                    .filter(Boolean);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.universities = parsed.length<!-- [et_pb_line_break_holder] -->                    ? parsed<!-- [et_pb_line_break_holder] -->                    : universityData.map(item => ({<!-- [et_pb_line_break_holder] -->                        name: item.name,<!-- [et_pb_line_break_holder] -->                        lat: parseFloat(item.lat),<!-- [et_pb_line_break_holder] -->                        lng: parseFloat(item.lng),<!-- [et_pb_line_break_holder] -->                        country: item.country<!-- [et_pb_line_break_holder] -->                    }));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                this.setCacheItem(cacheKey, this.universities, 1000 * 60 * 60 * 24); \/\/ 1 day cache<!-- [et_pb_line_break_holder] -->            } catch (err) {<!-- [et_pb_line_break_holder] -->                console.error('Failed to load universities.json, using fallback data', err);<!-- [et_pb_line_break_holder] -->                this.universities = universityData.map(item => ({<!-- [et_pb_line_break_holder] -->                    name: item.name,<!-- [et_pb_line_break_holder] -->                    lat: parseFloat(item.lat),<!-- [et_pb_line_break_holder] -->                    lng: parseFloat(item.lng),<!-- [et_pb_line_break_holder] -->                    country: item.country<!-- [et_pb_line_break_holder] -->                }));<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            \/\/ Invalidate cached continent counts when the dataset updates<!-- [et_pb_line_break_holder] -->            this._continentCounts.clear();<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        computeHighlightedFeatures() {<!-- [et_pb_line_break_holder] -->            this.highlightedFeatureIndices = new Set();<!-- [et_pb_line_break_holder] -->            if (!this.landFeatures?.features?.length || !this.universities?.length) return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.landFeatures.features.forEach((feature, idx) => {<!-- [et_pb_line_break_holder] -->                const bounds = d3.geoBounds(feature);<!-- [et_pb_line_break_holder] -->                const [[minLng, minLat], [maxLng, maxLat]] = bounds;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const hasUni = this.universities.some(uni => {<!-- [et_pb_line_break_holder] -->                    \/\/ quick bbox reject<!-- [et_pb_line_break_holder] -->                    if (uni.lng < minLng || uni.lng > maxLng || uni.lat < minLat || uni.lat > maxLat) {<!-- [et_pb_line_break_holder] -->                        return false;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                    return this.pointInFeature([uni.lng, uni.lat], feature);<!-- [et_pb_line_break_holder] -->                });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                if (hasUni) this.highlightedFeatureIndices.add(idx);<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        loadLogo() {<!-- [et_pb_line_break_holder] -->            this.logoImage = new Image();<!-- [et_pb_line_break_holder] -->            this.logoImage.crossOrigin = 'anonymous';<!-- [et_pb_line_break_holder] -->            this.logoImage.src = 'https:\/\/worldofstudents.org\/wp-content\/uploads\/2025\/09\/WOS_Logo_white.png';<!-- [et_pb_line_break_holder] -->            this.logoImage.onload = () => {<!-- [et_pb_line_break_holder] -->                this.render();<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        showError(message) {<!-- [et_pb_line_break_holder] -->            this.error = message;<!-- [et_pb_line_break_holder] -->            this.isLoading = false;<!-- [et_pb_line_break_holder] -->            this.loadingOverlay.innerHTML = `<!-- [et_pb_line_break_holder] -->                <\/p>\n<div class=\"error-state\"><!-- [et_pb_line_break_holder] -->                    <\/p>\n<div class=\"error-title\">Error loading Earth visualization<\/div>\n<p><!-- [et_pb_line_break_holder] -->                    <\/p>\n<div class=\"error-message\">${message}<\/div>\n<p><!-- [et_pb_line_break_holder] -->                <\/div>\n<p><!-- [et_pb_line_break_holder] -->            `;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        getContinentUniversityCount(index) {<!-- [et_pb_line_break_holder] -->            if (this._continentCounts.has(index)) {<!-- [et_pb_line_break_holder] -->                return this._continentCounts.get(index);<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const tour = this.continentTours[index];<!-- [et_pb_line_break_holder] -->            if (!tour || !Array.isArray(this.universities)) return 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const latRange = tour.latRange || [-90, 90];<!-- [et_pb_line_break_holder] -->            const lngRanges = tour.lngRanges || [[-180, 180]];<!-- [et_pb_line_break_holder] -->            const countrySet = new Set((tour.countries || []).map(c => c.toLowerCase()));<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const inLatRange = (lat) => lat >= latRange[0] && lat <= latRange[1];<!-- [et_pb_line_break_holder] -->            const inLngRange = (lng) => {<!-- [et_pb_line_break_holder] -->                const normLng = this.normalizeLng(lng);<!-- [et_pb_line_break_holder] -->                return lngRanges.some(([min, max]) => this.lngWithinRange(normLng, min, max));<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] -->            const inCountrySet = (country) => {<!-- [et_pb_line_break_holder] -->                if (!country) return false;<!-- [et_pb_line_break_holder] -->                const c = country.toLowerCase().trim();<!-- [et_pb_line_break_holder] -->                return countrySet.has(c);<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const count = this.universities.reduce((acc, uni) => {<!-- [et_pb_line_break_holder] -->                if (!Number.isFinite(uni.lat) || !Number.isFinite(uni.lng)) return acc;<!-- [et_pb_line_break_holder] -->                const countryOk = countrySet.size ? inCountrySet(uni.country) : true;<!-- [et_pb_line_break_holder] -->                const geoOk = inLatRange(uni.lat) && inLngRange(uni.lng);<!-- [et_pb_line_break_holder] -->                if (countryOk && geoOk) return acc + 1;<!-- [et_pb_line_break_holder] -->                return acc;<!-- [et_pb_line_break_holder] -->            }, 0);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this._continentCounts.set(index, count);<!-- [et_pb_line_break_holder] -->            return count;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        normalizeLng(lng) {<!-- [et_pb_line_break_holder] -->            const n = ((lng + 180) % 360 + 360) % 360 - 180;<!-- [et_pb_line_break_holder] -->            return n === -180 ? 180 : n;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        lngWithinRange(lng, min, max) {<!-- [et_pb_line_break_holder] -->            const nMin = this.normalizeLng(min);<!-- [et_pb_line_break_holder] -->            const nMax = this.normalizeLng(max);<!-- [et_pb_line_break_holder] -->            if (nMin <= nMax) {<!-- [et_pb_line_break_holder] -->                return lng >= nMin && lng <= nMax;<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->            return lng >= nMin || lng <= nMax;<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        startRotation() {<!-- [et_pb_line_break_holder] -->            this._lastElapsed = 0;<!-- [et_pb_line_break_holder] -->            this._lastFrame = 0;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const rotate = (elapsed) => {<!-- [et_pb_line_break_holder] -->                if (!this.autoRotate || this.mode !== 'globe') return;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const deltaMs = elapsed - this._lastElapsed;<!-- [et_pb_line_break_holder] -->                this._lastElapsed = elapsed;<!-- [et_pb_line_break_holder] -->                const deltaSec = Math.max(0, deltaMs) \/ 1000;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                \/\/ gently drift stars while globe rotates<!-- [et_pb_line_break_holder] -->                this.advanceStars(deltaSec);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const degPerSec = this.rotationSpeed * 60;<!-- [et_pb_line_break_holder] -->                this.rotation[0] += degPerSec * deltaSec;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->                const FRAME_MS = this.isInteracting ? 16 : 33;<!-- [et_pb_line_break_holder] -->                if (elapsed - this._lastFrame >= FRAME_MS) {<!-- [et_pb_line_break_holder] -->                    this.orthoProjection.rotate(this.rotation);<!-- [et_pb_line_break_holder] -->                    this.render();<!-- [et_pb_line_break_holder] -->                    this._lastFrame = elapsed;<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            };<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (this.rotationTimer) this.rotationTimer.stop();<!-- [et_pb_line_break_holder] -->            this.rotationTimer = d3.timer(rotate);<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        setupInteractions() {<!-- [et_pb_line_break_holder] -->            \/\/ Disable drag\/zoom; keep hover for tooltip<!-- [et_pb_line_break_holder] -->            this.canvas.addEventListener('mousemove', (event) => this.handleMouseMove(event));<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        handleMouseMove(event) {<!-- [et_pb_line_break_holder] -->            const rect = this.canvas.getBoundingClientRect();<!-- [et_pb_line_break_holder] -->            const x = event.clientX - rect.left;<!-- [et_pb_line_break_holder] -->            const y = event.clientY - rect.top;<!-- [et_pb_line_break_holder] -->            let hoveredUni = null;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            const projection = (this.mode === 'flat' || this.mode === 'morphing') ? this.flatProjection : this.orthoProjection;<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            this.universities.forEach(uni => {<!-- [et_pb_line_break_holder] -->                const projected = projection([uni.lng, uni.lat]);<!-- [et_pb_line_break_holder] -->                if (projected &&<!-- [et_pb_line_break_holder] -->                    projected[0] >= 0 && projected[0] <= this.containerWidth &#038;&#038;<!-- [et_pb_line_break_holder] -->                    projected[1] >= 0 && projected[1] <= this.containerHeight) {<!-- [et_pb_line_break_holder] -->                    const dx = projected[0] - x;<!-- [et_pb_line_break_holder] -->                    const dy = projected[1] - y;<!-- [et_pb_line_break_holder] -->                    const distance = Math.sqrt(dx * dx + dy * dy);<!-- [et_pb_line_break_holder] -->                    if (distance < 15) {<!-- [et_pb_line_break_holder] -->                        hoveredUni = uni;<!-- [et_pb_line_break_holder] -->                    }<!-- [et_pb_line_break_holder] -->                }<!-- [et_pb_line_break_holder] -->            });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->            if (hoveredUni && this.tooltip) {<!-- [et_pb_line_break_holder] -->                this.tooltip.style.display = 'block';<!-- [et_pb_line_break_holder] -->                this.tooltip.style.left = `${x + 10}px`;<!-- [et_pb_line_break_holder] -->                this.tooltip.style.top = `${y + 10}px`;<!-- [et_pb_line_break_holder] -->                this.tooltip.innerHTML = `<strong>${hoveredUni.name}<\/strong><!\u2013- [et_pb_br_holder] -\u2013>${hoveredUni.country}`;<!-- [et_pb_line_break_holder] -->            } else if (this.tooltip) {<!-- [et_pb_line_break_holder] -->                this.tooltip.style.display = 'none';<!-- [et_pb_line_break_holder] -->            }<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] -->    }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->    document.addEventListener('DOMContentLoaded', () => {<!-- [et_pb_line_break_holder] -->        const earth = new RotatingEarth('earth-canvas', {<!-- [et_pb_line_break_holder] -->            width: 1080,<!-- [et_pb_line_break_holder] -->            height: 540,<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->        earth.autoplayBtn = document.getElementById('autoplay-btn');<!-- [et_pb_line_break_holder] -->        if (earth.autoplayBtn) {<!-- [et_pb_line_break_holder] -->            earth.autoplayBtn.addEventListener('click', () => earth.toggleAutoPlay());<!-- [et_pb_line_break_holder] -->        }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] -->        \/\/ Drive the animation by scroll progress; use the wrapper for full scroll length<!-- [et_pb_line_break_holder] -->        \/\/ startOffset delays the start; endOffset ends a bit before the wrapper ends<!-- [et_pb_line_break_holder] -->        earth.autoRotate = false;<!-- [et_pb_line_break_holder] -->        earth.setupScrollTrigger(document.getElementById('earth-sticky-wrapper'), {<!-- [et_pb_line_break_holder] -->            \/\/ startAtSticky uses the point where the wrapper first pins as the start<!-- [et_pb_line_break_holder] -->            startAtSticky: true,<!-- [et_pb_line_break_holder] -->            endOffset: 0.8<!-- [et_pb_line_break_holder] -->        });<!-- [et_pb_line_break_holder] -->    });<!-- [et_pb_line_break_holder] --><\/script><!-- [et_pb_line_break_holder] -->[\/et_pb_fullwidth_code][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; fullwidth=&#8221;on&#8221; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; global_colors_info=&#8221;{}&#8221;][et_pb_fullwidth_code _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; global_colors_info=&#8221;{}&#8221;]<div id=\"search-filter-map-dev\" style=\"padding: 19px 15px 20px;\"\n     data-preset-country=\"\"\n     data-preset-degree=\"\"\n     data-preset-teaches=\"\"\n     data-preset-program=\"\"\n>\n    <div class=\"search-filter-map-dev-head\">\n        <div class=\"filter-hero\">\n            <h1 id=\"page-headline\" style=\"font-size: 30px; font-weight: bold; line-height: 1.25; color:#fff;\">\n                Your studies abroad.                <br><span style=\"color: #b0e6ff;color: #fff;opacity: 0.8;font-size: 25px;\">Find the right university now.<\/span>\n            <\/h1>\n            <div style=\"position:relative;\" class=\"search-container\">\n                <label for=\"filter-search\" class=\"screen-reader-text\">Where should your journey take you?<\/label>\n                <input type=\"text\" id=\"filter-search\" placeholder=\"Where should your journey take you?\" style=\" width: 100%; margin-top: 12px; max-width: 100%; outline: none !important; border-color: #006ab3 !important; box-shadow: 0 0 0 3px rgb(255 255 255 \/ 50%) !important; margin-bottom: 0; font-size: 16px; border: 2px solid #ddd; border-radius: 4px; color: #0f2a3c !important; \">\n                <svg id=\"search-icon\" xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#999999\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\" position: absolute; right: 15px; top: 23px; pointer-events: none; \"><circle cx=\"11\" cy=\"11\" r=\"8\"><\/circle><path d=\"m21 21-4.3-4.3\"><\/path><\/svg>\n                <svg id=\"clear-search-icon\" xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#999999\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"position: absolute; right: 15px; top: 23px; cursor: pointer; display: none;\" role=\"button\" tabindex=\"0\" aria-label=\"Clear search input\"><title>Clear search input<\/title><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"><\/line><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"><\/line><\/svg>\n                <div class=\"suggestions-dropdown\" id=\"filter-suggestions\"><ul><\/ul><\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n<\/div>\n<div class=\"search-filter-map-dev-content\">\n    <div id=\"search-result-header\"><p style=\"min-height: 24px;\">&nbsp;<\/p><\/div>\n    <div class=\"search-filter\" style=\"border-radius: 8px; width:100%; margin-top:0;\">\n      <div class=\"filter-menu\">\n      <details class=\"filter-section\">\n          <summary style=\"padding: 10px 15px; border-radius: 4px; background: #fff; border: 1px solid #0f2a3c; outline: none !important; height: 41px; overflow: hidden;\" aria-expanded=\"false\" aria-controls=\"filter-country-buttons\"><h3><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"margin-right: 6px;\"><path d=\"M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z\"><\/path><circle cx=\"12\" cy=\"10\" r=\"3\"><\/circle><\/svg> Countries <span class=\"filter-count\" id=\"country-count\"><\/span><span class=\"filter-active-count\" id=\"country-active-count\"><\/span><\/h3><\/summary>\n          <div class=\"filter-buttons\" id=\"filter-country-buttons\" style=\"width: calc(500% + 60px);\"><\/div>\n      <\/details>\n      <details class=\"filter-section\">\n          <summary style=\"padding: 10px 15px; border-radius: 4px; background: #fff; border: 1px solid #0f2a3c; outline: none !important; height: 41px; overflow: hidden;\" aria-expanded=\"false\" aria-controls=\"filter-degree-buttons\"><h3><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"width: 20px; height: 20px; margin-right: 6px; margin-left: -1px;\"><path d=\"M21.42 10.922a1 1 0 0 0-.019-1.838L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.832l8.57 3.908a2 2 0 0 0 1.66 0z\"><\/path><path d=\"M22 10v6\"><\/path><path d=\"M6 12.5V16a6 3 0 0 0 12 0v-3.5\"><\/path><\/svg> Degree Types <span class=\"filter-count\" id=\"degree-count\"><\/span><span class=\"filter-active-count\" id=\"degree-active-count\"><\/span><\/h3><\/summary>\n          <div class=\"filter-buttons\" id=\"filter-degree-buttons\" style=\"width: calc(500% + 60px); margin-left: calc(-100% - 15px);\"><\/div>\n      <\/details>\n      <details class=\"filter-section\">\n          <summary style=\"padding: 10px 15px; border-radius: 4px; background: #fff; border: 1px solid #0f2a3c; outline: none !important; height: 41px; overflow: hidden;\" aria-expanded=\"false\" aria-controls=\"filter-teaches-buttons\"><h3><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"margin-right: 5px;\"><path d=\"m16 6 4 14\"><\/path><path d=\"M12 6v14\"><\/path><path d=\"M8 8v12\"><\/path><path d=\"M4 4v16\"><\/path><\/svg> <span style=\"\">Study Areas <\/span><span class=\"filter-count\" id=\"teaches-count\"><\/span><span class=\"filter-active-count\" id=\"teaches-active-count\"><\/span><\/h3><\/summary>\n          <div class=\"filter-buttons\" id=\"filter-teaches-buttons\" style=\"width: calc(500% + 60px); margin-left: calc(-200% - 30px);\"><\/div>\n      <\/details>\n      <details class=\"filter-section\">\n          <summary style=\"padding: 10px 15px; border-radius: 4px; background: #fff; border: 1px solid #0f2a3c; outline: none !important; height: 41px; overflow: hidden;\" aria-expanded=\"false\" aria-controls=\"filter-program-buttons\"><h3><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"width: 18px; height: 18px; margin-right: 8px; margin-left: -1px;\"><path d=\"M12 7v14\"><\/path><path d=\"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z\"><\/path><\/svg> Programs <span class=\"filter-count\" id=\"program-count\"><\/span><span class=\"filter-active-count\" id=\"program-active-count\"><\/span><\/h3><\/summary>\n          <div class=\"filter-buttons\" id=\"filter-program-buttons\" style=\"width: calc(500% + 60px); margin-left: calc(-300% - 45px);\"><\/div>\n      <\/details>\n      <details class=\"filter-section\">\n          <summary style=\"padding: 10px 15px; border-radius: 4px; background: #fff; border: 1px solid #0f2a3c; outline: none !important; height: 41px; overflow: hidden;\" aria-expanded=\"false\" aria-controls=\"filter-budget-section\"><h3><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-euro-icon lucide-euro\" style=\" width: 18px; height: 18px; margin-right: 5px; margin-left: -1px; margin-bottom: 1px; \"><path d=\"M4 10h12\"><\/path><path d=\"M4 14h9\"><\/path><path d=\"M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12c0 4.4 3.5 8 7.8 8 2 0 3.8-.8 5.2-2\"><\/path><\/svg> Budget <span class=\"filter-active-count\" id=\"budget-active-count\"><\/span><\/h3><\/summary>\n          <div id=\"filter-budget-section\" style=\"padding:0 0 12px 0; width: calc(500% + 60px); margin-left: calc(-400% - 60px);\">\n              <div id=\"budget-quickselect\" class=\"budget-quickselect\" role=\"group\" aria-label=\"Quick budget selections\"><\/div>\n              <div id=\"budget-histogram\" aria-label=\"Budget histogram\" style=\"display:flex; align-items:flex-end; gap:2px; height:80px;\"><\/div>\n              <div class=\"budget-slider\" style=\"position:relative; padding: 44px 0 8px 0;\">\n                  <div id=\"budget-range\" style=\"position:relative; height:4px; background:#e0e0e0; border-radius:2px; cursor: pointer;\">\n                      <div id=\"budget-track\" style=\"position:absolute; height:4px; left:0; width:100%; background:#0f2a3c; border-radius:2px;\"><\/div>\n                      <div id=\"budget-handle-min\" style=\"position:absolute; top:50%; transform:translate(-50%,-50%); width:18px; height:18px; background:#fff; border:1px solid #0f2a3c; border-radius:50%; cursor:grab;\"><\/div>\n                      <div id=\"budget-handle-max\" style=\"position:absolute; top:50%; transform:translate(-50%,-50%); width:18px; height:18px; background:#fff; border:1px solid #0f2a3c; border-radius:50%; cursor:grab;\"><\/div>\n                  <\/div>\n              <\/div>\n              <div id=\"budget-range-label\" style=\"margin-top:8px; font-weight:600; display:none;\"><\/div>\n          <\/div>\n      <\/details>\n      <\/div>\n    <\/div>\n    <div id=\"map\" style=\"width:100%; margin-top: 3px; margin-bottom: 20px; border: 1px solid rgb(87, 105, 118); border-radius: 4px; background-color: #aad3df !important;\"><\/div>\n    <div class=\"sorting-controls\" style=\"display:none;\">\n      <select id=\"sort-options\">\n        <option value=\"default\">Sort by:<\/option> \n        <option value=\"rating_desc\" selected>Rating<\/option>\n        <option value=\"name_asc\">Name (A-Z)<\/option>\n        <option value=\"name_desc\">Name (Z-A)<\/option>\n        <option value=\"country_asc\">Country (A-Z)<\/option>\n        <option value=\"country_desc\">Country (Z-A)<\/option>\n        <option value=\"programs_desc\">Number of Programs<\/option>\n        <\/select>\n    <\/div>\n    <div id=\"university-programs\"><\/div>\n    <div id=\"pagination\"><\/div>\n<\/div>\n[\/et_pb_fullwidth_code][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; disabled_on=&#8221;on|on|on&#8221; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; background_color=&#8221;#005499&#8243; custom_padding=&#8221;0px||||false|false&#8221; bottom_divider_style=&#8221;slant&#8221; bottom_divider_color=&#8221;#f0f6fb&#8221; bottom_divider_height=&#8221;40px&#8221; bottom_divider_flip=&#8221;horizontal&#8221; bottom_divider_height_tablet=&#8221;30px&#8221; bottom_divider_height_phone=&#8221;20px&#8221; bottom_divider_height_last_edited=&#8221;on|desktop&#8221; disabled=&#8221;on&#8221; global_colors_info=&#8221;{}&#8221;][et_pb_row _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; custom_padding=&#8221;||0px||false|false&#8221; global_colors_info=&#8221;{}&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; global_colors_info=&#8221;{}&#8221;][et_pb_code disabled_on=&#8221;on|on|on&#8221; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; disabled=&#8221;on&#8221; global_colors_info=&#8221;{}&#8221;]<!-- Search-Filter-Map Header mit Suche und Suggestions --><!-- [et_pb_line_break_holder] --><link rel=\"stylesheet\" id=\"search-filter-map-css\" href=\"\/wp-content\/uploads\/tim\/search-filter-map.css\" type=\"text\/css\" media=\"all\"><!-- [et_pb_line_break_holder] --><\/p>\n<div class=\"filter-hero\"><!-- [et_pb_line_break_holder] -->    <\/p>\n<h1 style=\"font-size: 30px; font-weight: bold; line-height: 1.25; color:#fff;\"><!-- [et_pb_line_break_holder] -->        Dein Auslandsstudium. <!\u2013- [et_pb_br_holder] -\u2013><span style=\"color: #b0e6ff;color: #fff;opacity: 0.8;font-size: 25px;\">Finde jetzt die richtige Universit\u00e4t.<\/span><!-- [et_pb_line_break_holder] -->    <\/h1>\n<p><!-- [et_pb_line_break_holder] -->    <\/p>\n<div style=\"position:relative;\" class=\"search-container\"><!-- [et_pb_line_break_holder] -->        <input type=\"text\" id=\"filter-search\" placeholder=\"Wo soll deine Reise hingehen?\" style=\" width: 100%; margin-top: 12px; max-width: 100%; outline: none !important; border-color: #006ab3 !important; box-shadow: 0 0 0 3px rgb(255 255 255 \/ 50%) !important; margin-bottom: 0; font-size: 16px; border: 2px solid #ddd; border-radius: 4px; color: #0f2a3c !important; \"><!-- [et_pb_line_break_holder] -->        <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#999999\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\" position: absolute; right: 15px; top: 23px; pointer-events: none; \"><circle cx=\"11\" cy=\"11\" r=\"8\"><\/circle><path d=\"m21 21-4.3-4.3\"><\/path><\/svg><!-- [et_pb_line_break_holder] -->        <\/p>\n<div class=\"suggestions-dropdown\" id=\"filter-suggestions\">\n<ul><\/ul>\n<\/div>\n<p><!-- [et_pb_line_break_holder] -->    <\/div>\n<p><!-- [et_pb_line_break_holder] --><\/div>\n<p>[\/et_pb_code][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; disabled_on=&#8221;on|on|on&#8221; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; background_color=&#8221;#f0f6fb&#8221; custom_padding=&#8221;0px||0px||false|false&#8221; disabled=&#8221;on&#8221; global_colors_info=&#8221;{}&#8221;][et_pb_row _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; custom_padding=&#8221;0px||||false|false&#8221; global_colors_info=&#8221;{}&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; global_colors_info=&#8221;{}&#8221;][et_pb_code disabled_on=&#8221;on|on|on&#8221; _builder_version=&#8221;4.27.4&#8243; _module_preset=&#8221;default&#8221; disabled=&#8221;on&#8221; global_colors_info=&#8221;{}&#8221;]<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.9.3\/dist\/leaflet.css\" integrity=\"sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI=\" crossorigin=\"\" \/><!-- [et_pb_line_break_holder] --><link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet.markercluster@1.4.1\/dist\/MarkerCluster.css\" \/><!-- [et_pb_line_break_holder] --><link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet.markercluster@1.4.1\/dist\/MarkerCluster.Default.css\" \/><!-- [et_pb_line_break_holder] --><script src=\"https:\/\/unpkg.com\/leaflet@1.9.3\/dist\/leaflet.js\" integrity=\"sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM=\" crossorigin=\"\"><\/script><!-- [et_pb_line_break_holder] --><script src=\"https:\/\/unpkg.com\/leaflet.markercluster@1.4.1\/dist\/leaflet.markercluster.js\"><\/script><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --><\/p>\n<div class=\"filter-headline\" style=\"min-height: 37px; flex-direction:row-reverse; margin-top:-20px;\"><!-- [et_pb_line_break_holder] -->  <pee style=\"min-height: 40px; display: inline-flex;color: #0f2a3c;margin-right: 10px;font-weight: bold;font-style: italic;font-size: 20px;flex-direction: row;flex-wrap: nowrap;align-items: center;gap: 8px; display:none;\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"\"><path d=\"M21.42 10.922a1 1 0 0 0-.019-1.838L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.832l8.57 3.908a2 2 0 0 0 1.66 0z\"><\/path><path d=\"M22 10v6\"><\/path><path d=\"M6 12.5V16a6 3 0 0 0 12 0v-3.5\"><\/path><\/svg> Where to study?<\/pee><!-- [et_pb_line_break_holder] -->  <button id=\"clear-filters\" class=\"filter-button\" aria-label=\"Clear all filters\"><span class=\"clear-icon\">\u2715<\/span> Clear Filters<\/button><!-- [et_pb_line_break_holder] --><\/div>\n<p><!-- [et_pb_line_break_holder] --><\/p>\n<div class=\"search-filter\" style=\"border-radius: 8px; width:100%; background:#f0f6fb; margin-top:0;\"><!-- [et_pb_line_break_holder] -->  <\/p>\n<div class=\"filter-menu\"><!-- [et_pb_line_break_holder] -->  <\/p>\n<details class=\"filter-section\"><!-- [et_pb_line_break_holder] -->      <\/p>\n<summary aria-expanded=\"false\" aria-controls=\"filter-country-buttons\">\n<h3><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"margin-right: 6px;\"><path d=\"M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z\"><\/path><circle cx=\"12\" cy=\"10\" r=\"3\"><\/circle><\/svg> Countries <span class=\"filter-count\" id=\"country-count\"><\/span><span class=\"filter-active-count\" id=\"country-active-count\"><\/span><\/h3>\n<\/summary>\n<p><!-- [et_pb_line_break_holder] -->      <\/p>\n<div class=\"filter-buttons\" id=\"filter-country-buttons\" style=\"width: calc(400% + 60px);\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->  <\/details>\n<p><!-- [et_pb_line_break_holder] -->  <\/p>\n<details class=\"filter-section\"><!-- [et_pb_line_break_holder] -->      <\/p>\n<summary aria-expanded=\"false\" aria-controls=\"filter-degree-buttons\">\n<h3><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"width: 20px; height: 20px; margin-right: 6px; margin-left: -1px;\"><path d=\"M21.42 10.922a1 1 0 0 0-.019-1.838L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.832l8.57 3.908a2 2 0 0 0 1.66 0z\"><\/path><path d=\"M22 10v6\"><\/path><path d=\"M6 12.5V16a6 3 0 0 0 12 0v-3.5\"><\/path><\/svg> Degree Types <span class=\"filter-count\" id=\"degree-count\"><\/span><span class=\"filter-active-count\" id=\"degree-active-count\"><\/span><\/h3>\n<\/summary>\n<p><!-- [et_pb_line_break_holder] -->      <\/p>\n<div class=\"filter-buttons\" id=\"filter-degree-buttons\" style=\"width: calc(400% + 60px); margin-left: calc(-100% - 20px);\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->  <\/details>\n<p><!-- [et_pb_line_break_holder] -->  <\/p>\n<details class=\"filter-section\"><!-- [et_pb_line_break_holder] -->      <\/p>\n<summary aria-expanded=\"false\" aria-controls=\"filter-teaches-buttons\">\n<h3><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"margin-right: 5px;\"><path d=\"m16 6 4 14\"><\/path><path d=\"M12 6v14\"><\/path><path d=\"M8 8v12\"><\/path><path d=\"M4 4v16\"><\/path><\/svg> Study Areas <span class=\"filter-count\" id=\"teaches-count\"><\/span><span class=\"filter-active-count\" id=\"teaches-active-count\"><\/span><\/h3>\n<\/summary>\n<p><!-- [et_pb_line_break_holder] -->      <\/p>\n<div class=\"filter-buttons\" id=\"filter-teaches-buttons\" style=\"width: calc(400% + 60px); margin-left: calc(-200% - 42px);\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->  <\/details>\n<p><!-- [et_pb_line_break_holder] -->  <\/p>\n<details class=\"filter-section\"><!-- [et_pb_line_break_holder] -->      <\/p>\n<summary aria-expanded=\"false\" aria-controls=\"filter-program-buttons\">\n<h3><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"width: 18px; height: 18px; margin-right: 8px; margin-left: -1px;\"><path d=\"M12 7v14\"><\/path><path d=\"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z\"><\/path><\/svg> Programs <span class=\"filter-count\" id=\"program-count\"><\/span><span class=\"filter-active-count\" id=\"program-active-count\"><\/span><\/h3>\n<\/summary>\n<p><!-- [et_pb_line_break_holder] -->      <\/p>\n<div class=\"filter-buttons\" id=\"filter-program-buttons\" style=\"width: calc(400% + 60px); margin-left: calc(-300% - 60px);\"><\/div>\n<p><!-- [et_pb_line_break_holder] -->  <\/details>\n<p><!-- [et_pb_line_break_holder] -->  <\/div>\n<p><!-- [et_pb_line_break_holder] --><\/div>\n<p><!-- [et_pb_line_break_holder] --><\/p>\n<div id=\"search-result-header\"><\/div>\n<p><!-- [et_pb_line_break_holder] --><\/p>\n<div id=\"map\" style=\"width:100%; margin-top: 3px; margin-bottom: 20px; border: 1px solid rgb(87, 105, 118); border-radius: 4px; background-color: #aad3df !important;\"><\/div>\n<p><!-- [et_pb_line_break_holder] --><\/p>\n<div class=\"sorting-controls\"><!-- [et_pb_line_break_holder] -->  <select id=\"sort-options\"><!-- [et_pb_line_break_holder] --><option value=\"default\" selected>Sort by:<\/option><option value=\"rating_desc\">Rating<\/option><!-- [et_pb_line_break_holder] --><option value=\"name_asc\">Name (A-Z)<\/option><!-- [et_pb_line_break_holder] --><option value=\"name_desc\">Name (Z-A)<\/option><!-- [et_pb_line_break_holder] --><option value=\"country_asc\">Country (A-Z)<\/option><!-- [et_pb_line_break_holder] --><option value=\"country_desc\">Country (Z-A)<\/option><!-- [et_pb_line_break_holder] --><option value=\"programs_desc\">Number of Programs<\/option><!-- [et_pb_line_break_holder] -->    <\/select><!-- [et_pb_line_break_holder] --><\/div>\n<p><!-- [et_pb_line_break_holder] --><\/p>\n<div id=\"university-programs\"><\/div>\n<p><!-- [et_pb_line_break_holder] --><\/p>\n<div id=\"pagination\"><\/div>\n<p><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --><script type=\"text\/javascript\" src=\"\/wp-content\/uploads\/tim\/search-filter-map.js\" defer><\/script>[\/et_pb_code][\/et_pb_column][\/et_pb_row][\/et_pb_section]<\/p>\n","protected":false},"excerpt":{"rendered":"<div id=\"search-filter-map-dev\" style=\"padding: 19px 15px 20px;\"\n     data-preset-country=\"\"\n     data-preset-degree=\"\"\n     data-preset-teaches=\"\"\n     data-preset-program=\"\"\n>\n    <div class=\"search-filter-map-dev-head\">\n        <div class=\"filter-hero\">\n            <h1 id=\"page-headline\" style=\"font-size: 30px; font-weight: bold; line-height: 1.25; color:#fff;\">\n                Your studies abroad.                <br><span style=\"color: #b0e6ff;color: #fff;opacity: 0.8;font-size: 25px;\">Find the right university now.<\/span>\n            <\/h1>\n            <div style=\"position:relative;\" class=\"search-container\">\n                <label for=\"filter-search\" class=\"screen-reader-text\">Where should your journey take you?<\/label>\n                <input type=\"text\" id=\"filter-search\" placeholder=\"Where should your journey take you?\" style=\" width: 100%; margin-top: 12px; max-width: 100%; outline: none !important; border-color: #006ab3 !important; box-shadow: 0 0 0 3px rgb(255 255 255 \/ 50%) !important; margin-bottom: 0; font-size: 16px; border: 2px solid #ddd; border-radius: 4px; color: #0f2a3c !important; \">\n                <svg id=\"search-icon\" xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#999999\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\" position: absolute; right: 15px; top: 23px; pointer-events: none; \"><circle cx=\"11\" cy=\"11\" r=\"8\"><\/circle><path d=\"m21 21-4.3-4.3\"><\/path><\/svg>\n                <svg id=\"clear-search-icon\" xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#999999\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"position: absolute; right: 15px; top: 23px; cursor: pointer; display: none;\" role=\"button\" tabindex=\"0\" aria-label=\"Clear search input\"><title>Clear search input<\/title><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"><\/line><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"><\/line><\/svg>\n                <div class=\"suggestions-dropdown\" id=\"filter-suggestions\"><ul><\/ul><\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n<\/div>\n<div class=\"search-filter-map-dev-content\">\n    <div id=\"search-result-header\"><p style=\"min-height: 24px;\">&nbsp;<\/p><\/div>\n    <div class=\"search-filter\" style=\"border-radius: 8px; width:100%; margin-top:0;\">\n      <div class=\"filter-menu\">\n      <details class=\"filter-section\">\n          <summary style=\"padding: 10px 15px; border-radius: 4px; background: #fff; border: 1px solid #0f2a3c; outline: none !important; height: 41px; overflow: hidden;\" aria-expanded=\"false\" aria-controls=\"filter-country-buttons\"><h3><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"margin-right: 6px;\"><path d=\"M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z\"><\/path><circle cx=\"12\" cy=\"10\" r=\"3\"><\/circle><\/svg> Countries <span class=\"filter-count\" id=\"country-count\"><\/span><span class=\"filter-active-count\" id=\"country-active-count\"><\/span><\/h3><\/summary>\n          <div class=\"filter-buttons\" id=\"filter-country-buttons\" style=\"width: calc(500% + 60px);\"><\/div>\n      <\/details>\n      <details class=\"filter-section\">\n          <summary style=\"padding: 10px 15px; border-radius: 4px; background: #fff; border: 1px solid #0f2a3c; outline: none !important; height: 41px; overflow: hidden;\" aria-expanded=\"false\" aria-controls=\"filter-degree-buttons\"><h3><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"width: 20px; height: 20px; margin-right: 6px; margin-left: -1px;\"><path d=\"M21.42 10.922a1 1 0 0 0-.019-1.838L12.83 5.18a2 2 0 0 0-1.66 0L2.6 9.08a1 1 0 0 0 0 1.832l8.57 3.908a2 2 0 0 0 1.66 0z\"><\/path><path d=\"M22 10v6\"><\/path><path d=\"M6 12.5V16a6 3 0 0 0 12 0v-3.5\"><\/path><\/svg> Degree Types <span class=\"filter-count\" id=\"degree-count\"><\/span><span class=\"filter-active-count\" id=\"degree-active-count\"><\/span><\/h3><\/summary>\n          <div class=\"filter-buttons\" id=\"filter-degree-buttons\" style=\"width: calc(500% + 60px); margin-left: calc(-100% - 15px);\"><\/div>\n      <\/details>\n      <details class=\"filter-section\">\n          <summary style=\"padding: 10px 15px; border-radius: 4px; background: #fff; border: 1px solid #0f2a3c; outline: none !important; height: 41px; overflow: hidden;\" aria-expanded=\"false\" aria-controls=\"filter-teaches-buttons\"><h3><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"margin-right: 5px;\"><path d=\"m16 6 4 14\"><\/path><path d=\"M12 6v14\"><\/path><path d=\"M8 8v12\"><\/path><path d=\"M4 4v16\"><\/path><\/svg> <span style=\"\">Study Areas <\/span><span class=\"filter-count\" id=\"teaches-count\"><\/span><span class=\"filter-active-count\" id=\"teaches-active-count\"><\/span><\/h3><\/summary>\n          <div class=\"filter-buttons\" id=\"filter-teaches-buttons\" style=\"width: calc(500% + 60px); margin-left: calc(-200% - 30px);\"><\/div>\n      <\/details>\n      <details class=\"filter-section\">\n          <summary style=\"padding: 10px 15px; border-radius: 4px; background: #fff; border: 1px solid #0f2a3c; outline: none !important; height: 41px; overflow: hidden;\" aria-expanded=\"false\" aria-controls=\"filter-program-buttons\"><h3><svg xmlns=\"https:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"width: 18px; height: 18px; margin-right: 8px; margin-left: -1px;\"><path d=\"M12 7v14\"><\/path><path d=\"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z\"><\/path><\/svg> Programs <span class=\"filter-count\" id=\"program-count\"><\/span><span class=\"filter-active-count\" id=\"program-active-count\"><\/span><\/h3><\/summary>\n          <div class=\"filter-buttons\" id=\"filter-program-buttons\" style=\"width: calc(500% + 60px); margin-left: calc(-300% - 45px);\"><\/div>\n      <\/details>\n      <details class=\"filter-section\">\n          <summary style=\"padding: 10px 15px; border-radius: 4px; background: #fff; border: 1px solid #0f2a3c; outline: none !important; height: 41px; overflow: hidden;\" aria-expanded=\"false\" aria-controls=\"filter-budget-section\"><h3><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-euro-icon lucide-euro\" style=\" width: 18px; height: 18px; margin-right: 5px; margin-left: -1px; margin-bottom: 1px; \"><path d=\"M4 10h12\"><\/path><path d=\"M4 14h9\"><\/path><path d=\"M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12c0 4.4 3.5 8 7.8 8 2 0 3.8-.8 5.2-2\"><\/path><\/svg> Budget <span class=\"filter-active-count\" id=\"budget-active-count\"><\/span><\/h3><\/summary>\n          <div id=\"filter-budget-section\" style=\"padding:0 0 12px 0; width: calc(500% + 60px); margin-left: calc(-400% - 60px);\">\n              <div id=\"budget-quickselect\" class=\"budget-quickselect\" role=\"group\" aria-label=\"Quick budget selections\"><\/div>\n              <div id=\"budget-histogram\" aria-label=\"Budget histogram\" style=\"display:flex; align-items:flex-end; gap:2px; height:80px;\"><\/div>\n              <div class=\"budget-slider\" style=\"position:relative; padding: 44px 0 8px 0;\">\n                  <div id=\"budget-range\" style=\"position:relative; height:4px; background:#e0e0e0; border-radius:2px; cursor: pointer;\">\n                      <div id=\"budget-track\" style=\"position:absolute; height:4px; left:0; width:100%; background:#0f2a3c; border-radius:2px;\"><\/div>\n                      <div id=\"budget-handle-min\" style=\"position:absolute; top:50%; transform:translate(-50%,-50%); width:18px; height:18px; background:#fff; border:1px solid #0f2a3c; border-radius:50%; cursor:grab;\"><\/div>\n                      <div id=\"budget-handle-max\" style=\"position:absolute; top:50%; transform:translate(-50%,-50%); width:18px; height:18px; background:#fff; border:1px solid #0f2a3c; border-radius:50%; cursor:grab;\"><\/div>\n                  <\/div>\n              <\/div>\n              <div id=\"budget-range-label\" style=\"margin-top:8px; font-weight:600; display:none;\"><\/div>\n          <\/div>\n      <\/details>\n      <\/div>\n    <\/div>\n    <div id=\"map\" style=\"width:100%; margin-top: 3px; margin-bottom: 20px; border: 1px solid rgb(87, 105, 118); border-radius: 4px; background-color: #aad3df !important;\"><\/div>\n    <div class=\"sorting-controls\" style=\"display:none;\">\n      <select id=\"sort-options\">\n        <option value=\"default\">Sort by:<\/option> \n        <option value=\"rating_desc\" selected>Rating<\/option>\n        <option value=\"name_asc\">Name (A-Z)<\/option>\n        <option value=\"name_desc\">Name (Z-A)<\/option>\n        <option value=\"country_asc\">Country (A-Z)<\/option>\n        <option value=\"country_desc\">Country (Z-A)<\/option>\n        <option value=\"programs_desc\">Number of Programs<\/option>\n        <\/select>\n    <\/div>\n    <div id=\"university-programs\"><\/div>\n    <div id=\"pagination\"><\/div>\n<\/div>\n\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_et_pb_use_builder":"on","_et_pb_old_content":"","_et_gb_content_width":"","footnotes":""},"class_list":["post-35061","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Test Tim - World of Students<\/title>\n<meta name=\"robots\" content=\"noindex, nofollow\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Test Tim - World of Students\" \/>\n<meta property=\"og:url\" content=\"https:\/\/worldofstudents.org\/en\/test-tim\/\" \/>\n<meta property=\"og:site_name\" content=\"World of Students\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/WOS.org\" \/>\n<meta property=\"article:modified_time\" content=\"2025-12-15T08:04:45+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/worldofstudents.org\/wp-content\/uploads\/2025\/06\/WOS_Logo_blue_bg_druck_5000px.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"1200\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:description\" content=\"[search_filter_map_dev]\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/worldofstudents.org\/en\/test-tim\/\",\"url\":\"https:\/\/worldofstudents.org\/en\/test-tim\/\",\"name\":\"Test Tim - World of Students\",\"isPartOf\":{\"@id\":\"https:\/\/worldofstudents.org\/#website\"},\"datePublished\":\"2021-08-16T11:09:28+00:00\",\"dateModified\":\"2025-12-15T08:04:45+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/worldofstudents.org\/en\/test-tim\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/worldofstudents.org\/en\/test-tim\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/worldofstudents.org\/en\/test-tim\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Startseite\",\"item\":\"https:\/\/worldofstudents.org\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Test Tim\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/worldofstudents.org\/#website\",\"url\":\"https:\/\/worldofstudents.org\/\",\"name\":\"World of Students\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/worldofstudents.org\/#organization\"},\"alternateName\":\"WOS\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/worldofstudents.org\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/worldofstudents.org\/#organization\",\"name\":\"World of Students\",\"url\":\"https:\/\/worldofstudents.org\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/worldofstudents.org\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/worldofstudents.org\/wp-content\/uploads\/2021\/03\/wos_150x150_nobg.png\",\"contentUrl\":\"https:\/\/worldofstudents.org\/wp-content\/uploads\/2021\/03\/wos_150x150_nobg.png\",\"width\":150,\"height\":150,\"caption\":\"World of Students\"},\"image\":{\"@id\":\"https:\/\/worldofstudents.org\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/WOS.org\",\"https:\/\/www.instagram.com\/wos_world_of_students\/\"]}]}<\/script>\n<meta name=\"keywords\" content=\"\" \/>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Test Tim - World of Students","robots":{"index":"noindex","follow":"nofollow"},"og_locale":"en_US","og_type":"article","og_title":"Test Tim - World of Students","og_url":"https:\/\/worldofstudents.org\/en\/test-tim\/","og_site_name":"World of Students","article_publisher":"https:\/\/www.facebook.com\/WOS.org","article_modified_time":"2025-12-15T08:04:45+00:00","og_image":[{"width":1200,"height":1200,"url":"https:\/\/worldofstudents.org\/wp-content\/uploads\/2025\/06\/WOS_Logo_blue_bg_druck_5000px.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_description":"[search_filter_map_dev]","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/worldofstudents.org\/en\/test-tim\/","url":"https:\/\/worldofstudents.org\/en\/test-tim\/","name":"Test Tim - World of Students","isPartOf":{"@id":"https:\/\/worldofstudents.org\/#website"},"datePublished":"2021-08-16T11:09:28+00:00","dateModified":"2025-12-15T08:04:45+00:00","breadcrumb":{"@id":"https:\/\/worldofstudents.org\/en\/test-tim\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/worldofstudents.org\/en\/test-tim\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/worldofstudents.org\/en\/test-tim\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Startseite","item":"https:\/\/worldofstudents.org\/en\/"},{"@type":"ListItem","position":2,"name":"Test Tim"}]},{"@type":"WebSite","@id":"https:\/\/worldofstudents.org\/#website","url":"https:\/\/worldofstudents.org\/","name":"World of Students","description":"","publisher":{"@id":"https:\/\/worldofstudents.org\/#organization"},"alternateName":"WOS","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/worldofstudents.org\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/worldofstudents.org\/#organization","name":"World of Students","url":"https:\/\/worldofstudents.org\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/worldofstudents.org\/#\/schema\/logo\/image\/","url":"https:\/\/worldofstudents.org\/wp-content\/uploads\/2021\/03\/wos_150x150_nobg.png","contentUrl":"https:\/\/worldofstudents.org\/wp-content\/uploads\/2021\/03\/wos_150x150_nobg.png","width":150,"height":150,"caption":"World of Students"},"image":{"@id":"https:\/\/worldofstudents.org\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/WOS.org","https:\/\/www.instagram.com\/wos_world_of_students\/"]}]}},"_links":{"self":[{"href":"https:\/\/worldofstudents.org\/en\/wp-json\/wp\/v2\/pages\/35061","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/worldofstudents.org\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/worldofstudents.org\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/worldofstudents.org\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/worldofstudents.org\/en\/wp-json\/wp\/v2\/comments?post=35061"}],"version-history":[{"count":38,"href":"https:\/\/worldofstudents.org\/en\/wp-json\/wp\/v2\/pages\/35061\/revisions"}],"predecessor-version":[{"id":41782,"href":"https:\/\/worldofstudents.org\/en\/wp-json\/wp\/v2\/pages\/35061\/revisions\/41782"}],"wp:attachment":[{"href":"https:\/\/worldofstudents.org\/en\/wp-json\/wp\/v2\/media?parent=35061"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}