Stillwood Candle Studio

The course shelf

Learn the material, then make it yours.

Every course is written for deliberate practice, with transparent scope and a clear reason for each exercise.

`; document.getElementById('site-header').innerHTML = headerHTML; document.getElementById('site-footer').innerHTML = footerHTML; let products = []; let page = 1; const perPage = 6; const tracked = () => JSON.parse(localStorage.getItem("stillwoodTracked") || "[]"); const cart = () => JSON.parse(localStorage.getItem("stillwoodCart") || "{}"); const esc = s => String(s).replace(/[&<>"']/g, m => ({"&":"&","<":"<",">":">","\"":""","'":"'"}[m])); function populateCategories() { const select = document.getElementById('category'); const cats = [...new Set(products.map(p => p.category))].sort(); cats.forEach(cat => { const opt = document.createElement('option'); opt.value = cat; opt.textContent = cat; select.appendChild(opt); }); } function render() { const q = document.getElementById('search').value.toLowerCase(); const c = document.getElementById('category').value; let list = products.filter(x => (c === "all" || x.category === c) && (`${x.title} ${x.description} ${x.category}`.toLowerCase().includes(q))); const pages = Math.max(1, Math.ceil(list.length / perPage)); page = Math.min(page, pages); const view = list.slice((page - 1) * perPage, page * perPage); const ids = tracked(); const grid = document.getElementById('course-grid'); grid.innerHTML = view.length ? view.map(x => `

${esc(x.category)} · ${esc(x.level)}

${esc(x.title)}

${esc(x.description)}

$${x.price.toFixed(2)} · ${esc(x.duration)}

View details ${ids.includes(x.id) ? "Tracked" : "Track"} Add to cart
`).join('') : `

No course matches those filters. Try a broader search.

`; const pag = document.getElementById('pagination'); pag.innerHTML = Array.from({length: pages}, (_, i) => `${i+1}`).join(''); } async function init() { try { const res = await fetch('./catalog.json'); products = await res.json(); } catch (e) { products = []; } populateCategories(); render(); document.getElementById('search').addEventListener('input', () => { page = 1; render(); }); document.getElementById('category').addEventListener('change', () => { page = 1; render(); }); document.addEventListener('click', e => { const a = e.target.closest('[data-detail],[data-track],[data-cart],[data-page]'); if (!a) return; e.preventDefault(); const id = a.dataset.detail || a.dataset.track || a.dataset.cart; if (a.dataset.detail) { const x = products.find(p => p.id === id); document.getElementById('detail-content').innerHTML = `

${esc(x.category)}

${esc(x.title)}

${esc(x.details)}

$${x.price.toFixed(2)} · ${esc(x.duration)}

`; document.getElementById('detail-modal').classList.replace('hidden', 'flex'); } else if (a.dataset.track) { let t = tracked(); const n = t.includes(id) ? t.filter(v => v !== id) : [...t, id]; localStorage.setItem('stillwoodTracked', JSON.stringify(n)); render(); } else if (a.dataset.cart) { let b = cart(); b[id] = (b[id] || 0) + 1; localStorage.setItem('stillwoodCart', JSON.stringify(b)); window.location.href = './cart.html'; } else if (a.dataset.page) { page = +a.dataset.page; render(); } }); document.getElementById('close-detail').addEventListener('click', e => { e.preventDefault(); document.getElementById('detail-modal').classList.replace('flex', 'hidden'); }); document.getElementById('detail-modal').addEventListener('click', e => { if (e.target.id === 'detail-modal') document.getElementById('detail-modal').classList.replace('flex', 'hidden'); }); const menuToggle = document.getElementById('menu-toggle'); const mainNav = document.getElementById('main-nav'); if (menuToggle && mainNav) { menuToggle.addEventListener('click', () => { mainNav.classList.toggle('hidden'); mainNav.classList.toggle('flex'); mainNav.classList.toggle('flex-col'); mainNav.classList.toggle('absolute'); mainNav.classList.toggle('top-20'); mainNav.classList.toggle('left-6'); mainNav.classList.toggle('right-6'); mainNav.classList.toggle('bg-[#F7F2E8]'); mainNav.classList.toggle('border'); mainNav.classList.toggle('border-[#D8CDBB]'); mainNav.classList.toggle('p-6'); mainNav.classList.toggle('rounded-2xl'); mainNav.classList.toggle('shadow-xl'); mainNav.classList.toggle('dark:bg-[#24352D]'); mainNav.classList.toggle('dark:border-[#3A4A42]'); }); } const cookieBanner = document.getElementById('cookie-banner'); function hideCookieBanner() { if (cookieBanner) cookieBanner.style.display = 'none'; } if (localStorage.getItem('stillwoodCookies') === 'accepted') hideCookieBanner(); const cookieCloseLink = document.querySelector('[data-cookie-close]'); if (cookieCloseLink) { cookieCloseLink.addEventListener('click', function(e) { e.preventDefault(); localStorage.setItem('stillwoodCookies', 'accepted'); hideCookieBanner(); }); } const themeToggleLinks = document.querySelectorAll('[data-theme-toggle]'); themeToggleLinks.forEach(link => { link.addEventListener('click', function(e) { e.preventDefault(); const html = document.documentElement; if (html.classList.contains('dark')) { html.classList.remove('dark'); localStorage.setItem('stillwoodTheme', 'light'); } else { html.classList.add('dark'); localStorage.setItem('stillwoodTheme', 'dark'); } }); }); if (localStorage.getItem('stillwoodTheme') === 'dark') { document.documentElement.classList.add('dark'); } } init();