The service shelf
Choose the care your space needs.
Browse focused routines designed for real homes and working spaces. Save a service to Fallows or add it to your planning cart.
The service shelf
Browse focused routines designed for real homes and working spaces. Save a service to Fallows or add it to your planning cart.
${x.category}
${x.shortDescription}
$${x.price.toFixed(2)} ${x.unit}
${x.category}
${x.description}
$${x.price.toFixed(2)} ${x.unit} · ${x.duration}
`; $('detail').classList.replace('hidden', 'flex'); } if (e.target.id === 'detailClose') $('detail').classList.replace('flex', 'hidden'); }); document.getElementById('detail').addEventListener('click', e => { if (e.target.id === 'detail') $('detail').classList.replace('flex', 'hidden'); if (e.target.dataset.cartModal) { let c = cart(); c[e.target.dataset.cartModal] = (c[e.target.dataset.cartModal] || 0) + 1; localStorage.setItem('ember-cart', JSON.stringify(c)); $('detail').classList.replace('flex', 'hidden'); } }); // header/footer bootstrap const t = document.getElementById('menuToggle'), n = document.getElementById('mobileNav'); t && t.addEventListener('click', () => { const o = !n.classList.contains('hidden'); n.classList.toggle('hidden'); t.setAttribute('aria-expanded', String(!o)); }); const a = document.getElementById('accountModal'); document.getElementById('accountOpen')?.addEventListener('click', () => a.classList.replace('hidden', 'flex')); document.getElementById('accountClose')?.addEventListener('click', () => a.classList.replace('flex', 'hidden')); document.getElementById('themeToggle')?.addEventListener('click', () => { const d = document.documentElement.classList.toggle('dark'); localStorage.setItem('ember-theme', d ? 'dark' : 'light'); }); if (localStorage.getItem('ember-theme') === 'dark') document.documentElement.classList.add('dark'); const banner = document.getElementById('cookieBanner'); if (localStorage.getItem('ember-cookie-consent') === 'accepted') banner?.remove(); document.getElementById('cookieAccept')?.addEventListener('click', () => { localStorage.setItem('ember-cookie-consent', 'accepted'); banner.remove(); }); } fetch('./catalog.json') .then(r => r.json()) .then(data => { state.items = data; populateCategories(); render(); setupListeners(); }) .catch(() => { // fallback with sample data if fetch fails in demo state.items = [ {"id":"s1","name":"Weekly Home Refresh","category":"Home","shortDescription":"Regular maintenance for bright homes.","description":"Complete dusting, vacuuming and surface sanitization every week.","duration":"3 hours","price":185,"unit":"per visit","accent":"#c8875a"}, {"id":"s2","name":"Deep Kitchen Clean","category":"Home","shortDescription":"Focus on the busiest room in the house.","description":"Appliances, behind the stove, inside fridge and cabinets.","duration":"4 hours","price":265,"unit":"per visit","accent":"#5b3a29"}, {"id":"s3","name":"Post-Reno Reset","category":"Specialty","shortDescription":"New construction or renovation dust removal.","description":"Fine dust, paint splatter, adhesive residue, and window frames.","duration":"5 hours","price":310,"unit":"per visit","accent":"#c8875a"}, {"id":"s4","name":"Large Office Tidy","category":"Office","shortDescription":"Corporate and coworking spaces.","description":"Daily or weekly service for desks, meeting rooms and shared areas.","duration":"4 hours","price":240,"unit":"per visit","accent":"#5b3a29"}, {"id":"s5","name":"Move-Out Clean","category":"Specialty","shortDescription":"Deposit-ready results.","description":"Full interior clean for rentals returning keys.","duration":"6 hours","price":385,"unit":"flat","accent":"#c8875a"} ]; populateCategories(); render(); setupListeners(); });