// ChronoBirth Magic - Interactive Birth Date Form // ==================== CONFIGURATION ==================== const CONFIG = { zodiacSigns: [ { name: 'Capricorn', symbol: '♑', element: 'Earth', start: [12, 22], end: [1, 19] }, { name: 'Aquarius', symbol: '♒', element: 'Air', start: [1, 20], end: [2, 18] }, { name: 'Pisces', symbol: '♓', element: 'Water', start: [2, 19], end: [3, 20] }, { name: 'Aries', symbol: '♈', element: 'Fire', start: [3, 21], end: [4, 19] }, { name: 'Taurus', symbol: '♉', element: 'Earth', start: [4, 20], end: [5, 20] }, { name: 'Gemini', symbol: '♊', element: 'Air', start: [5, 21], end: [6, 20] }, { name: 'Cancer', symbol: '♋', element: 'Water', start: [6, 21], end: [7, 22] }, { name: 'Leo', symbol: '♌', element: 'Fire', start: [7, 23], end: [8, 22] }, { name: 'Virgo', symbol: '♍', element: 'Earth', start: [8, 23], end: [9, 22] }, { name: 'Libra', symbol: '♎', element: 'Air', start: [9, 23], end: [10, 22] }, { name: 'Scorpio', symbol: '♏', element: 'Water', start: [10, 23], end: [11, 21] }, { name: 'Sagittarius', symbol: '♐', element: 'Fire', start: [11, 22], end: [12, 21] } ], seasons: [ { name: 'Spring', start: [3, 20], end: [6, 20], icon: '🌸' }, { name: 'Summer', start: [6, 21], end: [9, 22], icon: '☀️' }, { name: 'Autumn', start: [9, 23], end: [12, 20], icon: '🍂' }, { name: 'Winter', start: [12, 21], end: [3, 19], icon: '❄️' } ], dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] }; // ==================== STATE ==================== let currentStep = 1; const totalSteps = 3; let birthData = { day: null, month: null, year: null, time: null, timezone: 'UTC' }; // ==================== DOM ELEMENTS ==================== const elements = { form: document.getElementById('birthForm'), formCard: document.getElementById('birthFormCard'), resultsCard: document.getElementById('resultsCard'), prevBtn: document.getElementById('prevBtn'), nextBtn: document.getElementById('nextBtn'), submitBtn: document.getElementById('submitBtn'), themeToggle: document.getElementById('themeToggle'), toast: document.getElementById('toast'), confettiCanvas: document.getElementById('confettiCanvas') }; // ==================== INITIALIZATION ==================== document.addEventListener('DOMContentLoaded', () => { initializeEventListeners(); initializeTheme(); updateStepIndicators(); feather.replace(); }); // ==================== EVENT LISTENERS ==================== function initializeEventListeners() { // Navigation elements.nextBtn.addEventListener('click', nextStep); elements.prevBtn.addEventListener('click', prevStep); elements.form.addEventListener('submit', handleSubmit); // Theme elements.themeToggle.addEventListener('click', toggleTheme); // Preset buttons document.querySelectorAll('.preset-btn').forEach(btn => { btn.addEventListener('click', () => applyPreset(btn.dataset.preset)); }); // Input validation and auto-advance ['day', 'month', 'year'].forEach(id => { const input = document.getElementById(id); input.addEventListener('input', handleDateInput); input.addEventListener('blur', validateDate); }); // Reset document.getElementById('resetBtn').addEventListener('click', resetForm); // Share & Save document.getElementById('shareBtn').addEventListener('click', shareResults); document.getElementById('saveBtn').addEventListener('click', saveResults); } // ==================== THEME ==================== function initializeTheme() { const savedTheme = localStorage.getItem('chronobirth-theme'); const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; if (savedTheme === 'dark' || (!savedTheme && prefersDark)) { document.documentElement.classList.add('dark'); } } function toggleTheme() { const isDark = document.documentElement.classList.toggle('dark'); localStorage.setItem('chronobirth-theme', isDark ? 'dark' : 'light'); // Animate icon const icon = elements.themeToggle.querySelector('i'); icon.style.transform = 'rotate(360deg)'; setTimeout(() => { icon.style.transform = ''; feather.replace(); }, 300); } // ==================== STEP NAVIGATION ==================== function nextStep() { if (!validateCurrentStep()) return; if (currentStep < totalSteps) { goToStep(currentStep + 1); } } function prevStep() { if (currentStep > 1) { goToStep(currentStep - 1); } } function goToStep(step) { // Update form steps document.querySelectorAll('.form-step').forEach((el, i) => { el.classList.remove('active', 'prev'); if (i + 1 === step) { el.classList.add('active'); } else if (i + 1 < step) { el.classList.add('prev'); } }); currentStep = step; updateStepIndicators(); updateNavigationButtons(); // Update preview on step 3 if (step === 3) { updateZodiacPreview(); } } function updateStepIndicators() { document.querySelectorAll('.step-indicator').forEach((el, i) => { el.classList.remove('active', 'completed'); if (i + 1 === currentStep) { el.classList.add('active'); } else if (i + 1 < currentStep) { el.classList.add('completed'); el.querySelector('.step-number').innerHTML = ''; feather.replace(); } else { el.querySelector('.step-number').textContent = i + 1; } }); } function updateNavigationButtons() { elements.prevBtn.classList.toggle('hidden', currentStep === 1); elements.nextBtn.classList.toggle('hidden', currentStep === totalSteps); elements.submitBtn.classList.toggle('hidden', currentStep !== totalSteps); } function validateCurrentStep() { const currentStepEl = document.querySelector(`.form-step[data-step="${currentStep}"]`); const inputs = currentStepEl.querySelectorAll('input, select'); let isValid = true; inputs.forEach(input => { if (!input.checkValidity()) { input.reportValidity(); isValid = false; } }); return isValid; } // ==================== INPUT HANDLING ==================== function handleDateInput(e) { const input = e.target; const value = input.value; // Auto-advance for day and month if (input.id === 'day' && value.length === 2) { document.getElementById('month').focus(); } else if (input.id === 'year' && value.length === 4) { document.getElementById('time').focus(); } // Store data birthData[input.id] = input.id === 'month' ? parseInt(value) : value; } function validateDate() { const day = parseInt(document.getElementById('day').value); const month = parseInt(document.getElementById('month').value); const year = parseInt(document.getElementById('year').value); if (!day || !month || !year) return; const date = new Date(year, month - 1, day); const isValid = date.getDate() === day && date.getMonth() === month - 1; if (!isValid) { showToast('Please enter a valid date!', 'error'); } } function applyPreset(preset) { const now = new Date(); const year = now.getFullYear(); const presets = { today: { day: now.getDate(), month: now.getMonth() + 1, year: year }, newyear: { day: 1, month: 1, year: year }, leap: { day: 29, month: 2, year: 2020 }, halloween: { day: 31, month: 10, year: year } }; const data = presets[preset]; if (data) { document.getElementById('day').value = data.day.toString().padStart(2, '0'); document.getElementById('month').value = data.month; document.getElementById('year').value = data.year; birthData.day = data.day; birthData.month = data.month; birthData.year = data.year; // Visual feedback document.querySelectorAll('.preset-btn').forEach(btn => { btn.style.transform = btn.dataset.preset === preset ? 'scale(1.05)' : ''; btn.style.background = btn.dataset.preset === preset ? '#fef2f2' : ''; }); } } // ==================== ZODIAC & CALCULATIONS ==================== function getZodiacSign(day, month) { for (const sign of CONFIG.zodiacSigns) { const [startMonth, startDay] = sign.start; const [endMonth, endDay] = sign.end; if ((month === startMonth && day >= startDay) || (month === endMonth && day <= endDay) || (startMonth > endMonth && (month === startMonth && day >= startDay || month === endMonth && day <= endDay))) { return sign; } } return CONFIG.zodiacSigns[0]; } function getSeason(day, month) { for (const season of CONFIG.seasons) { const [startMonth, startDay] = season.start; const [endMonth, endDay] = season.end; const dateNum = month * 100 + day; const startNum = startMonth * 100 + startDay; const endNum = endMonth * 100 + endDay; if (startNum > endNum) { // Winter spans year boundary if (dateNum >= startNum || dateNum <= endNum) return season; } else { if (dateNum >= startNum && dateNum <= endNum) return season; } } return CONFIG.seasons[3]; // Default to winter } function calculateAge(birthDate) { const now = new Date(); const diff = now - birthDate; const years = Math.floor(diff / (365.25 * 24 * 60 * 60 * 1000)); const months = Math.floor((diff % (365.25 * 24 * 60 * 60 * 1000)) / (30.44 * 24 * 60 * 60 * 1000)); const days = Math.floor((diff % (30.44 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000)); const hours = Math.floor((diff % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000)); return { years, months, days, hours, totalDays: Math.floor(diff / (24 * 60 * 60 * 1000)) }; } function getNextBirthday(birthDate) { const now = new Date(); const currentYear = now.getFullYear(); let nextBirthday = new Date(currentYear, birthDate.getMonth(), birthDate.getDate()); if (nextBirthday < now) { nextBirthday.setFullYear(currentYear + 1); } const daysUntil = Math.ceil((nextBirthday - now) / (24 * 60 * 60 * 1000)); return { date: nextBirthday, daysUntil }; } function updateZodiacPreview() { const day = parseInt(document.getElementById('day').value); const month = parseInt(document.getElementById('month').value); const year = parseInt(document.getElementById('year').value); if (!day || !month || !year) return; const zodiac = getZodiacSign(day, month); const birthDate = new Date(year, month - 1, day); const dayOfWeek = CONFIG.dayNames[birthDate.getDay()]; const age = calculateAge(birthDate); // Update preview document.getElementById('zodiacIcon').textContent = zodiac.symbol; document.getElementById('zodiacName').textContent = zodiac.name; document.getElementById('zodiacDates').textContent = `${zodiac.element} Element • ${zodiac.start[0]}/${zodiac.start[1]} - ${zodiac.end[0]}/${zodiac.end[1]}`; document.getElementById('dayOfWeek').textContent = dayOfWeek; document.getElementById('agePreview').textContent = `${age.years} years`; // Animate const preview = document.getElementById('zodiacPreview'); preview.style.opacity = '1'; preview.style.transform = 'scale(1.02)'; } // ==================== FORM SUBMISSION ==================== function handleSubmit(e) { e.preventDefault(); // Gather all data const day = parseInt(document.getElementById('day').value); const month = parseInt(document.getElementById('month').value); const year = parseInt(document.getElementById('year').value); const time = document.getElementById('time').value; const timezone = document.getElementById('timezone').value; const birthDate = new Date(year, month - 1, day); const zodiac = getZodiacSign(day, month); const season = getSeason(day, month); const age = calculateAge(birthDate); const nextBirthday = getNextBirthday(birthDate); // Calculate life progress (assuming 80 years) const lifeProgress = Math.min((age.years / 80) * 100, 100); // Display results displayResults({ birthDate, day, month, year, time, timezone, zodiac, season, age, nextBirthday, lifeProgress, dayOfWeek: CONFIG.dayNames[birthDate.getDay()] }); } function displayResults(data) { // Hide form, show results elements.formCard.style.display = 'none'; elements.resultsCard.classList.remove('hidden'); // Populate results document.getElementById('resultZodiacIcon').textContent = data.zodiac.symbol; document.getElementById('resultDate').textContent = `${data.dayOfWeek}, ${data.month}/${data.day}/${data.year}`; document.getElementById('resultZodiac').textContent = data.zodiac.name; document.getElementById('resultZodiacElement').textContent = `${data.zodiac.element} Element`; document.getElementById('resultAge').textContent = `${data.age.years} years`; document.getElementById('resultAgeDetailed').textContent = `${data.age.months}m ${data.age.days}d ${data.age.hours}h old`; document.getElementById('resultDayName').textContent = data.dayOfWeek; document.getElementById('resultDayType').textContent = getDayType(data.dayOfWeek); document.getElementById('resultNextBirthday').textContent = data.nextBirthday.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); document.getElementById('resultDaysUntil').textContent = `${data.nextBirthday.daysUntil} days until party time!`; document.getElementById('resultSeason').textContent = `${data.season.icon} ${data.season.name}`; document.getElementById('resultSeasonDates').textContent = getSeasonPeriod(data.season); // Animate progress bar setTimeout(() => { document.getElementById('lifeProgressBar').style.width = `${data.lifeProgress}%`; }, 300); document.getElementById('resultLifeProgress').textContent = `${data.lifeProgress.toFixed(1)}% of estimated lifespan`; // Generate fun facts generateFunFacts(data); // Trigger confetti triggerConfetti(); // Scroll to top window.scrollTo({ top: 0, behavior: 'smooth' }); } function getDayType(day) { const types = { 'Monday': 'Moon Day 🌙', 'Tuesday': 'Mars Day ♂️', 'Wednesday': 'Mercury Day ☿️', 'Thursday': 'Jupiter Day ♃', 'Friday': 'Venus Day ♀️', 'Saturday': 'Saturn Day ♄', 'Sunday': 'Sun Day ☉' }; return types[day] || 'Special Day'; } function getSeasonPeriod(season) { const [startM, startD] = season.start; const [endM, endD] = season.end; return `${startM}/${startD} - ${endM}/${endD}`; } function generateFunFacts(data) { const facts = [ `You've been alive for approximately ${data.age.totalDays.toLocaleString()} days`, `Your heart has beaten about ${(data.age.totalDays * 115000).toLocaleString()} times`, `You've taken roughly ${(data.age.totalDays * 23000).toLocaleString()} breaths`, `The moon has orbited Earth ${Math.floor(data.age.years * 12.4)} times since you were born`, `Your zodiac sign ${data.zodiac.name} is known for being ${getZodiacTrait(data.zodiac.name)}`, `People born on ${data.dayOfWeek}s are said to be ${getDayTrait(data.dayOfWeek)}` ]; // Add historical events const historicalEvents = getHistoricalEvents(data.year); facts.push(...historicalEvents); const list = document.getElementById('funFactsList'); list.innerHTML = facts.map(fact => `