// 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 => `
  • ${fact}
  • `).join(''); } function getZodiacTrait(sign) { const traits = { 'Aries': 'bold and ambitious', 'Taurus': 'reliable and patient', 'Gemini': 'adaptable and outgoing', 'Cancer': 'intuitive and sentimental', 'Leo': 'confident and charismatic', 'Virgo': 'analytical and hardworking', 'Libra': 'diplomatic and gracious', 'Scorpio': 'passionate and resourceful', 'Sagittarius': 'generous and idealistic', 'Capricorn': 'responsible and disciplined', 'Aquarius': 'progressive and original', 'Pisces': 'compassionate and artistic' }; return traits[sign] || 'unique and special'; } function getDayTrait(day) { const traits = { 'Monday': 'creative and intuitive', 'Tuesday': 'energetic and courageous', 'Wednesday': 'communicative and curious', 'Thursday': 'expansive and optimistic', 'Friday': 'loving and harmonious', 'Saturday': 'wise and disciplined', 'Sunday': 'vital and radiant' }; return traits[day] || 'wonderful'; } function getHistoricalEvents(year) { const events = { 2024: ['AI continues to transform technology', 'Space exploration reaches new heights'], 2023: ['ChatGPT became widely popular', 'James Webb Space Telescope discoveries'], 2020: ['COVID-19 pandemic changed the world', 'SpaceX launched first crewed mission'], 2000: ['Y2K bug was avoided', 'Human genome project completed'], 1990: ['World Wide Web was invented', 'Hubble Space Telescope launched'], 1980: ['Pac-Man was released', 'CNN launched as first 24-hour news channel'], 1970: ['First Earth Day celebrated', 'Floppy disk invented'], 1969: ['Humans first landed on the Moon', 'Internet precursor ARPANET launched'], 1960: ['Laser was invented', 'First weather satellite launched'] }; // Find closest year with events const years = Object.keys(events).map(Number).sort((a, b) => b - a); const closestYear = years.find(y => y <= year) || years[years.length - 1]; return events[closestYear] ? events[closestYear].map(e => `In ${closestYear}: ${e}`) : []; } // ==================== ACTIONS ==================== function resetForm() { elements.form.reset(); currentStep = 1; birthData = { day: null, month: null, year: null, time: null, timezone: 'UTC' }; elements.resultsCard.classList.add('hidden'); elements.formCard.style.display = 'block'; document.getElementById('lifeProgressBar').style.width = '0%'; goToStep(1); window.scrollTo({ top: 0, behavior: 'smooth' }); } async function shareResults() { const text = `I discovered my cosmic birthday profile with ChronoBirth! 🎂✨`; try { if (navigator.share) { await navigator.share({ title: 'My ChronoBirth Profile', text }); } else { await navigator.clipboard.writeText(text + ' https://chronobirth.magic'); showToast('Link copied to clipboard!'); } } catch (err) { showToast('Unable to share', 'error'); } } function saveResults() { // Create a simple text representation const results = document.getElementById('resultsCard').innerText; const blob = new Blob([results], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'my-chronobirth-profile.txt'; a.click(); URL.revokeObjectURL(url); showToast('Profile saved!'); } function showToast(message, type = 'success') { const toast = elements.toast; const toastMessage = document.getElementById('toastMessage'); toastMessage.textContent = message; toast.querySelector('i').className = type === 'error' ? 'w-5 h-5 text-rose-500' : 'w-5 h-5 text-emerald-400 dark:text-emerald-500'; toast.style.transform = 'translateX(-50%) translateY(0)'; toast.style.opacity = '1'; setTimeout(() => { toast.style.transform = 'translateX(-50%) translateY(20px)'; toast.style.opacity = '0'; }, 3000); } // ==================== CONFETTI ==================== function triggerConfetti() { const canvas = elements.confettiCanvas; const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const particles = []; const colors = ['#ef4444', '#f59e0b', '#10b981', '#8b5cf6', '#ec4899', '#06b6d4']; // Create particles for (let i = 0; i < 150; i++) { particles.push({ x: canvas.width / 2, y: canvas.height / 2, vx: (Math.random() - 0.5) * 15, vy: (Math.random() - 0.5) * 15 - 5, color: colors[Math.floor(Math.random() * colors.length)], size: Math.random() * 8 + 4, rotation: Math.random() * 360, rotationSpeed: (Math.random() - 0.5) * 10, gravity: 0.3, drag: 0.98 }); } let animationId; function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); let activeParticles = 0; particles.forEach(p => { if (p.y < canvas.height + 50) { activeParticles++; p.x += p.vx; p.y += p.vy; p.vy += p.gravity; p.vx *= p.drag; p.vy *= p.drag; p.rotation += p.rotationSpeed; ctx.save(); ctx.translate(p.x, p.y); ctx.rotate(p.rotation * Math.PI / 180); ctx.fillStyle = p.color; ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size); ctx.restore(); } }); if (activeParticles > 0) { animationId = requestAnimationFrame(animate); } else { ctx.clearRect(0, 0, canvas.width, canvas.height); } } animate(); // Cleanup on resize window.addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }, { once: true }); } // ==================== UTILITY ==================== // Add smooth scroll behavior document.documentElement.style.scrollBehavior = 'smooth'; // Prevent form submission on Enter for multi-step document.addEventListener('keydown', (e) => { if (e.key === 'Enter' && currentStep < totalSteps) { e.preventDefault(); nextStep(); } });