document.addEventListener('DOMContentLoaded', () => { initThemeToggle(); initSmoothScroll(); initFormValidation(); initAnimations(); initScrollProgress(); initStarryBackground(); initCookieConsent(); }); function initThemeToggle() { const themeToggle = document.getElementById('theme-toggle'); const body = document.body; const icon = themeToggle.querySelector('i'); // Check for saved theme preference const savedTheme = localStorage.getItem('theme'); if (savedTheme) { body.className = savedTheme; updateIcon(savedTheme === 'light-theme'); } // Theme toggle functionality themeToggle.addEventListener('click', () => { const isLight = body.classList.toggle('light-theme'); localStorage.setItem('theme', isLight ? 'light-theme' : 'dark-theme'); updateIcon(isLight); }); function updateIcon(isLight) { icon.className = isLight ? 'fas fa-moon' : 'fas fa-sun'; icon.style.animation = 'iconBounce 0.5s ease'; setTimeout(() => icon.style.animation = '', 500); } } function initScrollProgress() { const progressBar = document.querySelector('.scroll-progress'); window.addEventListener('scroll', () => { const winScroll = document.body.scrollTop || document.documentElement.scrollTop; const height = document.documentElement.scrollHeight - document.documentElement.clientHeight; const scrolled = (winScroll / height) * 100; progressBar.style.transform = `scaleX(${scrolled / 100})`; }); } function initStarryBackground() { const starsContainer = document.querySelector('.stars'); const numStars = 50; // Create stars for (let i = 0; i < numStars; i++) { const star = document.createElement('div'); star.className = 'star'; star.style.width = Math.random() * 3 + 'px'; star.style.height = star.style.width; star.style.left = Math.random() * 100 + '%'; star.style.top = Math.random() * 100 + '%'; star.style.animationDelay = Math.random() * 2 + 's'; starsContainer.appendChild(star); } } function initSmoothScroll() { document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { const headerOffset = 60; const elementPosition = target.getBoundingClientRect().top; const offsetPosition = elementPosition + window.pageYOffset - headerOffset; window.scrollTo({ top: offsetPosition, behavior: 'smooth' }); } }); }); } function initFormValidation() { const form = document.querySelector('.contact-form'); if (!form) return; const inputs = { name: form.querySelector('#name'), email: form.querySelector('#email'), phone: form.querySelector('#phone'), message: form.querySelector('#message') }; const submitButton = form.querySelector('.submit-button'); const buttonText = submitButton.querySelector('.button-text'); const loadingSpinner = submitButton.querySelector('.loading-spinner'); const feedbackDiv = form.querySelector('.form-feedback'); const messageCounter = form.querySelector('.char-counter'); // Initialize character counter if (inputs.message && messageCounter) { const maxLength = inputs.message.getAttribute('maxlength'); updateCharCounter(inputs.message.value.length, maxLength); inputs.message.addEventListener('input', (e) => { const length = e.target.value.length; updateCharCounter(length, maxLength); }); } function updateCharCounter(current, max) { messageCounter.textContent = `${current} / ${max}`; // Add visual feedback when approaching limit if (current >= max * 0.9) { messageCounter.style.color = 'var(--error-red)'; } else if (current >= max * 0.8) { messageCounter.style.color = 'var(--warning-yellow)'; } else { messageCounter.style.color = 'var(--text-muted)'; } } // Real-time validation Object.values(inputs).forEach(input => { if (!input) return; ['input', 'blur'].forEach(eventType => { input.addEventListener(eventType, () => { validateInput(input); updateSubmitButton(); }); }); // Add focus effects input.addEventListener('focus', () => { input.closest('.input-wrapper').classList.add('focused'); }); input.addEventListener('blur', () => { input.closest('.input-wrapper').classList.remove('focused'); }); }); function validateInput(input) { if (!input.required && input.value === '') { // Optional fields are valid when empty const wrapper = input.closest('.input-wrapper'); wrapper.classList.remove('invalid', 'valid'); return true; } const isValid = input.checkValidity(); const wrapper = input.closest('.input-wrapper'); wrapper.classList.toggle('invalid', !isValid); wrapper.classList.toggle('valid', isValid); // Show validation message let errorMessage = ''; if (!isValid) { if (input.validity.valueMissing) { errorMessage = 'Dieses Feld ist erforderlich'; } else if (input.validity.typeMismatch) { errorMessage = 'Bitte geben Sie ein gültiges Format ein'; } else if (input.validity.patternMismatch) { errorMessage = input.title; } else if (input.validity.tooShort) { const minLength = input.getAttribute('minlength'); errorMessage = `Mindestens ${minLength} Zeichen erforderlich`; } } let errorElement = wrapper.querySelector('.error-message'); if (!errorElement && errorMessage) { errorElement = document.createElement('div'); errorElement.className = 'error-message'; wrapper.appendChild(errorElement); } if (errorElement) { if (errorMessage) { errorElement.textContent = errorMessage; errorElement.style.display = 'flex'; } else { errorElement.style.display = 'none'; } } return isValid; } function updateSubmitButton() { const requiredInputs = Object.values(inputs).filter(input => input && input.required); const isFormValid = requiredInputs.every(input => input.checkValidity()); submitButton.disabled = !isFormValid; // Visual feedback submitButton.classList.toggle('ready', isFormValid); } form.addEventListener('submit', async (e) => { e.preventDefault(); const requiredInputs = Object.values(inputs).filter(input => input && input.required); if (!requiredInputs.every(input => validateInput(input))) { showFeedback('Bitte füllen Sie alle Pflichtfelder korrekt aus.', false); return; } // Show loading state submitButton.disabled = true; buttonText.style.opacity = '0'; loadingSpinner.style.display = 'block'; try { // Simulate API call await new Promise(resolve => setTimeout(resolve, 1500)); showFeedback('Vielen Dank für Ihre Nachricht! Wir werden uns in Kürze bei Ihnen melden.', true); form.reset(); // Reset validation states Object.values(inputs).forEach(input => { if (!input) return; const wrapper = input.closest('.input-wrapper'); wrapper.classList.remove('valid', 'invalid', 'focused'); const errorElement = wrapper.querySelector('.error-message'); if (errorElement) { errorElement.style.display = 'none'; } }); // Reset character counter if (messageCounter) { updateCharCounter(0, inputs.message.getAttribute('maxlength')); } } catch (error) { showFeedback('Es ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.', false); } finally { // Reset button state submitButton.disabled = false; buttonText.style.opacity = '1'; loadingSpinner.style.display = 'none'; submitButton.classList.remove('ready'); } }); function showFeedback(message, isSuccess) { feedbackDiv.textContent = message; feedbackDiv.className = `form-feedback ${isSuccess ? 'success' : 'error'}`; feedbackDiv.style.display = 'block'; // Scroll feedback into view feedbackDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); // Animate feedback feedbackDiv.style.animation = 'scaleIn 0.3s ease forwards'; // Auto-hide feedback after 5 seconds setTimeout(() => { feedbackDiv.style.animation = 'fadeOut 0.3s ease forwards'; setTimeout(() => { feedbackDiv.style.display = 'none'; }, 300); }, 5000); } } function initAnimations() { const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); // Add stagger effect to child elements const children = entry.target.querySelectorAll('.animate-stagger'); children.forEach((child, index) => { child.style.animationDelay = `${index * 0.1}s`; child.classList.add('visible'); }); observer.unobserve(entry.target); } }); }, observerOptions); // Observe elements document.querySelectorAll('.service-card, .vision-item, .fade-in-scroll').forEach(el => { observer.observe(el); }); } function initCookieConsent() { const cookieConsent = document.getElementById('cookie-consent'); const acceptButton = document.getElementById('accept-cookies'); const rejectButton = document.getElementById('reject-cookies'); // Check if user has already made a choice const cookieChoice = localStorage.getItem('cookieChoice'); if (!cookieChoice) { setTimeout(() => { cookieConsent.style.display = 'flex'; cookieConsent.style.animation = 'slideUp 0.5s ease forwards'; }, 1000); } acceptButton.addEventListener('click', () => { localStorage.setItem('cookieChoice', 'accepted'); hideCookieConsent(); }); rejectButton.addEventListener('click', () => { localStorage.setItem('cookieChoice', 'rejected'); hideCookieConsent(); }); function hideCookieConsent() { cookieConsent.style.animation = 'slideDown 0.5s ease forwards'; setTimeout(() => { cookieConsent.style.display = 'none'; }, 500); } }