(function ($) {
const STATE = {
boundAnchors: false,
boundUrgency: false,
countdownInterval: null,
timelineBound: false,
timelineObserved: false
};
// -------- Utils
function getCountdownEnd() {
// 1) Tentar sua data hardcoded
let end = new Date('2024-12-31T23:59:59');
const now = Date.now();
// 2) Se estiver no passado ou inválida, tentar pegar do atributo data-countdown-end
if (isNaN(end.getTime()) || end.getTime() <= now) {
const dataEl = document.querySelector('[data-countdown-end]');
if (dataEl) {
const parsed = new Date(dataEl.getAttribute('data-countdown-end'));
if (!isNaN(parsed.getTime()) && parsed.getTime() > now) {
end = parsed;
} else {
// 3) Fallback: 31/12 do ano atual, 23:59:59
const y = new Date().getFullYear();
end = new Date(y, 11, 31, 23, 59, 59);
}
} else {
const y = new Date().getFullYear();
end = new Date(y, 11, 31, 23, 59, 59);
}
}
return end;
}
// -------- Countdown
function renderCountdown(distance) {
const clamp = (v) => Math.max(0, v);
const days = clamp(Math.floor(distance / (1000 * 60 * 60 * 24)));
const hours = clamp(Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)));
const minutes = clamp(Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)));
const seconds = clamp(Math.floor((distance % (1000 * 60)) / 1000));
const setAll = (sel, val) =>
document.querySelectorAll(sel).forEach(el => (el.textContent = String(val).padStart(2, '0')));
setAll('#days, #final-days', days);
setAll('#hours, #final-hours', hours);
setAll('#minutes, #final-minutes', minutes);
setAll('#seconds, #final-seconds', seconds);
}
function bindCountdown() {
if (STATE.countdownInterval) return;
const endDate = getCountdownEnd();
function tick() {
const now = Date.now();
const distance = endDate.getTime() - now;
if (distance <= 0) {
renderCountdown(0);
clearInterval(STATE.countdownInterval);
STATE.countdownInterval = null;
return;
}
renderCountdown(distance);
}
tick();
STATE.countdownInterval = setInterval(tick, 1000);
}
// -------- Smooth scrolling (âncoras internas)
function bindAnchors() {
if (STATE.boundAnchors) return;
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
const href = this.getAttribute('href');
if (!href || href === '#') return;
const target = document.querySelector(href);
if (target) {
e.preventDefault();
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
});
STATE.boundAnchors = true;
}
// -------- Urgency effect (hover nos CTAs .pulse-btn)
function addUrgencyEffect() {
if (STATE.boundUrgency) return;
const ctaButtons = document.querySelectorAll('.pulse-btn');
ctaButtons.forEach(button => {
button.addEventListener('mouseenter', function () {
this.style.transform = 'scale(1.05)';
});
button.addEventListener('mouseleave', function () {
this.style.transform = 'scale(1)';
});
});
STATE.boundUrgency = true;
}
// -------- Timeline
function animateTimelineProgress() {
const progressFill = document.querySelector('.progress-fill');
if (!progressFill) return;
let progress = 0;
const interval = setInterval(() => {
progress += 2;
progressFill.style.width = progress + '%';
if (progress >= 100) clearInterval(interval);
}, 50);
}
function animateProgressRings() {
document.querySelectorAll('.progress-ring-circle').forEach((ring, index) => {
setTimeout(() => {
ring.style.strokeDashoffset = '0';
}, index * 200);
});
}
function initTimelineInteractivity() {
if (STATE.timelineBound) return;
const timelineItems = document.querySelectorAll('.timeline-item');
if (!timelineItems.length) {
STATE.timelineBound = true; // evita tentar de novo a cada init
return;
}
const total = timelineItems.length;
function activateModule(moduleNumber) {
const n = parseInt(moduleNumber, 10) || 1;
timelineItems.forEach(item => item.classList.remove('active'));
const selectedItem = document.querySelector(`[data-module="${n}"]`);
if (selectedItem) selectedItem.classList.add('active');
const progressFill = document.querySelector('.progress-fill');
if (progressFill && total > 0) {
const progress = (n / total) * 100;
progressFill.style.width = progress + '%';
}
}
timelineItems.forEach(item => {
item.addEventListener('click', function () {
const moduleNumber = this.getAttribute('data-module');
activateModule(moduleNumber);
});
item.addEventListener('mouseenter', function () {
this.style.transform = 'translateX(10px)';
});
item.addEventListener('mouseleave', function () {
this.style.transform = 'translateX(0)');
});
});
// Ativar primeiro módulo por padrão
activateModule(1);
STATE.timelineBound = true;
}
function initTimelineAnimation() {
if (STATE.timelineObserved) return;
const timelineSection = document.querySelector('.content-section');
if (!timelineSection) {
STATE.timelineObserved = true;
return;
}
if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animateTimelineProgress();
animateProgressRings();
observer.unobserve(entry.target);
}
});
}, { threshold: 0.3 });
observer.observe(timelineSection);
} else {
// Fallback se o browser não tiver IntersectionObserver
animateTimelineProgress();
animateProgressRings();
}
STATE.timelineObserved = true;
}
// -------- Elementor hook
$(window).on('elementor/frontend/init', function () {
// Rode uma vez globalmente (funciona no editor e no front)
bindCountdown();
bindAnchors();
addUrgencyEffect();
initTimelineInteractivity();
initTimelineAnimation();
});
})(jQuery);