Loading map…
). image / link / linkText are optional: leave “” to hide them. ============================================================ */ var WM_DATA = { “United Kingdom”: { text: “Head office in London, opened 2016. Our largest team, covering client work across Europe.”, image: “”, link: “”, linkText: “Read more” }, “France”: { text: “Partner studio in Lyon handling French-language projects.”, image: “”, link: “”, linkText: “” }, “United States of America”: { text: “Two bases — New York and Austin — serving clients across North America.”, image: “”, link: “”, linkText: “” }, “Brazil”: { text: “Regional hub in São Paulo since 2021.”, image: “”, link: “”, linkText: “” }, “Kenya”: { text: “Field programmes run from Nairobi with local partners.”, image: “”, link: “”, linkText: “” }, “India”: { text: “Engineering and support team in Bengaluru.”, image: “”, link: “”, linkText: “” }, “Japan”: { text: “Tokyo office, focused on manufacturing clients.”, image: “”, link: “”, linkText: “” }, “Australia”: { text: “Melbourne studio covering Australia and New Zealand.”, image: “”, link: “”, linkText: “” } }; /* Shown in the side panel before anything is clicked. */ var WM_INTRO = { title: “Where we work”, text: “Select any highlighted country on the map to see what we do there.” }; /* ========================= END OF EDITABLE CONTENT ========================= */ /* Common names → the names used in the map file. Add your own if needed. */ var ALIASES = { “usa”: “United States of America”, “us”: “United States of America”, “united states”: “United States of America”, “america”: “United States of America”, “uk”: “United Kingdom”, “great britain”: “United Kingdom”, “britain”: “United Kingdom”, “england”: “United Kingdom”, “scotland”: “United Kingdom”, “wales”: “United Kingdom”, “uae”: “United Arab Emirates”, “south korea”: “South Korea”, “korea”: “South Korea”, “russia”: “Russia”, “czech republic”: “Czechia”, “holland”: “Netherlands”, “ivory coast”: “Côte d’Ivoire”, “drc”: “Dem. Rep. Congo”, “democratic republic of the congo”: “Dem. Rep. Congo”, “bosnia”: “Bosnia and Herz.”, “dominican republic”: “Dominican Rep.”, “central african republic”: “Central African Rep.”, “south sudan”: “S. Sudan”, “north macedonia”: “Macedonia”, “myanmar”: “Myanmar”, “burma”: “Myanmar”, “eswatini”: “Swaziland”, “cape verde”: “Cabo Verde” }; var root = document.getElementById(“wm-root”); if (!root) return; var svgEl = root.querySelector(“.wm-svg”); var panel = root.querySelector(“.wm-panel”); var chips = root.querySelector(“.wm-chips”); var tip = root.querySelector(“.wm-tip”); var loading = root.querySelector(“.wm-loading”); var W = 900, H = 470; var current = null; function norm(s) { return String(s).toLowerCase() .normalize(“NFD”).replace(/[\u0300-\u036f]/g, “”) .replace(/[^a-z]/g, “”); } /* Build a lookup that tolerates spelling and alias differences. */ var LOOKUP = {}; Object.keys(WM_DATA).forEach(function (key) { var canonical = ALIASES[key.toLowerCase().trim()] || key; var entry = WM_DATA[key]; entry._label = key; LOOKUP[norm(canonical)] = entry; LOOKUP[norm(key)] = entry; }); function loadScript(src) { return new Promise(function (resolve, reject) { var s = document.createElement(“script”); s.src = src; s.onload = resolve; s.onerror = reject; document.head.appendChild(s); }); } function ready() { return (window.d3 ? Promise.resolve() : loadScript(“https://cdn.jsdelivr.net/npm/d3@7”)) .then(function () { return window.topojson ? null : loadScript(“https://cdn.jsdelivr.net/npm/topojson-client@3”); }); } function showIntro() { current = null; panel.innerHTML = ““; panel.querySelector(“h3”).textContent = WM_INTRO.title; panel.querySelector(“p”).innerHTML = WM_INTRO.text; chips.querySelectorAll(“button”).forEach(function (b) { b.setAttribute(“aria-pressed”, “false”); }); } function select(name, entry) { current = name; var html = “”; if (entry.image) html += ‘
” + (entry._label || name) + “
“; if (entry.text) html += “” + entry.text + “
“; if (entry.link) html += ‘‘ + (entry.linkText || “Read more”) + ““; panel.innerHTML = html; d3.selectAll(“#wm-root .wm-country”).classed(“is-selected”, function (d) { return d && d.properties && norm(d.properties.name) === norm(name); }); chips.querySelectorAll(“button”).forEach(function (b) { b.setAttribute(“aria-pressed”, String(b.dataset.name === name)); }); } ready().then(function () { return fetch(“https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json”).then(function (r) { if (!r.ok) throw new Error(“map data”); return r.json(); }); }).then(function (world) { loading.remove(); var features = topojson.feature(world, world.objects.countries).features .filter(function (f) { return f.properties.name !== “Antarctica”; }); var svg = d3.select(svgEl) .attr(“viewBox”, “0 0 ” + W + ” ” + H) .attr(“preserveAspectRatio”, “xMidYMid meet”); var projection = d3.geoNaturalEarth1().fitExtent([[6, 6], [W – 6, H – 6]], { type: “Sphere” }); var path = d3.geoPath(projection); var g = svg.append(“g”); g.append(“path”).attr(“class”, “wm-sphere”).attr(“d”, path({ type: “Sphere” })); g.append(“path”).attr(“class”, “wm-grid”).attr(“d”, path(d3.geoGraticule10())); var matched = {}; g.append(“g”).selectAll(“path”).data(features).join(“path”) .attr(“d”, path) .attr(“class”, function (d) { var e = LOOKUP[norm(d.properties.name)]; if (e) { matched[norm(d.properties.name)] = true; return “wm-country is-active”; } return “wm-country”; }) .attr(“tabindex”, function (d) { return LOOKUP[norm(d.properties.name)] ? 0 : null; }) .attr(“role”, function (d) { return LOOKUP[norm(d.properties.name)] ? “button” : null; }) .attr(“aria-label”, function (d) { return LOOKUP[norm(d.properties.name)] ? “Show details for ” + d.properties.name : null; }) .on(“click”, function (event, d) { var e = LOOKUP[norm(d.properties.name)]; if (e) select(d.properties.name, e); }) .on(“keydown”, function (event, d) { if (event.key !== “Enter” && event.key !== ” “) return; event.preventDefault(); var e = LOOKUP[norm(d.properties.name)]; if (e) select(d.properties.name, e); }) .on(“pointerenter”, function (event, d) { if (event.pointerType === “touch” || !LOOKUP[norm(d.properties.name)]) return; tip.textContent = d.properties.name; tip.classList.add(“is-on”); }) .on(“pointermove”, function (event) { var b = svgEl.getBoundingClientRect(); tip.style.left = (event.clientX – b.left) + “px”; tip.style.top = (event.clientY – b.top) + “px”; }) .on(“pointerleave”, function () { tip.classList.remove(“is-on”); }); /* Country buttons under the map — useful on phones and for keyboard users. */ Object.keys(WM_DATA).sort().forEach(function (key) { var canonical = ALIASES[key.toLowerCase().trim()] || key; var b = document.createElement(“button”); b.type = “button”; b.textContent = key; b.dataset.name = canonical; b.setAttribute(“aria-pressed”, “false”); b.addEventListener(“click”, function () { select(canonical, WM_DATA[key]); }); chips.appendChild(b); }); var zoom = d3.zoom().scaleExtent([1, 8]) .translateExtent([[0, 0], [W, H]]) .on(“zoom”, function (event) { g.attr(“transform”, event.transform); }); svg.call(zoom); root.querySelector(“.wm-zin”).addEventListener(“click”, function () { svg.transition().duration(250).call(zoom.scaleBy, 1.6); }); root.querySelector(“.wm-zout”).addEventListener(“click”, function () { svg.transition().duration(250).call(zoom.scaleBy, 0.625); }); root.querySelector(“.wm-zreset”).addEventListener(“click”, function () { svg.transition().duration(300).call(zoom.transform, d3.zoomIdentity); showIntro(); }); showIntro(); var missing = Object.keys(WM_DATA).filter(function (k) { var c = ALIASES[k.toLowerCase().trim()] || k; return !matched[norm(c)]; }); if (missing.length) console.warn(“World map: no country found on the map for — ” + missing.join(“, “)); }).catch(function () { if (loading) loading.textContent = “The map couldn’t load. Check the page’s internet connection and try again.”; }); })();