Enerboard Studio

The course shelf

Find the next useful experiment.

Choose a focused course for the stage your game is in. Each listing explains the practical artifact you will create.

Open Fallows

Need a considered recommendation?

Tell us about your current prototype and we will point you toward a sensible starting place.

Contact the studio
`; const footerHTML = ``; document.querySelector('header').innerHTML = headerHTML; document.querySelector('footer').innerHTML = footerHTML; let products=[],filtered=[],page=1;const perPage=6;const fallowsKey="enerboard-fallows",cartKey="enerboard-cart";const esc=s=>String(s).replace(/[&<>"']/g,c=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[c])); function saveFallows(ids){localStorage.setItem(fallowsKey,JSON.stringify(ids))} function getFallows(){try{return JSON.parse(localStorage.getItem(fallowsKey))||[]}catch{return[]}} function saveCart(cart){localStorage.setItem(cartKey,JSON.stringify(cart))} function getCart(){try{return JSON.parse(localStorage.getItem(cartKey))||{}}catch{return{}}} function applyFilters(){const q=(document.getElementById("searchInput").value||"").toLowerCase().trim();const cat=document.getElementById("categoryFilter").value;filtered=products.filter(p=>{const matchQ=!q||p.title.toLowerCase().includes(q)||p.shortDescription.toLowerCase().includes(q);const matchC=cat==="all"||p.category===cat;const matchL=true;return matchQ&&matchC&&matchL});page=1;render()} function render(){const grid=document.getElementById("catalogGrid");const count=document.getElementById("resultCount");const pag=document.getElementById("pagination");grid.innerHTML="";pag.innerHTML="";const total=filtered.length;count.textContent=`${total} courses`;if(total===0){grid.innerHTML='

No courses match your filters.

';return}const start=(page-1)*perPage;const end=Math.min(start+perPage,total);const slice=filtered.slice(start,end);const fallows=getFallows();const cart=getCart();slice.forEach(p=>{const isTracked=fallows.includes(p.id);const qty=cart[p.id]||0;const card=document.createElement("div");card.className="dd23l xk9h7 p2aql flex flex-col justify-between rounded-3xl bg-[#FFFDF8] p-7 border border-[#DCCFC0]";card.innerHTML=`
${esc(p.category)}

${esc(p.title)}

${esc(p.level)} · ${esc(p.format)} · ${esc(p.duration)}

${esc(p.shortDescription)}

`;grid.appendChild(card)});const pages=Math.ceil(total/perPage);for(let i=1;i<=pages;i++){const a=document.createElement("a");a.href="#";a.textContent=i;a.className=`px-4 py-2 rounded-full border ${i===page?"bg-[#B95F45] text-white border-[#B95F45]":"border-[#DCCFC0]"}`;a.onclick=e=>{e.preventDefault();page=i;render()};pag.appendChild(a)}} function openDetail(id){const p=products.find(x=>x.id===id);if(!p)return;const modal=document.getElementById("detailModal");const content=document.getElementById("detailContent");const fallows=getFallows();const cart=getCart();const isTracked=fallows.includes(p.id);const qty=cart[p.id]||0;content.innerHTML=`
${esc(p.category)}

${esc(p.title)}

${esc(p.level)} · ${esc(p.format)} · ${esc(p.duration)} · $${p.price}

${esc(p.description)}

Outcomes

Lessons

Go to cart
`;modal.classList.remove("hidden");modal.classList.add("flex");modal.onclick=e=>{if(e.target===modal)modal.classList.add("hidden")};content.querySelectorAll("[data-track-modal]").forEach(b=>b.onclick=()=>{const ids=getFallows();const idx=ids.indexOf(p.id);if(idx>-1)ids.splice(idx,1);else ids.push(p.id);saveFallows(ids);openDetail(p.id);render()});content.querySelectorAll("[data-cart-modal]").forEach(b=>b.onclick=()=>{const c=getCart();c[p.id]=(c[p.id]||0)+1;saveCart(c);openDetail(p.id);render()})} function bindEvents(){document.getElementById("searchInput").oninput=applyFilters;document.getElementById("categoryFilter").onchange=applyFilters;document.getElementById("closeDetail").onclick=()=>document.getElementById("detailModal").classList.add("hidden");document.getElementById("catalogGrid").onclick=e=>{const t=e.target;if(t.dataset.detail)openDetail(t.dataset.detail);if(t.dataset.track){const ids=getFallows();const id=t.dataset.track;const idx=ids.indexOf(id);if(idx>-1)ids.splice(idx,1);else ids.push(id);saveFallows(ids);render()}if(t.dataset.cart){const c=getCart();const id=t.dataset.cart;c[id]=(c[id]||0)+1;saveCart(c);render()}}} async function init(){try{const r=await fetch("./catalog.json");if(!r.ok)throw Error();products=await r.json();[...new Set(products.map(x=>x.category))].forEach(c=>document.getElementById("categoryFilter").insertAdjacentHTML("beforeend",``));applyFilters();bindEvents()}catch(e){document.getElementById("catalogGrid").innerHTML='

The catalog could not be loaded. Please contact the studio for assistance.

'}} init();