diff --git a/assets/js/popover.js b/assets/js/popover.js
index ee0477e..460c245 100644
--- a/assets/js/popover.js
+++ b/assets/js/popover.js
@@ -1,5 +1,5 @@
 function htmlToElement(html) {
-  const template = document.createElement('template')
+  const template = document.createElement("template")
   html = html.trim()
   template.innerHTML = html
   return template.content.firstChild
@@ -7,29 +7,27 @@ function htmlToElement(html) {
 
 function initPopover(baseURL) {
   const basePath = baseURL.replace(window.location.origin, "")
-  document.addEventListener("DOMContentLoaded", () => {
-    fetchData.then(({ content }) => {
-      const links = [...document.getElementsByClassName("internal-link")]
-      links
-        .filter(li => li.dataset.src)
-        .forEach(li => {
-          const linkDest = content[li.dataset.src.replace(/\/$/g, "").replace(basePath, "")]
-          if (linkDest) {
-            const popoverElement = `
+  fetchData.then(({ content }) => {
+    const links = [...document.getElementsByClassName("internal-link")]
+    links
+      .filter((li) => li.dataset.src)
+      .forEach((li) => {
+        const linkDest = content[li.dataset.src.replace(/\/$/g, "").replace(basePath, "")]
+        if (linkDest) {
+          const popoverElement = `
     ${linkDest.title}
     ${removeMarkdown(linkDest.content).split(" ", 20).join(" ")}...
     ${new Date(linkDest.lastmodified).toLocaleDateString()}
  `
-            const el = htmlToElement(popoverElement)
-            li.appendChild(el)
-            li.addEventListener("mouseover", () => {
-              el.classList.add("visible")
-            })
-            li.addEventListener("mouseout", () => {
-              el.classList.remove("visible")
-            })
-          }
-        })
-    })
+          const el = htmlToElement(popoverElement)
+          li.appendChild(el)
+          li.addEventListener("mouseover", () => {
+            el.classList.add("visible")
+          })
+          li.addEventListener("mouseout", () => {
+            el.classList.remove("visible")
+          })
+        }
+      })
   })
 }
diff --git a/assets/js/router.js b/assets/js/router.js
new file mode 100644
index 0000000..5c874ee
--- /dev/null
+++ b/assets/js/router.js
@@ -0,0 +1,25 @@
+import { router, navigate } from "https://unpkg.com/million/dist/router.mjs"
+
+export const init = (loader) => {
+  // SPA navigation for access later
+  window.navigate = navigate
+  // We only mutate document.title and content within .singlePage element
+  router(".singlePage")
+  // We need on initial load, then subsequent redirs
+  window.addEventListener("million:navigate", () => callback(loader))
+  window.addEventListener("DOMContentLoaded", () => callback(loader))
+}
+
+export const callback = (loader) => {
+  // requestAnimationFrame() delays graph draw until SPA routing is finished
+  const draw = () => {
+    const container = document.getElementById("graph-container")
+    // retry if the graph is not ready
+    if (!container) return requestAnimationFrame(draw)
+    // clear the graph in case there is anything within it
+    container.textContent = ""
+
+    loader()
+  }
+  requestAnimationFrame(draw)
+}
diff --git a/layouts/_default/section.html b/layouts/_default/section.html
index 1a4aae0..abdf0b0 100644
--- a/layouts/_default/section.html
+++ b/layouts/_default/section.html
@@ -19,7 +19,6 @@
     
     {{partial "contact.html" .}}
 
 
-{{partial "popover.html" .}}