(() => { const $ = (sel, root = document) => root.querySelector(sel); const $$ = (sel, root = document) => Array.from(root.querySelectorAll(sel)); const toast = $("#miniToast"); const showToast = (message) => { if (!toast) return; toast.textContent = message; toast.classList.add("show"); window.clearTimeout(showToast.t); showToast.t = window.setTimeout(() => toast.classList.remove("show"), 2400); }; const bottomNav = $("[data-bottom-nav]"); const syncNav = () => bottomNav?.classList.toggle("visible", window.scrollY > 8); window.addEventListener("scroll", syncNav, { passive: true }); syncNav(); $$("[data-section-jump], .bottom-icons a, .top-links a, .scroll-cue, .chapter-link").forEach((link) => { link.addEventListener("click", () => { const label = link.getAttribute("data-title") || link.textContent.trim() || "section"; showToast(`Navigating to ${label}`); }); }); const themeToggle = $("[data-theme-toggle]"); const storedTheme = localStorage.getItem("landscaper-theme"); if (storedTheme === "night") { document.documentElement.dataset.theme = "night"; if (themeToggle) { themeToggle.textContent = "Night"; themeToggle.setAttribute("aria-pressed", "true"); } } themeToggle?.addEventListener("click", () => { const next = document.documentElement.dataset.theme === "night" ? "day" : "night"; if (next === "night") document.documentElement.dataset.theme = "night"; else delete document.documentElement.dataset.theme; themeToggle.textContent = next === "night" ? "Night" : "Day"; themeToggle.setAttribute("aria-pressed", String(next === "night")); localStorage.setItem("landscaper-theme", next === "night" ? "night" : "day"); showToast(`${next === "night" ? "Night" : "Day"} mode enabled`); }); const grid = $("[data-section-grid]"); $$(".mode-switch button").forEach((button) => { button.addEventListener("click", () => { $$(".mode-switch button").forEach((b) => b.classList.remove("active")); button.classList.add("active"); grid?.classList.remove("blocks", "files", "text"); grid?.classList.add(button.dataset.mode); showToast(`${button.dataset.mode} mode`); }); }); const pdfLibrary = $("[data-pdf-library]"); $$("[data-pdf-view]").forEach((button) => { button.addEventListener("click", () => { $$("[data-pdf-view]").forEach((b) => b.classList.remove("active")); button.classList.add("active"); if (pdfLibrary) pdfLibrary.dataset.pdfMode = button.dataset.pdfView || "intro"; showToast(`PDF view: ${button.textContent.trim()}`); }); }); const referencePage = $("[data-reference-page]"); const referenceModeButtons = $$("button[data-reference-mode]"); const referencePanels = $$("[data-mode-panel]"); const filter = $("#linkFilter"); const filterDimToggle = $("#filterDimToggle"); const filterStatus = $("#filterStatus"); const filterModeLabel = $("[data-filter-mode-label]"); try { const savedFilterMode = localStorage.getItem("curated-filter-mode"); if (filterDimToggle && savedFilterMode === "hide") filterDimToggle.checked = false; } catch {} function setReferenceMode(mode, options = {}) { if (!referencePage || !mode) return; referencePage.dataset.referenceMode = mode; referenceModeButtons.forEach((button) => { const active = button.dataset.referenceMode === mode; button.classList.toggle("active", active); button.setAttribute("aria-selected", String(active)); }); referencePanels.forEach((panel) => { const active = panel.dataset.modePanel === mode; panel.hidden = !active; panel.classList.toggle("is-active", active); }); if (!options.quiet) showToast(mode === "pdfs" ? "PDF block ready" : (mode === "chapters" ? "Chapter groups ready" : "Icon boxes ready")); scheduleVisibleHighlights(); } function panelForMode(mode) { return referencePanels.find((panel) => panel.dataset.modePanel === mode) || null; } referenceModeButtons.forEach((button) => { button.addEventListener("click", () => { const mode = button.dataset.referenceMode; setReferenceMode(mode, { quiet: true }); const panel = panelForMode(mode); if (panel?.id) window.setTimeout(() => markTarget(panel.id, { scroll: false, switchMode: false }), 30); }); }); $$("[data-reference-mode-jump]").forEach((link) => { link.addEventListener("click", () => setReferenceMode(link.dataset.referenceModeJump, { quiet: true })); }); document.addEventListener("click", (event) => { const control = event.target.closest("[data-highlight-target]"); if (!control) return; const targetId = control.dataset.highlightTarget; if (!targetId) return; const href = control.getAttribute("href") || ""; const external = control.matches("a[href]") && href && !href.startsWith("#"); if (!external) event.preventDefault(); markTarget(targetId, { scroll: !external, switchMode: true }); }); document.addEventListener("click", (event) => { const link = event.target.closest("a[href^=\"#\"]"); if (!link || link.matches("[data-highlight-target]") || link.matches("[data-map-target]")) return; const raw = link.getAttribute("href") || ""; if (raw.length <= 1) return; const targetId = decodeURIComponent(raw.slice(1)); if (!document.getElementById(targetId)) return; event.preventDefault(); if (link.dataset.referenceModeJump) setReferenceMode(link.dataset.referenceModeJump, { quiet: true }); window.setTimeout(() => markTarget(targetId, { scroll: true, switchMode: true }), 30); }); function previewOwner(control, persist = false) { const targetId = control?.dataset?.ownerTarget; if (!targetId) return; $$(".is-owner-preview").forEach((el) => el.classList.remove("is-owner-preview")); $$(".is-owner-link").forEach((el) => el.classList.remove("is-owner-link")); control.classList.add("is-owner-link"); markRelatedControls(targetId, true); const target = document.getElementById(targetId); const hiddenPanel = target?.closest("[data-mode-panel]")?.hidden; if (target && !hiddenPanel) target.classList.add("is-owner-preview"); if (persist && target && !hiddenPanel) markTarget(targetId, { scroll: false, switchMode: false, toast: false }); } document.addEventListener("mouseover", (event) => { const control = event.target.closest("[data-owner-target]"); if (control) previewOwner(control, false); }); document.addEventListener("focusin", (event) => { const control = event.target.closest("[data-owner-target]"); if (control) previewOwner(control, false); }); document.addEventListener("click", (event) => { const control = event.target.closest("[data-owner-target]"); if (control) previewOwner(control, true); }); const applyFilter = () => { const anchorState = captureScrollAnchor(); const q = filter?.value.trim().toLowerCase() || ""; const dimOut = filterDimToggle ? filterDimToggle.checked : true; let total = 0; let hits = 0; const setFiltered = (el, hit) => { const miss = Boolean(q && !hit); el.classList.toggle("is-filter-muted", miss && dimOut); el.classList.toggle("is-hidden", miss && !dimOut); el.classList.toggle("is-filter-hit", Boolean(q && hit)); }; $$("[data-pdf-card]").forEach((card) => { total += 1; const hit = !q || (card.dataset.search || "").includes(q); if (hit) hits += 1; setFiltered(card, hit); }); $$("[data-section-card]").forEach((card) => { const sectionMatch = !q || (card.dataset.title || "").includes(q) || (card.dataset.chapter || "").toLowerCase().includes(q); let any = sectionMatch; $$("[data-link-row]", card).forEach((row) => { total += 1; const hit = !q || (row.dataset.search || "").includes(q) || sectionMatch; if (hit) hits += 1; if (hit) any = true; setFiltered(row, hit); }); setFiltered(card, any); }); if (filterModeLabel) filterModeLabel.textContent = dimOut ? "dim" : "hide"; try { localStorage.setItem("curated-filter-mode", dimOut ? "dim" : "hide"); } catch {} if (filterStatus) filterStatus.textContent = q ? `${hits}/${total} · ${dimOut ? "dim" : "hide"}` : `All · ${dimOut ? "dim" : "hide"}`; restoreScrollAnchor(anchorState); scheduleVisibleHighlights(); }; filter?.addEventListener("input", applyFilter); filterDimToggle?.addEventListener("change", applyFilter); applyFilter(); const mapBtn = $("[data-open-map]"); mapBtn?.addEventListener("click", () => { const modalElement = $("#mapModal"); if (!modalElement || !window.bootstrap) return; const modal = new bootstrap.Modal(modalElement); modal.show(); }); $$("[data-map-target]").forEach((link) => { link.addEventListener("click", (event) => { event.preventDefault(); const targetId = link.getAttribute("data-map-target"); const modalEl = $("#mapModal"); if (modalEl && window.bootstrap) bootstrap.Modal.getInstance(modalEl)?.hide(); window.setTimeout(() => openSection(targetId), 170); }); }); const scanBtn = $("#scanLinks"); const auditStatus = $("#auditStatus"); const auditResults = $("#auditResults"); scanBtn?.addEventListener("click", async () => { scanBtn.disabled = true; auditStatus.textContent = "Scanning live source…"; auditResults.innerHTML = ""; try { const response = await fetch("api/scan_links.php", { headers: { "Accept": "application/json" }}); if (!response.ok) throw new Error("Scanner returned " + response.status); const payload = await response.json(); const known = new Set(JSON.parse(auditResults.dataset.known || "[]").map((u) => normalize(u))); const rows = (payload.links || []).map((link) => ({ ...link, known: known.has(normalize(link.href)) })); const newCount = rows.filter((row) => !row.known).length; const ignored = Number(payload.ignored_count || 0); const suppressed = Number(payload.suppressed_count || 0); auditStatus.textContent = `${rows.length} visible source links found · ${newCount} not curated · ${ignored} noise/asset links ignored · ${suppressed} deliberately removed · scanned ${payload.scanned_at || "now"}.`; auditResults.innerHTML = rows.map(renderAuditRow).join(""); showToast("Live scan complete"); } catch (error) { auditStatus.textContent = "Scan failed. The curated structure remains available."; showToast("Scanner unavailable"); } finally { scanBtn.disabled = false; } }); const structureButtons = [$("#loadStructure"), $("#loadStructureBottom")].filter(Boolean); const structureStatus = $("#sourceStructureStatus"); const structureTarget = $("#sourceStructure"); structureButtons.forEach((button) => { button.addEventListener("click", () => loadSourceStructure()); }); async function loadSourceStructure() { if (!structureTarget || !structureStatus) return; structureButtons.forEach((button) => { button.disabled = true; }); structureStatus.textContent = "Reading live source icon ownership…"; structureTarget.innerHTML = ""; try { const response = await fetch("api/scan_structure.php", { headers: { "Accept": "application/json" }}); if (!response.ok) throw new Error("Structure scanner returned " + response.status); const payload = await response.json(); if (!payload.success) throw new Error(payload.error || "Structure scanner failed"); structureStatus.textContent = `${payload.section_count} source owner icons · ${payload.link_count} grouped branch links · ${Number(payload.utility_count || 0)} separated utility links · scanned ${payload.scanned_at}.`; structureTarget.innerHTML = `
Bonus shadow icon ${(payload.shadow_bonus || []).map((item) => `${escapeHtml(item.icon)} ${escapeHtml(item.title)}`).join("")}
${renderUtilityChrome(payload.utility_chrome || [])} ${(payload.sections || []).map(renderSourceSection).join("")} `; showToast("Source ownership loaded"); } catch (error) { structureStatus.textContent = "Could not load live structure. The curated snapshot above remains available."; showToast("Source map unavailable"); scheduleVisibleHighlights(); } finally { structureButtons.forEach((button) => { button.disabled = false; }); } } function renderAuditRow(row) { const badgeText = row.known ? "curated" : (row.safety === "review" ? "review external" : (row.category || "new")); const badgeClass = row.known ? "audit-badge-known" : (row.safety === "review" ? "audit-badge-review" : (row.safety === "source" ? "audit-badge-source" : "audit-badge-new")); const category = String(row.category || "external").replace(/[^a-z0-9-]/gi, "-").toLowerCase(); return ` `; } function renderUtilityChrome(items) { if (!items.length) return ""; return `
Separated utility chrome ${items.slice(0, 18).map((item) => `${escapeHtml(item.text || item.href)}`).join("")}
`; } function renderSourceSection(section) { return `
${escapeHtml(section.icon)}

${escapeHtml(section.title)}

${escapeHtml(section.source_anchor)} owns ${Number(section.link_count || 0)} links until ${escapeHtml(section.owns_until || "next")}

${escapeHtml(section.excerpt || "")}

${section.layout ? `
${escapeHtml(section.layout.zone || "layout")}${escapeHtml(section.layout.computed || "")}${escapeHtml(section.layout.parent || "")} · ${escapeHtml(section.layout.position || "")}${escapeHtml(section.layout.note || "")}
` : ""}
`; } const highlightableTargetSelector = [ "[data-section-card][id]", ".section-card[id]", ".chapter-card[id]", ".chapter-reference-card[id]", ".layout-zone-card[id]", ".layout-audit[id]", ".ownership-strip[id]", ".reference-tools[id]", ".reference-hero[id]", ".pdf-library[id]", ".reference-mode-panel[id]", ".utility-chrome-panel[id]", ".source-owner-card[id]", ".audit-panel[id]", ".document-shell[id]", ".console-screen[id]" ].join(","); const boxClickTargetSelector = [ "[data-section-card][id]", ".section-card[id]", ".chapter-card[id]", ".chapter-reference-card[id]", ".layout-zone-card[id]", ".layout-audit[id]", ".ownership-strip[id]", ".reference-tools[id]", ".reference-hero[id]", ".pdf-library[id]", ".reference-mode-panel[id]", ".utility-chrome-panel[id]", ".source-owner-card[id]", ".audit-panel[id]" ].join(","); const targetControlSelector = "[data-highlight-target],[data-owner-target],[data-map-target],a[href^='#']"; const visibleTargetSelector = highlightableTargetSelector; const interactiveSelector = "a,button,input,select,textarea,label,summary,[role='button'],[tabindex]:not([tabindex='-1'])"; function controlTargetId(control) { if (!control) return ""; const direct = control.dataset?.highlightTarget || control.dataset?.ownerTarget || control.dataset?.mapTarget || ""; if (direct) return direct; const href = control.getAttribute?.("href") || ""; if (!href.startsWith("#") || href.length <= 1) return ""; try { return decodeURIComponent(href.slice(1)); } catch { return href.slice(1); } } function isElementHidden(el) { if (!el || !el.isConnected || el.hidden || el.closest("[hidden]")) return true; const style = window.getComputedStyle(el); return style.display === "none" || style.visibility === "hidden" || Number(style.opacity || 1) === 0; } function visiblePixels(el) { const rect = el.getBoundingClientRect(); const width = Math.max(0, Math.min(rect.right, window.innerWidth) - Math.max(rect.left, 0)); const height = Math.max(0, Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0)); return { width, height, area: width * height }; } function isMeaningfullyVisible(el) { if (!el?.id || isElementHidden(el)) return false; const visible = visiblePixels(el); return visible.width >= 20 && visible.height >= 20 && visible.area >= 400; } function visibleTargetIds() { const ids = new Set(); $$(visibleTargetSelector).forEach((target) => { if (isMeaningfullyVisible(target)) ids.add(target.id); }); return ids; } function syncVisibleHighlights() { const visibleIds = visibleTargetIds(); $$(".is-in-view-target").forEach((el) => el.classList.remove("is-in-view-target")); $$(".is-visible-owner-link").forEach((el) => el.classList.remove("is-visible-owner-link")); visibleIds.forEach((id) => { document.getElementById(id)?.classList.add("is-in-view-target"); }); $$(targetControlSelector).forEach((control) => { const targetId = controlTargetId(control); control.classList.toggle("is-visible-owner-link", Boolean(targetId && visibleIds.has(targetId))); }); } function scheduleVisibleHighlights() { window.cancelAnimationFrame(scheduleVisibleHighlights.raf || 0); scheduleVisibleHighlights.raf = window.requestAnimationFrame(syncVisibleHighlights); } window.addEventListener("scroll", scheduleVisibleHighlights, { passive: true }); window.addEventListener("resize", scheduleVisibleHighlights, { passive: true }); window.addEventListener("hashchange", scheduleVisibleHighlights); document.addEventListener("click", (event) => { const box = event.target.closest?.(boxClickTargetSelector); if (!box?.id) return; const interactive = event.target.closest(interactiveSelector); if (interactive && box.contains(interactive)) { const explicitTarget = controlTargetId(interactive); if (explicitTarget || !interactive.matches("a[href]")) return; } markTarget(box.id, { scroll: false, switchMode: false, toast: !interactive }); }); function openSection(id) { markTarget(id, { scroll: true, switchMode: true }); } function clearTargetHighlights() { const activeSelector = [ ".section-card.is-active", ".chapter-reference-card.is-active", ".pdf-card.is-active", ".layout-zone-card.is-active", ".layout-audit.is-active", ".ownership-strip.is-active", ".reference-tools.is-active", ".utility-chrome-panel.is-active", ".source-owner-card.is-active", ".audit-panel.is-active", ".reference-hero.is-active", ".chapter-card.is-active", ".document-shell.is-active", ".console-screen.is-active", ".pdf-library.is-active" ].join(","); $$(activeSelector).forEach((el) => el.classList.remove("is-active")); $$(".is-focus-target,.is-owner-preview,.is-owner-link").forEach((el) => el.classList.remove("is-focus-target", "is-owner-preview", "is-owner-link")); $$("[aria-current=\"location\"]").forEach((el) => el.removeAttribute("aria-current")); } function markRelatedControls(targetId, lightweight = false) { $$(targetControlSelector).forEach((el) => { const match = controlTargetId(el) === targetId; el.classList.toggle("is-owner-link", match); if (match && !lightweight) el.setAttribute("aria-current", "location"); }); } function markTarget(id, options = {}) { const target = id ? document.getElementById(id) : null; if (!target) return; const modePanel = target.closest("[data-mode-panel]"); if (modePanel && modePanel.hidden && options.switchMode !== false) setReferenceMode(modePanel.dataset.modePanel || "icons", { quiet: true }); clearTargetHighlights(); target.classList.add("is-focus-target"); if (!target.classList.contains("reference-mode-panel")) target.classList.add("is-active"); markRelatedControls(id, false); if (options.scroll !== false) { target.scrollIntoView({ behavior: "smooth", block: "start" }); if (history.replaceState) history.replaceState(null, "", `#${id}`); } window.clearTimeout(markTarget.pulseTimer); markTarget.pulseTimer = window.setTimeout(() => target.classList.remove("is-focus-target"), 4200); scheduleVisibleHighlights(); window.setTimeout(scheduleVisibleHighlights, options.scroll !== false ? 520 : 40); if (options.toast !== false) showToast(`Pinned ${target.querySelector("h1,h2,h3")?.textContent?.trim() || id}`); } function captureScrollAnchor() { const y = Math.min(Math.max(120, window.innerHeight * 0.25), window.innerHeight - 1); const el = document.elementFromPoint(Math.min(90, window.innerWidth - 1), y); return el ? { el, top: el.getBoundingClientRect().top } : null; } function restoreScrollAnchor(state) { if (!state || !state.el || !state.el.isConnected) return; window.requestAnimationFrame(() => { const diff = state.el.getBoundingClientRect().top - state.top; if (Math.abs(diff) > 1) window.scrollBy(0, diff); }); } scheduleVisibleHighlights(); if (window.location.hash && document.getElementById(window.location.hash.slice(1))) { window.setTimeout(() => openSection(window.location.hash.slice(1)), 250); } function normalize(url) { try { return new URL(url, "https://spireason.neocities.org/").href.replace(/#$/, ""); } catch { return String(url || "").trim(); } } function escapeHtml(value) { return String(value || "").replace(/[&<>"\x27]/g, (char) => { if (char === "&") return "&"; if (char === "<") return "<"; if (char === ">") return ">"; if (char.charCodeAt(0) === 34) return """; return "'"; }); } function escapeAttr(value) { return escapeHtml(value); } })();