// ============================================================================ // Project : modelmap // File : site/public/app.js // Purpose : Client-side interactivity — mobile menu, chart tooltips // Author : Simon-Pierre Boucher // Contact : contact@spboucher.ai // Website : https://modelmap.io // Created : 2026-08-12 // Modified : 2026-08-12 // Platform : Web (deployed from macOS / Apple Silicon) // License : All rights reserved (research code) // ============================================================================ "use strict"; // ---------------------------------------------------------- mobile nav (function () { const btn = document.getElementById("nav-toggle"); const nav = document.getElementById("site-nav"); if (!btn || !nav) return; btn.addEventListener("click", () => { const open = nav.classList.toggle("open"); btn.setAttribute("aria-expanded", open ? "true" : "false"); }); // close when a link is chosen or when tapping outside nav.addEventListener("click", (e) => { if (e.target.tagName === "A") { nav.classList.remove("open"); btn.setAttribute("aria-expanded", "false"); } }); document.addEventListener("click", (e) => { if (!nav.classList.contains("open")) return; if (!nav.contains(e.target) && !btn.contains(e.target)) { nav.classList.remove("open"); btn.setAttribute("aria-expanded", "false"); } }); })(); // ---------------------------------------------------------- chart tooltips (function () { document.querySelectorAll(".chart-fig").forEach((fig) => { const tip = fig.querySelector(".chart-tip"); if (!tip) return; fig.querySelectorAll(".pt").forEach((pt) => { pt.addEventListener("mouseenter", () => { tip.textContent = pt.getAttribute("data-tip"); tip.hidden = false; }); pt.addEventListener("mousemove", (ev) => { const r = fig.getBoundingClientRect(); tip.style.left = Math.min(ev.clientX - r.left + 14, r.width - tip.offsetWidth - 4) + "px"; tip.style.top = ev.clientY - r.top - 34 + "px"; }); pt.addEventListener("mouseleave", () => { tip.hidden = true; }); }); }); })();