1a12fb870b
- New /developers/ page with API docs, SDKs, pricing, use cases - OpenAPI 3.0 spec for Orders, Missions, Photos, Analytics - Case study: Glasskiosken i Flatenbadet — complete ROI analysis - Updated /order/ with recurring missions and frequency dropdown
740 lines
23 KiB
HTML
740 lines
23 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Glossary — Landvex Ecosystem</title>
|
|
<meta name="description" content="The complete glossary for quiXzoom, AMOS, and Landvex. Every term defined precisely — from Zoomer to AMOS Prediction Engine.">
|
|
<link rel="canonical" href="https://www.quixzoom.com/glossary">
|
|
<meta name="robots" content="index, follow">
|
|
|
|
<!-- Open Graph -->
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:title" content="Glossary — Landvex Ecosystem">
|
|
<meta property="og:description" content="Every term used across quiXzoom, AMOS, and Landvex. Defined precisely and kept current.">
|
|
<meta property="og:url" content="https://www.quixzoom.com/glossary">
|
|
<meta property="og:image" content="https://www.quixzoom.com/images/og-glossary.jpg">
|
|
<meta property="og:image:width" content="1200">
|
|
<meta property="og:image:height" content="630">
|
|
|
|
<!-- Twitter -->
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:title" content="Glossary — Landvex Ecosystem">
|
|
<meta name="twitter:description" content="Every term used across quiXzoom, AMOS, and Landvex. Defined precisely and kept current.">
|
|
<meta name="twitter:image" content="https://www.quixzoom.com/images/og-glossary.jpg">
|
|
|
|
<!-- Structured Data -->
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "DefinedTermSet",
|
|
"name": "Landvex Ecosystem Glossary",
|
|
"description": "Complete glossary of terms used across quiXzoom, AMOS, and Landvex platforms",
|
|
"publisher": {
|
|
"@type": "Organization",
|
|
"name": "Landvex Inc",
|
|
"url": "https://www.landvex.com"
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
:root {
|
|
--bg: #ffffff;
|
|
--bg-off: #f5f5f7;
|
|
--bg-card: #ffffff;
|
|
--text: #1d1d1f;
|
|
--text-secondary: #6e6e73;
|
|
--blue: #0066FF;
|
|
--blue-light: #e8f0ff;
|
|
--green: #00a550;
|
|
--green-light: #e6f7ee;
|
|
--purple: #7b2fff;
|
|
--purple-light: #f0e8ff;
|
|
--orange: #e8660a;
|
|
--orange-light: #fff0e6;
|
|
--pink: #ff3366;
|
|
--pink-light: #ffe8f0;
|
|
--radius: 18px;
|
|
--radius-sm: 8px;
|
|
--border: #d2d2d7;
|
|
--nav-height: 64px;
|
|
--shadow-hover: 0 12px 40px rgba(0,0,0,.10);
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--bg: #000000;
|
|
--bg-off: #1c1c1e;
|
|
--bg-card: #1c1c1e;
|
|
--text: #f5f5f7;
|
|
--text-secondary: #98989f;
|
|
--border: #3a3a3c;
|
|
--blue-light: rgba(0,102,255,.15);
|
|
--green-light: rgba(0,165,80,.15);
|
|
--purple-light: rgba(123,47,255,.15);
|
|
--orange-light: rgba(232,102,10,.15);
|
|
--pink-light: rgba(255,51,102,.15);
|
|
--shadow-hover: 0 12px 40px rgba(0,0,0,.5);
|
|
}
|
|
}
|
|
|
|
html { scroll-behavior: smooth; }
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', Arial, sans-serif;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
line-height: 1.6;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
/* ─── NAV ─────────────────────────────────────────────────── */
|
|
.nav {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
height: var(--nav-height);
|
|
background: rgba(255,255,255,.82);
|
|
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
|
backdrop-filter: blur(20px) saturate(180%);
|
|
border-bottom: 1px solid var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
padding: 0 24px;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
.nav { background: rgba(0,0,0,.82); }
|
|
}
|
|
|
|
.nav-logo {
|
|
font-size: 1.25rem;
|
|
font-weight: 800;
|
|
color: var(--text);
|
|
text-decoration: none;
|
|
letter-spacing: -.04em;
|
|
white-space: nowrap;
|
|
flex-shrink: 0;
|
|
}
|
|
.nav-logo .x { color: var(--blue); }
|
|
|
|
.nav-divider {
|
|
width: 1px;
|
|
height: 20px;
|
|
background: var(--border);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.nav-title {
|
|
font-size: .9375rem;
|
|
font-weight: 600;
|
|
color: var(--text-secondary);
|
|
white-space: nowrap;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.nav-search {
|
|
flex: 1;
|
|
max-width: 280px;
|
|
position: relative;
|
|
}
|
|
|
|
.nav-search input {
|
|
width: 100%;
|
|
padding: 8px 14px 8px 36px;
|
|
border-radius: 20px;
|
|
border: 1px solid var(--border);
|
|
background: var(--bg-off);
|
|
color: var(--text);
|
|
font-size: .875rem;
|
|
font-family: inherit;
|
|
outline: none;
|
|
transition: border-color .15s, box-shadow .15s;
|
|
}
|
|
|
|
.nav-search input:focus {
|
|
border-color: var(--blue);
|
|
box-shadow: 0 0 0 3px rgba(0,102,255,.12);
|
|
}
|
|
|
|
.nav-search-icon {
|
|
position: absolute;
|
|
left: 11px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
color: var(--text-secondary);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.nav-az {
|
|
display: flex;
|
|
gap: 2px;
|
|
overflow-x: auto;
|
|
scrollbar-width: none;
|
|
-ms-overflow-style: none;
|
|
}
|
|
.nav-az::-webkit-scrollbar { display: none; }
|
|
|
|
.nav-az a {
|
|
display: inline-block;
|
|
min-width: 24px;
|
|
padding: 3px 4px;
|
|
text-align: center;
|
|
font-size: .75rem;
|
|
font-weight: 600;
|
|
color: var(--text-secondary);
|
|
text-decoration: none;
|
|
border-radius: 5px;
|
|
transition: background .12s, color .12s;
|
|
line-height: 1.4;
|
|
}
|
|
.nav-az a:hover { background: var(--blue-light); color: var(--blue); }
|
|
.nav-az a.active { background: var(--blue); color: #fff; }
|
|
.nav-az a.empty { opacity: .25; pointer-events: none; }
|
|
|
|
/* ─── MAIN ────────────────────────────────────────────────── */
|
|
main {
|
|
max-width: 860px;
|
|
margin: 0 auto;
|
|
padding: 0 24px 80px;
|
|
}
|
|
|
|
.page-header {
|
|
padding: 64px 0 48px;
|
|
text-align: center;
|
|
}
|
|
|
|
.page-header h1 {
|
|
font-size: clamp(2.5rem, 6vw, 4rem);
|
|
font-weight: 800;
|
|
letter-spacing: -.04em;
|
|
line-height: 1.05;
|
|
color: var(--text);
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.page-header p {
|
|
font-size: 1.125rem;
|
|
color: var(--text-secondary);
|
|
max-width: 480px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
/* ─── DOMAIN FILTER ───────────────────────────────────────── */
|
|
.domain-filter {
|
|
display: flex;
|
|
gap: 8px;
|
|
justify-content: center;
|
|
margin-bottom: 32px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.domain-filter button {
|
|
padding: 6px 16px;
|
|
border-radius: 20px;
|
|
border: 1px solid var(--border);
|
|
background: var(--bg-off);
|
|
color: var(--text-secondary);
|
|
font-size: .875rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all .15s;
|
|
}
|
|
|
|
.domain-filter button:hover {
|
|
border-color: var(--blue);
|
|
color: var(--blue);
|
|
}
|
|
|
|
.domain-filter button.active {
|
|
background: var(--blue);
|
|
color: #fff;
|
|
border-color: var(--blue);
|
|
}
|
|
|
|
/* ─── LETTER SECTION ──────────────────────────────────────── */
|
|
.letter-section {
|
|
margin-bottom: 48px;
|
|
}
|
|
|
|
.letter-heading {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 12px;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.letter-heading h2 {
|
|
font-size: 2rem;
|
|
font-weight: 800;
|
|
color: var(--text-secondary);
|
|
letter-spacing: -.04em;
|
|
line-height: 1;
|
|
width: 36px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.letter-count {
|
|
font-size: .8125rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
/* ─── TERM CARD ───────────────────────────────────────────── */
|
|
.term-card {
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 28px 32px;
|
|
margin-bottom: 16px;
|
|
transition: box-shadow .2s, transform .2s;
|
|
}
|
|
|
|
.term-card:hover {
|
|
box-shadow: var(--shadow-hover);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.term-header {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
margin-bottom: 10px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.term-name {
|
|
font-size: 1.3125rem;
|
|
font-weight: 700;
|
|
color: var(--text);
|
|
letter-spacing: -.02em;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.term-category {
|
|
display: inline-block;
|
|
padding: 4px 12px;
|
|
border-radius: 20px;
|
|
font-size: .75rem;
|
|
font-weight: 600;
|
|
letter-spacing: .02em;
|
|
text-transform: uppercase;
|
|
white-space: nowrap;
|
|
flex-shrink: 0;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.cat-platform { background: var(--blue-light); color: var(--blue); }
|
|
.cat-technical { background: var(--green-light); color: var(--green); }
|
|
.cat-payment { background: var(--purple-light); color: var(--purple); }
|
|
.cat-legal { background: var(--orange-light); color: var(--orange); }
|
|
.cat-core { background: var(--bg-off); color: var(--text-secondary); }
|
|
.cat-amos { background: var(--pink-light); color: var(--pink); }
|
|
.cat-landvex { background: var(--blue-light); color: var(--blue); }
|
|
.cat-quixzoom { background: var(--green-light); color: var(--green); }
|
|
.cat-product { background: var(--orange-light); color: var(--orange); }
|
|
|
|
.term-short {
|
|
font-size: 1rem;
|
|
font-weight: 500;
|
|
color: var(--text);
|
|
margin-bottom: 12px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.term-long {
|
|
font-size: .9375rem;
|
|
color: var(--text-secondary);
|
|
line-height: 1.65;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.term-context {
|
|
background: var(--bg-off);
|
|
border-radius: var(--radius-sm);
|
|
padding: 14px 18px;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.term-context-label {
|
|
font-size: .75rem;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: .06em;
|
|
color: var(--blue);
|
|
display: block;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.term-context p {
|
|
font-size: .875rem;
|
|
color: var(--text-secondary);
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.term-related {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
}
|
|
|
|
.term-related > span {
|
|
font-size: .8125rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.term-related a {
|
|
display: inline-block;
|
|
padding: 3px 10px;
|
|
border-radius: 20px;
|
|
font-size: .8125rem;
|
|
font-weight: 500;
|
|
color: var(--blue);
|
|
background: var(--blue-light);
|
|
text-decoration: none;
|
|
transition: background .12s, color .12s;
|
|
}
|
|
|
|
.term-related a:hover {
|
|
background: var(--blue);
|
|
color: #fff;
|
|
}
|
|
|
|
/* ─── SEARCH EMPTY ────────────────────────────────────────── */
|
|
.search-empty {
|
|
display: none;
|
|
text-align: center;
|
|
padding: 64px 24px;
|
|
color: var(--text-secondary);
|
|
font-size: 1rem;
|
|
}
|
|
.search-empty.visible { display: block; }
|
|
|
|
/* ─── LAST UPDATED ────────────────────────────────────────── */
|
|
.last-updated {
|
|
text-align: center;
|
|
font-size: .8125rem;
|
|
color: var(--text-secondary);
|
|
margin-top: 48px;
|
|
padding-top: 24px;
|
|
border-top: 1px solid var(--border);
|
|
}
|
|
|
|
/* ─── RESPONSIVE ──────────────────────────────────────────── */
|
|
@media (max-width: 600px) {
|
|
.nav { gap: 10px; padding: 0 16px; }
|
|
.nav-az { display: none; }
|
|
.nav-search { max-width: none; flex: 1; }
|
|
.term-card { padding: 20px 18px; }
|
|
.page-header { padding: 40px 0 32px; }
|
|
.domain-filter { gap: 6px; }
|
|
.domain-filter button { padding: 5px 12px; font-size: .8125rem; }
|
|
}
|
|
|
|
@media (max-width: 900px) {
|
|
.nav-az { max-width: 200px; }
|
|
}
|
|
|
|
/* ─── HIGHLIGHT when anchor-targeted ─────────────────────── */
|
|
.term-card:target {
|
|
border-color: var(--blue);
|
|
box-shadow: 0 0 0 3px rgba(0,102,255,.15);
|
|
}
|
|
|
|
/* ─── HIDDEN during search/filter ─────────────────────────── */
|
|
.term-card.hidden { display: none; }
|
|
.letter-section.hidden { display: none; }
|
|
|
|
/* ─── LOADING STATE ───────────────────────────────────────── */
|
|
.loading {
|
|
text-align: center;
|
|
padding: 80px 24px;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.loading-spinner {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 3px solid var(--border);
|
|
border-top-color: var(--blue);
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
margin: 0 auto 16px;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<nav class="nav">
|
|
<a href="/" class="nav-logo">qui<span class="x">X</span>zoom</a>
|
|
<div class="nav-divider"></div>
|
|
<span class="nav-title">Glossary</span>
|
|
<div class="nav-search">
|
|
<svg class="nav-search-icon" width="14" height="14" viewBox="0 0 16 16" fill="none">
|
|
<circle cx="6.5" cy="6.5" r="5" stroke="currentColor" stroke-width="1.5"/>
|
|
<path d="M10 10l4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
|
</svg>
|
|
<input type="search" id="glossary-search" placeholder="Search terms…" autocomplete="off" aria-label="Search glossary">
|
|
</div>
|
|
<nav class="nav-az" id="az-nav" aria-label="Jump to letter">
|
|
<!-- Populated by JS -->
|
|
</nav>
|
|
</nav>
|
|
|
|
<main>
|
|
<header class="page-header">
|
|
<h1>Every term.<br>Defined.</h1>
|
|
<p>The complete ecosystem reference — from field observations to decision intelligence. Covering quiXzoom, AMOS, and Landvex.</p>
|
|
</header>
|
|
|
|
<div class="domain-filter" id="domain-filter">
|
|
<button data-domain="all" class="active">All</button>
|
|
<button data-domain="quixzoom">quiXzoom</button>
|
|
<button data-domain="amos">AMOS</button>
|
|
<button data-domain="landvex">Landvex</button>
|
|
</div>
|
|
|
|
<div class="search-empty" id="search-empty">No terms match "<span id="search-query"></span>"</div>
|
|
|
|
<div id="glossary-content">
|
|
<div class="loading">
|
|
<div class="loading-spinner"></div>
|
|
<p>Loading glossary…</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="last-updated" id="last-updated"></div>
|
|
</main>
|
|
|
|
<footer style="background:#0d0d10;border-top:1px solid rgba(255,255,255,.07);padding:32px 24px;margin-top:48px">
|
|
<div style="max-width:1080px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:16px">
|
|
<a href="/" style="font-size:1.1rem;font-weight:800;color:#fff;letter-spacing:-.03em;text-decoration:none">qui<span style="color:#3b82f6">X</span>zoom</a>
|
|
<nav style="display:flex;gap:20px;flex-wrap:wrap">
|
|
<a href="/" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">For Zoomers</a>
|
|
<a href="/for-organisations/" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">For Organisations</a>
|
|
<a href="/developer" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">Developer</a>
|
|
<a href="/pricing" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">Pricing</a>
|
|
<a href="/about" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">About</a>
|
|
<a href="/status" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">Status</a>
|
|
<a href="/privacy" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">Privacy</a>
|
|
<a href="/terms" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">Terms</a>
|
|
<a href="/security" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">Security</a>
|
|
<a href="/support" style="font-size:.8125rem;color:rgba(255,255,255,.75);transition:color .15s;text-decoration:none">Support</a>
|
|
</nav>
|
|
<p style="font-size:.8125rem;color:rgba(255,255,255,.75);margin:0">© 2026 Landvex Inc</p>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
// ─── GLOSSARY DATA ──────────────────────────────────────────────────
|
|
let glossaryData = null;
|
|
let currentDomain = 'all';
|
|
|
|
// ─── LOAD DATA ──────────────────────────────────────────────────────
|
|
async function loadGlossary() {
|
|
try {
|
|
const response = await fetch('glossary-data.json');
|
|
if (!response.ok) throw new Error('Failed to load glossary data');
|
|
glossaryData = await response.json();
|
|
renderGlossary();
|
|
updateLastUpdated();
|
|
} catch (error) {
|
|
document.getElementById('glossary-content').innerHTML = `
|
|
<div class="search-empty visible">
|
|
<p>Error loading glossary. Please refresh the page.</p>
|
|
<p style="font-size: .875rem; margin-top: 8px;">${error.message}</p>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
// ─── RENDER GLOSSARY ────────────────────────────────────────────────
|
|
function renderGlossary() {
|
|
if (!glossaryData) return;
|
|
|
|
const content = document.getElementById('glossary-content');
|
|
const terms = glossaryData.terms;
|
|
const categories = glossaryData.categories;
|
|
|
|
// Group terms by first letter
|
|
const grouped = {};
|
|
terms.forEach(term => {
|
|
const letter = term.name.charAt(0).toUpperCase();
|
|
if (!grouped[letter]) grouped[letter] = [];
|
|
grouped[letter].push(term);
|
|
});
|
|
|
|
// Sort letters
|
|
const letters = Object.keys(grouped).sort();
|
|
|
|
// Build A-Z nav
|
|
const azNav = document.getElementById('az-nav');
|
|
azNav.innerHTML = letters.map(letter =>
|
|
`<a href="#${letter.toLowerCase()}">${letter}</a>`
|
|
).join('');
|
|
|
|
// Build content
|
|
let html = '';
|
|
letters.forEach(letter => {
|
|
const letterTerms = grouped[letter].sort((a, b) => a.name.localeCompare(b.name));
|
|
html += `
|
|
<section class="letter-section" id="${letter.toLowerCase()}">
|
|
<div class="letter-heading">
|
|
<h2>${letter}</h2>
|
|
<span class="letter-count">${letterTerms.length} term${letterTerms.length !== 1 ? 's' : ''}</span>
|
|
</div>
|
|
${letterTerms.map(term => renderTermCard(term, categories)).join('')}
|
|
</section>
|
|
`;
|
|
});
|
|
|
|
content.innerHTML = html;
|
|
|
|
// Setup intersection observer for A-Z active state
|
|
setupAZObserver();
|
|
}
|
|
|
|
// ─── RENDER TERM CARD ───────────────────────────────────────────────
|
|
function renderTermCard(term, categories) {
|
|
const cat = categories[term.category] || categories.platform;
|
|
const relatedLinks = term.related.map(id => {
|
|
const relatedTerm = glossaryData.terms.find(t => t.id === id);
|
|
return relatedTerm ? `<a href="#${id}">${relatedTerm.name}</a>` : '';
|
|
}).join('');
|
|
|
|
return `
|
|
<article id="${term.id}" class="term-card" data-domains="${term.domains.join(' ')}">
|
|
<div class="term-header">
|
|
<h3 class="term-name">${term.name}</h3>
|
|
<span class="term-category cat-${term.category}">${cat.label}</span>
|
|
</div>
|
|
<p class="term-short">${term.short}</p>
|
|
<p class="term-long">${term.long}</p>
|
|
<div class="term-context">
|
|
<span class="term-context-label">${term.contextLabel}</span>
|
|
<p>${term.context}</p>
|
|
</div>
|
|
<div class="term-related">
|
|
<span>Related:</span>
|
|
${relatedLinks}
|
|
</div>
|
|
</article>
|
|
`;
|
|
}
|
|
|
|
// ─── UPDATE LAST UPDATED ────────────────────────────────────────────
|
|
function updateLastUpdated() {
|
|
if (!glossaryData?.lastUpdated) return;
|
|
const date = new Date(glossaryData.lastUpdated);
|
|
document.getElementById('last-updated').textContent =
|
|
`Last updated: ${date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}`;
|
|
}
|
|
|
|
// ─── SEARCH ─────────────────────────────────────────────────────────
|
|
const searchInput = document.getElementById('glossary-search');
|
|
const emptyMsg = document.getElementById('search-empty');
|
|
const querySpan = document.getElementById('search-query');
|
|
|
|
searchInput.addEventListener('input', () => {
|
|
const q = searchInput.value.trim().toLowerCase();
|
|
|
|
if (!q) {
|
|
document.querySelectorAll('.term-card, .letter-section').forEach(el => el.classList.remove('hidden'));
|
|
emptyMsg.classList.remove('visible');
|
|
return;
|
|
}
|
|
|
|
let anyVisible = false;
|
|
|
|
document.querySelectorAll('.letter-section').forEach(section => {
|
|
let sectionHasVisible = false;
|
|
section.querySelectorAll('.term-card').forEach(card => {
|
|
const text = card.textContent.toLowerCase();
|
|
const id = card.id.toLowerCase();
|
|
const match = text.includes(q) || id.includes(q);
|
|
|
|
card.classList.toggle('hidden', !match);
|
|
if (match) sectionHasVisible = true;
|
|
});
|
|
section.classList.toggle('hidden', !sectionHasVisible);
|
|
if (sectionHasVisible) anyVisible = true;
|
|
});
|
|
|
|
querySpan.textContent = searchInput.value.trim();
|
|
emptyMsg.classList.toggle('visible', !anyVisible);
|
|
});
|
|
|
|
// ─── DOMAIN FILTER ──────────────────────────────────────────────────
|
|
document.getElementById('domain-filter').addEventListener('click', (e) => {
|
|
if (!e.target.matches('button')) return;
|
|
|
|
document.querySelectorAll('.domain-filter button').forEach(btn => btn.classList.remove('active'));
|
|
e.target.classList.add('active');
|
|
|
|
currentDomain = e.target.dataset.domain;
|
|
filterByDomain();
|
|
});
|
|
|
|
function filterByDomain() {
|
|
if (!glossaryData) return;
|
|
|
|
let anyVisible = false;
|
|
|
|
document.querySelectorAll('.letter-section').forEach(section => {
|
|
let sectionHasVisible = false;
|
|
section.querySelectorAll('.term-card').forEach(card => {
|
|
const domains = card.dataset.domains?.split(' ') || [];
|
|
const match = currentDomain === 'all' || domains.includes(currentDomain);
|
|
|
|
card.classList.toggle('hidden', !match);
|
|
if (match) sectionHasVisible = true;
|
|
});
|
|
section.classList.toggle('hidden', !sectionHasVisible);
|
|
if (sectionHasVisible) anyVisible = true;
|
|
});
|
|
|
|
emptyMsg.classList.toggle('visible', !anyVisible);
|
|
if (!anyVisible) {
|
|
querySpan.textContent = currentDomain === 'all' ? '' : `domain: ${currentDomain}`;
|
|
}
|
|
}
|
|
|
|
// ─── A-Z ACTIVE LETTER TRACKING ─────────────────────────────────────
|
|
function setupAZObserver() {
|
|
const azLinks = document.querySelectorAll('.nav-az a');
|
|
const sections = Array.from(document.querySelectorAll('.letter-section[id]'));
|
|
|
|
if (sections.length === 0) return;
|
|
|
|
const observer = new IntersectionObserver(entries => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
const id = entry.target.id;
|
|
azLinks.forEach(a => {
|
|
a.classList.toggle('active', a.getAttribute('href') === '#' + id);
|
|
});
|
|
}
|
|
});
|
|
}, { rootMargin: '-20% 0px -70% 0px' });
|
|
|
|
sections.forEach(s => observer.observe(s));
|
|
}
|
|
|
|
// ─── INITIALIZE ─────────────────────────────────────────────────────
|
|
loadGlossary();
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|