We'd Love to
Hear From You

Whether you have a project ready to go or just want to explore your options — get in touch and we'll get back to you quickly.

Business Hours

  • Monday – Friday7:00 AM – 6:00 PM
  • Saturday8:00 AM – 4:00 PM
  • SundayClosed

Service Area

We serve Calgary and the surrounding Alberta region, including Airdrie, Cochrane, Chestermere, Okotoks, and beyond.

Request a Free Quote

Fill out the form below and we'll get back to you within one business day.

Find Us in Calgary

Proudly serving Calgary and surrounding Alberta communities.

Google Maps Embed Goes HereCalgary, Alberta, Canada

Prefer to Call? We're Ready.

Our team is available Monday–Saturday. Call us directly for a quick conversation about your project.

Skip to main content
/* =================================== Northern Carpentry - Main JavaScript Zoho Sites Compatible (Vanilla JS only) =================================== */ document.addEventListener('DOMContentLoaded', function () { /* --------------- Navbar Scroll --------------- */ var navbar = document.getElementById('navbar'); function updateNavbar() { if (!navbar) return; if (window.scrollY > 80) { navbar.classList.add('scrolled'); } else { navbar.classList.remove('scrolled'); } } if (navbar) { window.addEventListener('scroll', updateNavbar, { passive: true }); updateNavbar(); } /* --------------- Active Nav Link --------------- */ var currentPage = window.location.pathname.split('/').pop() || 'index.html'; document.querySelectorAll('.nav-link').forEach(function (link) { var href = link.getAttribute('href'); if (href === currentPage || (currentPage === '' && href === 'index.html')) { link.classList.add('active'); } }); /* --------------- Hamburger Menu --------------- */ var hamburger = document.getElementById('hamburger'); var navMenu = document.getElementById('nav-menu'); var menuOverlay = document.getElementById('menu-overlay'); function openMenu() { if (!hamburger || !navMenu) return; hamburger.classList.add('open'); navMenu.classList.add('open'); if (menuOverlay) menuOverlay.classList.add('visible'); document.body.style.overflow = 'hidden'; } function closeMenu() { if (!hamburger || !navMenu) return; hamburger.classList.remove('open'); navMenu.classList.remove('open'); if (menuOverlay) menuOverlay.classList.remove('visible'); document.body.style.overflow = ''; } if (hamburger && navMenu) { hamburger.addEventListener('click', function () { navMenu.classList.contains('open') ? closeMenu() : openMenu(); }); document.querySelectorAll('.nav-link').forEach(function (link) { link.addEventListener('click', closeMenu); }); if (menuOverlay) { menuOverlay.addEventListener('click', closeMenu); } document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeMenu(); }); } /* --------------- Smooth Scroll --------------- */ document.querySelectorAll('a[href^="#"]').forEach(function (anchor) { anchor.addEventListener('click', function (e) { var targetId = this.getAttribute('href'); if (targetId === '#') return; var target = document.querySelector(targetId); if (target) { e.preventDefault(); var navH = navbar ? navbar.offsetHeight : 0; var top = target.getBoundingClientRect().top + window.scrollY - navH - 24; window.scrollTo({ top: top, behavior: 'smooth' }); } }); }); /* --------------- Stats Counter Animation --------------- */ var counters = document.querySelectorAll('[data-count]'); function animateCounter(el) { var target = parseInt(el.getAttribute('data-count'), 10); var suffix = el.getAttribute('data-suffix') || ''; var prefix = el.getAttribute('data-prefix') || ''; var duration = 2200; var start = performance.now(); function tick(now) { var elapsed = now - start; var progress = Math.min(elapsed / duration, 1); var eased = 1 - Math.pow(1 - progress, 3); // ease-out cubic var current = Math.floor(eased * target); el.textContent = prefix + current.toLocaleString() + suffix; if (progress < 1) { requestAnimationFrame(tick); } else { el.textContent=prefix + target.toLocaleString() + suffix; } } requestAnimationFrame(tick); } if (counters.length> 0 && 'IntersectionObserver' in window) { var counterObserver = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) { animateCounter(entry.target); counterObserver.unobserve(entry.target); } }); }, { threshold: 0.5 }); counters.forEach(function (el) { counterObserver.observe(el); }); } else { // Fallback: just show the final number counters.forEach(function (el) { var suffix = el.getAttribute('data-suffix') || ''; var prefix = el.getAttribute('data-prefix') || ''; el.textContent = prefix + parseInt(el.getAttribute('data-count'), 10).toLocaleString() + suffix; }); } /* --------------- Scroll Reveal --------------- */ var revealEls = document.querySelectorAll('.reveal, .reveal-left, .reveal-right'); if (revealEls.length > 0 && 'IntersectionObserver' in window) { var revealObserver = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) { entry.target.classList.add('visible'); revealObserver.unobserve(entry.target); } }); }, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' }); revealEls.forEach(function (el) { revealObserver.observe(el); }); } else { // Fallback: show all immediately revealEls.forEach(function (el) { el.classList.add('visible'); }); } /* --------------- Contact Form Handled by Zoho CRM WebToLead embed (see contact.html). No additional JS needed here. --------------- */ /* --------------- Sticky Header Highlight on Current Section (optional) --------------- */ var sections = document.querySelectorAll('section[id]'); if (sections.length > 0) { window.addEventListener('scroll', function () { var scrollPos = window.scrollY + (navbar ? navbar.offsetHeight + 40 : 100); sections.forEach(function (section) { var top = section.offsetTop; var bottom = top + section.offsetHeight; if (scrollPos >= top && scrollPos < bottom) { document.querySelectorAll('.nav-link').forEach(function (link) { link.classList.remove('active'); if (link.getAttribute('href')=== '#' + section.id) { link.classList.add('active'); } }); } }); }, { passive: true }); } });