/* ================================================================
LACEY LOBETTA & CO. — main.js
================================================================ */
/* ── Nav Fix for Squarespace CSS Transform ──────────────────────
CSS transform on the code block creates a new stacking context,
which breaks position:fixed. We move only the nav + mobile menu
to so they position correctly relative to the viewport.
────────────────────────────────────────────────────────────── */
(function () {
var nav = document.getElementById('navbar');
var menu = document.getElementById('mobileMenu');
if (!nav || !nav.closest('.sqs-block-code')) return;
document.body.insertBefore(nav, document.body.firstChild);
if (menu) document.body.insertBefore(menu, nav.nextSibling);
})();
document.addEventListener('DOMContentLoaded', () => {
/* ── Navbar Scroll Behavior ─────────────────────────────────── */
const navbar = document.getElementById('navbar');
const onScroll = () => {
navbar.classList.toggle('scrolled', window.scrollY > 60);
};
window.addEventListener('scroll', onScroll, { passive: true });
/* ── Mobile Menu ────────────────────────────────────────────── */
const hamburger = document.getElementById('hamburger');
const mobileMenu = document.getElementById('mobileMenu');
const menuClose = document.getElementById('menuClose');
const mmLinks = document.querySelectorAll('.mm-link');
const openMenu = () => { mobileMenu.classList.add('open'); document.body.style.overflow = 'hidden'; };
const closeMenu = () => { mobileMenu.classList.remove('open'); document.body.style.overflow = ''; };
hamburger.addEventListener('click', openMenu);
menuClose.addEventListener('click', closeMenu);
mmLinks.forEach(l => l.addEventListener('click', closeMenu));
/* ── Scroll Reveal ──────────────────────────────────────────── */
const reveals = document.querySelectorAll('.reveal');
const revealObserver = new IntersectionObserver((entries) => {
entries.forEach((entry, i) => {
if (entry.isIntersecting) {
// Stagger siblings slightly
const siblings = [...entry.target.parentElement.querySelectorAll('.reveal:not(.visible)')];
const idx = siblings.indexOf(entry.target);
setTimeout(() => {
entry.target.classList.add('visible');
}, Math.min(idx * 80, 400));
revealObserver.unobserve(entry.target);
}
});
}, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
reveals.forEach(el => revealObserver.observe(el));
/* ── Testimonial Slider ─────────────────────────────────────── */
const track = document.getElementById('testimonialTrack');
const dotsWrap = document.getElementById('tDots');
const prevBtn = document.getElementById('tPrev');
const nextBtn = document.getElementById('tNext');
if (track) {
const cards = track.querySelectorAll('.testimonial-card');
let current = 0;
let autoTimer = null;
// Build dots
cards.forEach((_, i) => {
const dot = document.createElement('div');
dot.className = 't-dot' + (i === 0 ? ' active' : '');
dot.addEventListener('click', () => goTo(i));
dotsWrap.appendChild(dot);
});
const goTo = (idx) => {
current = (idx + cards.length) % cards.length;
track.style.transform = `translateX(-${current * 100}%)`;
dotsWrap.querySelectorAll('.t-dot').forEach((d, i) => {
d.classList.toggle('active', i === current);
});
};
prevBtn.addEventListener('click', () => { goTo(current - 1); resetAuto(); });
nextBtn.addEventListener('click', () => { goTo(current + 1); resetAuto(); });
const startAuto = () => {
autoTimer = setInterval(() => goTo(current + 1), 5000);
};
const resetAuto = () => {
clearInterval(autoTimer);
startAuto();
};
startAuto();
}
/* ── Animated Counters ──────────────────────────────────────── */
const counters = document.querySelectorAll('.credbar__num');
const animateCounter = (el) => {
const raw = el.textContent.trim();
const match = raw.match(/^([\$]?)(\d+(?:\.\d+)?)([MK+x]*)$/);
if (!match) return;
const prefix = match[1];
const target = parseFloat(match[2]);
const suffix = match[3];
const em = el.querySelector('em');
const emText = em ? em.textContent : '';
const dur = 1800;
const start = performance.now();
const tick = (now) => {
const elapsed = now - start;
const progress = Math.min(elapsed / dur, 1);
const eased = 1 - Math.pow(1 - progress, 3); // ease-out-cubic
const value = eased * target;
const display = value >= 10 ? Math.round(value) : Math.round(value * 10) / 10;
el.textContent = `${prefix}${display}${suffix.replace(emText, '')}`;
if (em) {
const newEm = document.createElement('em');
newEm.textContent = emText;
el.appendChild(newEm);
}
if (progress < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
};
const counterObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animateCounter(entry.target);
counterObserver.unobserve(entry.target);
}
});
}, { threshold: 0.5 });
counters.forEach(c => counterObserver.observe(c));
/* ── Contact Form (placeholder behavior) ───────────────────── */
const form = document.getElementById('contactForm');
if (form) {
form.addEventListener('submit', (e) => {
e.preventDefault();
const btn = form.querySelector('button[type="submit"]');
btn.textContent = 'Application Submitted ✓';
btn.style.background = '#3a7d44';
btn.style.color = '#fff';
btn.disabled = true;
// Show a success note
const note = document.createElement('p');
note.style.cssText = 'color:#C9A96E;font-size:0.85rem;text-align:center;margin-top:12px;font-style:italic;';
note.textContent = 'Thank you. We review every application personally and will be in touch within 48 hours.';
form.querySelector('.form-submit').appendChild(note);
});
}
/* ── Smooth anchor scrolling with offset for fixed nav ─────── */
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', (e) => {
const target = document.querySelector(anchor.getAttribute('href'));
if (!target) return;
e.preventDefault();
const offset = navbar.offsetHeight + 20;
const top = target.getBoundingClientRect().top + window.scrollY - offset;
window.scrollTo({ top, behavior: 'smooth' });
});
});
/* ── Parallax tilt on service cards (desktop only) ──────────── */
if (window.matchMedia('(hover: hover)').matches) {
document.querySelectorAll('.service-card').forEach(card => {
card.addEventListener('mousemove', (e) => {
const rect = card.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width - 0.5;
const y = (e.clientY - rect.top) / rect.height - 0.5;
card.style.transform = `perspective(600px) rotateY(${x * 6}deg) rotateX(${-y * 6}deg) translateY(-4px)`;
});
card.addEventListener('mouseleave', () => {
card.style.transform = '';
});
});
}
});