Homes
Fresh Carpet Cleaning for Calgary Homes enter details below and you'll be contacted to finalize your appointment
Estimated service price Based on your carpet package and stairs.
$179
Submit
Booking Request Status
Choose an available service day to get started.
Sun Mon Tue Wed Thu Fri Sat
Grey dates are unavailable.
/* NIMBUS HOMES3 PRICING BUILD 2026-09-13-1 */ (function () { if (window.__nimbusHomesBookingLoaded) return; window.__nimbusHomesBookingLoaded = true; /* LIVE CLOUDFLARE API */ var API_URL = 'https://bookings.nimbuscarpetcare.ca'; var selectedDate = ''; var availability = { homeDates: [], blockedDates: [], condoDays: [] }; var now = new Date(); var calendarMonth = new Date( now.getFullYear(), now.getMonth(), 1 ); function cleanText(value) { return String(value || '') .trim() .replace(/\s+/g, ' '); } function trackingContext() { return window.NimbusTraffic && typeof window.NimbusTraffic.context === 'function' ? window.NimbusTraffic.context() : {}; } function padNumber(value) { return String(value).padStart(2, '0'); } function dateKeyFromDate(date) { return date.getFullYear() + '-' + padNumber(date.getMonth() + 1) + '-' + padNumber(date.getDate()); } function todayKey() { var today = new Date(); return dateKeyFromDate(new Date( today.getFullYear(), today.getMonth(), today.getDate() )); } function prettyDate(dateString) { if (!dateString) return ''; var parts = dateString.split('-'); var date = new Date( Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]) ); return date.toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' }); } function containsDate(list, dateString) { return Array.isArray(list) && list.indexOf(dateString) !== -1; } function condoDayForDate(dateString) { var days = availability.condoDays || []; for (var i = 0; i < days.length; i++) { if (days[i] && days[i].date === dateString) { return days[i]; } } return null; } function dateStatus(dateString) { if (containsDate(availability.blockedDates, dateString)) { return 'blocked'; } if (containsDate(availability.homeDates, dateString)) { return 'home'; } if (condoDayForDate(dateString)) { return 'condo'; } return ''; } async function api(path, options) { var response = await fetch(API_URL + path, options || {}); var data = {}; try { data = await response.json(); } catch (error) {} if (!response.ok) { throw new Error(data.error || 'Booking service is temporarily unavailable.'); } return data; } async function refreshAvailability() { try { var data = await api('/state'); availability.homeDates = data.homeDates || []; availability.blockedDates = data.blockedDates || []; availability.condoDays = data.condoDays || []; renderCalendar(); return true; } catch (error) { var root = document.getElementById('nbhApp'); var statusEl = root && root.querySelector('#nbhStatus'); if (statusEl) statusEl.textContent = error.message; return false; } } function updatePrice() { var root = document.getElementById('nbhApp'); if (!root) return; var packageInput = root.querySelector('#nbhPackage'); var stairsInput = root.querySelector('#nbhStairs'); var totalEl = root.querySelector('#nbhPriceTotal'); if (!packageInput || !stairsInput || !totalEl) return; var packagePrice = Number(packageInput.value) || 0; var total = packagePrice + (Number(stairsInput.value) || 0); totalEl.textContent = '$' + total + (packagePrice === 359 ? '+' : ''); } function syncConcernsValue() { var root = document.getElementById('nbhApp'); if (!root) return; var textarea = root.querySelector('#nbhNotes'); var display = root.querySelector('#nbhConcernsValue'); if (!display) return; var concerns = cleanText(textarea && textarea.value); display.textContent = concerns || 'Add details (optional)'; } function openConcerns() { var root = document.getElementById('nbhApp'); if (!root) return; var popover = root.querySelector('#nbhConcernsPopover'); var toggle = root.querySelector('#nbhConcernsToggle'); var textarea = root.querySelector('#nbhNotes'); if (!popover || !toggle) return; closeCalendar(); popover.hidden = false; toggle.setAttribute('aria-expanded', 'true'); if (textarea) setTimeout(function () { textarea.focus(); }, 0); } function closeConcerns() { var root = document.getElementById('nbhApp'); if (!root) return; var popover = root.querySelector('#nbhConcernsPopover'); var toggle = root.querySelector('#nbhConcernsToggle'); if (popover) popover.hidden = true; if (toggle) toggle.setAttribute('aria-expanded', 'false'); syncConcernsValue(); } function toggleConcerns() { var root = document.getElementById('nbhApp'); if (!root) return; var popover = root.querySelector('#nbhConcernsPopover'); if (!popover) return; if (popover.hidden) openConcerns(); else closeConcerns(); } function setSelectedDate(dateString) { var root = document.getElementById('nbhApp'); if (!root) return; selectedDate = dateString || ''; var hiddenInput = root.querySelector('#nbhDate'); var display = root.querySelector('#nbhCalendarValue'); if (hiddenInput) hiddenInput.value = selectedDate; if (display) { display.textContent = selectedDate ? prettyDate(selectedDate) : 'Choose an available date'; } renderCalendar(); } function openCalendar() { var root = document.getElementById('nbhApp'); if (!root) return; closeConcerns(); var popover = root.querySelector('#nbhCalendarPopover'); var backdrop = root.querySelector('#nbhCalendarBackdrop'); var toggle = root.querySelector('#nbhCalendarToggle'); if (!popover || !backdrop || !toggle) return; popover.hidden = false; backdrop.hidden = false; toggle.setAttribute('aria-expanded', 'true'); refreshAvailability(); } function closeCalendar() { var root = document.getElementById('nbhApp'); if (!root) return; var popover = root.querySelector('#nbhCalendarPopover'); var backdrop = root.querySelector('#nbhCalendarBackdrop'); var toggle = root.querySelector('#nbhCalendarToggle'); if (popover) popover.hidden = true; if (backdrop) backdrop.hidden = true; if (toggle) toggle.setAttribute('aria-expanded', 'false'); } function renderCalendar() { var root = document.getElementById('nbhApp'); if (!root) return; var monthLabel = root.querySelector('#nbhCalendarMonth'); var grid = root.querySelector('#nbhCalendarGrid'); if (!monthLabel || !grid) return; var year = calendarMonth.getFullYear(); var month = calendarMonth.getMonth(); var firstDay = new Date(year, month, 1); var daysInMonth = new Date(year, month + 1, 0).getDate(); var firstWeekday = firstDay.getDay(); monthLabel.textContent = firstDay.toLocaleDateString(undefined, { month: 'long', year: 'numeric' }); grid.innerHTML = ''; for (var blank = 0; blank < firstWeekday; blank++) { var blankCell = document.createElement('div'); blankCell.className = 'nbh-calendar-blank'; grid.appendChild(blankCell); } for (var dayNumber = 1; dayNumber <= daysInMonth; dayNumber++) { var date = new Date(year, month, dayNumber); var dateKey = dateKeyFromDate(date); var status = dateStatus(dateKey); var isPast = dateKey < todayKey(); var isToday = dateKey === todayKey(); var isSelected = selectedDate === dateKey; var button = document.createElement('button'); button.type = 'button'; button.className = 'nbh-calendar-day'; button.textContent = dayNumber; button.setAttribute('data-nbh-calendar-date', dateKey); if (isPast) { button.classList.add('past'); button.disabled = true; button.title = 'Past date'; } if (status) { button.classList.add('taken'); button.disabled = true; button.title = status === 'condo' ? 'Reserved for a Condo cleaning day' : status === 'home' ? 'Already booked' : 'Unavailable'; } if (isToday && !status) button.classList.add('today'); if (isSelected && !isPast && !status) button.classList.add('selected'); grid.appendChild(button); } var totalCells = firstWeekday + daysInMonth; var remaining = totalCells % 7 === 0 ? 0 : 7 - (totalCells % 7); for (var tail = 0; tail < remaining; tail++) { var tailCell = document.createElement('div'); tailCell.className = 'nbh-calendar-blank'; grid.appendChild(tailCell); } } function previousMonth() { calendarMonth = new Date( calendarMonth.getFullYear(), calendarMonth.getMonth() - 1, 1 ); renderCalendar(); } function nextMonth() { calendarMonth = new Date( calendarMonth.getFullYear(), calendarMonth.getMonth() + 1, 1 ); renderCalendar(); } function scrollToBookingStatus(statusEl) { if (!statusEl) return; setTimeout(function () { statusEl.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 50); } function showBookingStatus(root) { var panel = root && root.querySelector('#nbhStatusPanel'); if (panel) panel.hidden = false; } async function submitBooking() { var root = document.getElementById('nbhApp'); if (!root) return; var statusEl = root.querySelector('#nbhStatus'); showBookingStatus(root); scrollToBookingStatus(statusEl); var button = root.querySelector('#nbhSubmit'); var name = cleanText(root.querySelector('#nbhName').value); var email = cleanText(root.querySelector('#nbhEmail').value); var phone = cleanText(root.querySelector('#nbhPhone').value); var addressInput = root.querySelector('#nbhAddress'); var address = cleanText(addressInput && addressInput.value); var packageInput = root.querySelector('#nbhPackage'); var stairsInput = root.querySelector('#nbhStairs'); var preferredTime = cleanText(root.querySelector('#nbhTime').value); var notesInput = root.querySelector('#nbhNotes'); var notes = cleanText(notesInput && notesInput.value); if (!name) { statusEl.textContent = 'Add your name first.'; return; } if (!email && !phone) { statusEl.textContent = 'Add an email address or phone number so Nimbus can confirm your appointment.'; return; } if (!address) { statusEl.textContent = 'Add your Calgary neighbourhood or address.'; return; } if (!selectedDate) { statusEl.textContent = 'Choose an available service day first.'; openCalendar(); return; } var bookedDate = selectedDate; var packagePrice = Number(packageInput.value) || 0; var payload = { name: name, email: email, phone: phone, address: address, pkg: packageInput.options[packageInput.selectedIndex].text, packagePrice: packagePrice, stairs: stairsInput.options[stairsInput.selectedIndex].text, stairPrice: Number(stairsInput.value) || 0, stainLevel: 'Low', stainPrice: 0, preferredTime: preferredTime, notes: notes, date: bookedDate, tracking: trackingContext() }; button.disabled = true; statusEl.textContent = 'Sending your appointment request...'; try { await api('/book-home', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload) }); setSelectedDate(''); await refreshAvailability(); statusEl.textContent = 'Appointment requested for ' + prettyDate(bookedDate) + '. Nimbus will confirm your booking by text or email.'; } catch (error) { statusEl.textContent = error.message; await refreshAvailability(); if (dateStatus(bookedDate)) setSelectedDate(''); } finally { button.disabled = false; } } function initHomesBooking() { var root = document.getElementById('nbhApp'); if (!root) { setTimeout(initHomesBooking, 250); return; } if (root.getAttribute('data-nbh-initialized') === 'true') return; root.setAttribute('data-nbh-initialized', 'true'); root.querySelector('#nbhCalendarToggle').addEventListener('click', function (event) { event.preventDefault(); openCalendar(); }); root.querySelector('#nbhCalendarPrev').addEventListener('click', function (event) { event.preventDefault(); previousMonth(); }); root.querySelector('#nbhCalendarNext').addEventListener('click', function (event) { event.preventDefault(); nextMonth(); }); root.querySelector('#nbhCalendarGrid').addEventListener('click', function (event) { var button = event.target.closest('[data-nbh-calendar-date]'); if (!button || button.disabled) return; event.preventDefault(); setSelectedDate(button.getAttribute('data-nbh-calendar-date')); closeCalendar(); }); root.querySelector('#nbhCalendarBackdrop').addEventListener('click', closeCalendar); root.querySelector('#nbhPackage').addEventListener('change', updatePrice); root.querySelector('#nbhStairs').addEventListener('change', updatePrice); root.querySelector('#nbhConcernsToggle').addEventListener('click', function (event) { event.preventDefault(); event.stopPropagation(); toggleConcerns(); }); root.querySelector('#nbhConcernsDone').addEventListener('click', function (event) { event.preventDefault(); closeConcerns(); }); root.querySelector('#nbhNotes').addEventListener('input', syncConcernsValue); root.querySelector('#nbhSubmit').addEventListener('click', function (event) { event.preventDefault(); submitBooking(); }); document.addEventListener('keydown', function (event) { if (event.key === 'Escape') { closeCalendar(); closeConcerns(); } }); document.addEventListener('click', function (event) { if (!event.target.closest('#nbhConcernsField')) closeConcerns(); }); window.addEventListener('focus', refreshAvailability); updatePrice(); syncConcernsValue(); renderCalendar(); refreshAvailability(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initHomesBooking); } else { initHomesBooking(); } })();