Select Phone, Zoom, or In-Person Consultations Below
Click below to lock in your preferred consultation type. Whether it’s a quick call, a face-to-face on Zoom, or in person at the studio—this is where it gets good
// File Name: custom-animations.js
function setupAnimatedHeadingsOnScroll() {
// --- This is the reusable part for all your animations ---
// Add the unique class name for EACH of your animations here, separated by a comma.
const animatedContainers = document.querySelectorAll('.color-heading-container, .princeton-animation-container, .hairline-animation-container, .no-location-animation-container');
if (animatedContainers.length === 0) return;
// --- Universal Logic for splitting text into words ---
// This part works for all the animations we've built.
animatedContainers.forEach(container => {
if (container.hasAttribute('data-animation-prepared')) return;
container.setAttribute('data-animation-prepared', 'true');
const elementsToSplit = container.querySelectorAll('[data-split-words]');
elementsToSplit.forEach(element => {
const text = element.textContent.trim();
const words = text.split(' ');
element.innerHTML = '';
const redWords = ["Bad", "Results"];
let animationIndex = 0;
for (let i = 0; i < words.length; i++) {
let currentWord = words[i];
let isSpecialPhrase = false;
if (words[i] === "#1" && i + 1 < words.length && words[i+1] === "Priority") {
currentWord = "#1 Priority";
isSpecialPhrase = true;
i++;
}
const wrapper = document.createElement('div');
wrapper.classList.add('word-wrapper');
wrapper.style.animationDelay = `${animationIndex * 0.15 + 0.1}s`;
const wordSpan = document.createElement('span');
wordSpan.classList.add('word');
wordSpan.textContent = currentWord;
if (redWords.includes(currentWord) || isSpecialPhrase) {
wordSpan.classList.add('text-red');
}
if (isSpecialPhrase) {
wordSpan.classList.add('slide-italic-effect');
wrapper.classList.add('zoom-wrapper');
}
wrapper.appendChild(wordSpan);
element.appendChild(wrapper);
animationIndex++;
}
});
});
// --- Universal Logic for triggering animation on scroll ---
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('is-in-view');
observer.unobserve(entry.target); // Animate only once
}
});
}, {
threshold: 0.1 // Trigger when 10% of the element is visible
});
// Tell the observer to watch each container
animatedContainers.forEach(container => {
observer.observe(container);
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setupAnimatedHeadingsOnScroll);
} else {
setupAnimatedHeadingsOnScroll();
}