const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

document.querySelectorAll('[data-modernization-search]').forEach((input) => {
    const section = input.closest('[data-modernization-topics]')?.parentElement || input.closest('section');
    const cards = [...(section?.querySelectorAll('[data-modernization-topic]') || [])];
    const empty = section?.querySelector('[data-modernization-empty]');

    input.addEventListener('input', () => {
        const query = input.value.trim().toLocaleLowerCase('de');
        let visible = 0;

        cards.forEach((card) => {
            const matches = !query || (card.dataset.searchText || '').includes(query);
            card.hidden = !matches;
            if (matches) visible++;
        });

        if (empty) empty.hidden = visible > 0;
    });
});

document.querySelectorAll('[data-modernization-slider]').forEach((slider) => {
    const slides = [...slider.querySelectorAll('[data-modernization-slide]')];
    const dots = [...slider.querySelectorAll('[data-modernization-dot]')];
    const previous = slider.querySelector('[data-modernization-previous]');
    const next = slider.querySelector('[data-modernization-next]');
    const toggle = slider.querySelector('[data-modernization-toggle]');
    const toggleIcon = toggle?.querySelector('[data-modernization-toggle-icon]');
    const current = slider.querySelector('[data-modernization-current]');

    if (!slides.length) return;

    let activeIndex = Math.max(0, slides.findIndex((slide) => slide.classList.contains('is-active')));
    let intervalId = null;
    let manuallyPaused = reducedMotion;
    let pointerInside = false;
    let focusInside = false;

    const stopAutoplay = () => {
        if (!intervalId) return;
        window.clearInterval(intervalId);
        intervalId = null;
    };

    const updatePauseControl = () => {
        if (!toggle) return;

        toggle.setAttribute('aria-pressed', String(manuallyPaused));
        toggle.setAttribute('aria-label', manuallyPaused ? 'Automatischen Wechsel starten' : 'Automatischen Wechsel pausieren');
        if (toggleIcon) toggleIcon.textContent = manuallyPaused ? '▶' : 'Ⅱ';
    };

    const showSlide = (index) => {
        activeIndex = (index + slides.length) % slides.length;

        slides.forEach((slide, slideIndex) => {
            const isActive = slideIndex === activeIndex;
            slide.classList.toggle('is-active', isActive);
            slide.setAttribute('aria-hidden', String(!isActive));
            slide.tabIndex = isActive ? 0 : -1;
        });

        dots.forEach((dot, dotIndex) => {
            const isActive = dotIndex === activeIndex;
            dot.classList.toggle('is-active', isActive);
            dot.setAttribute('aria-pressed', String(isActive));
        });

        if (current) current.textContent = String(activeIndex + 1).padStart(2, '0');
    };

    const startAutoplay = () => {
        stopAutoplay();
        if (reducedMotion || manuallyPaused || pointerInside || focusInside || document.hidden || slides.length < 2) return;

        intervalId = window.setInterval(() => showSlide(activeIndex + 1), 6500);
    };

    const selectSlide = (index) => {
        showSlide(index);
        startAutoplay();
    };

    previous?.addEventListener('click', () => selectSlide(activeIndex - 1));
    next?.addEventListener('click', () => selectSlide(activeIndex + 1));
    dots.forEach((dot, index) => dot.addEventListener('click', () => selectSlide(index)));

    toggle?.addEventListener('click', () => {
        manuallyPaused = !manuallyPaused;
        updatePauseControl();
        startAutoplay();
    });

    slider.addEventListener('pointerenter', () => {
        pointerInside = true;
        stopAutoplay();
    });
    slider.addEventListener('pointerleave', () => {
        pointerInside = false;
        startAutoplay();
    });
    slider.addEventListener('focusin', () => {
        focusInside = true;
        stopAutoplay();
    });
    slider.addEventListener('focusout', () => {
        window.requestAnimationFrame(() => {
            focusInside = slider.contains(document.activeElement);
            startAutoplay();
        });
    });
    slider.addEventListener('keydown', (event) => {
        if (!['ArrowLeft', 'ArrowRight'].includes(event.key)) return;
        event.preventDefault();
        selectSlide(activeIndex + (event.key === 'ArrowRight' ? 1 : -1));
        slides[activeIndex]?.focus({ preventScroll: true });
    });

    document.addEventListener('visibilitychange', startAutoplay);

    if (toggle && reducedMotion) toggle.hidden = true;
    showSlide(activeIndex);
    updatePauseControl();
    startAutoplay();
});

const header = document.querySelector('[data-site-header]');
const updateHeader = () => header?.classList.toggle('is-scrolled', window.scrollY > 18);
updateHeader();
window.addEventListener('scroll', updateHeader, { passive: true });

const navToggle = document.querySelector('[data-nav-toggle]');
const mobileNav = document.querySelector('[data-mobile-nav]');

const closeNavigation = () => {
    if (!navToggle || !mobileNav) return;
    navToggle.setAttribute('aria-expanded', 'false');
    mobileNav.hidden = true;
    document.body.classList.remove('nav-open');
};

navToggle?.addEventListener('click', () => {
    const willOpen = navToggle.getAttribute('aria-expanded') !== 'true';
    navToggle.setAttribute('aria-expanded', String(willOpen));
    mobileNav.hidden = !willOpen;
    document.body.classList.toggle('nav-open', willOpen);
});

mobileNav?.querySelectorAll('a').forEach((link) => link.addEventListener('click', closeNavigation));
window.addEventListener('keydown', (event) => event.key === 'Escape' && closeNavigation());

const availability = document.querySelector('[data-availability]');
const availabilityLabel = document.querySelector('[data-availability-label]');

const updateAvailability = () => {
    if (!availability || !availabilityLabel) return;

    const parts = new Intl.DateTimeFormat('de-LU', {
        timeZone: 'Europe/Luxembourg',
        weekday: 'short',
        hour: '2-digit',
        minute: '2-digit',
        hour12: false,
    }).formatToParts(new Date());
    const part = (type) => parts.find((item) => item.type === type)?.value;
    const weekday = part('weekday')?.replace('.', '').toLowerCase();
    const minutes = Number(part('hour')) * 60 + Number(part('minute'));
    const isWeekday = ['mo', 'di', 'mi', 'do', 'fr'].includes(weekday);
    const isOpen = isWeekday && minutes >= 8 * 60 && minutes < 16 * 60 + 30;

    availability.classList.toggle('is-open', isOpen);
    availabilityLabel.textContent = isOpen ? 'Jetzt erreichbar' : 'Mo–Fr · 08:00–16:30';
};
updateAvailability();
window.setInterval(updateAvailability, 60_000);

const revealItems = document.querySelectorAll('[data-reveal]');
revealItems.forEach((item) => {
    const delay = Number(item.dataset.revealDelay || 0);
    item.style.setProperty('--reveal-delay', `${delay}ms`);
});

if (reducedMotion || !('IntersectionObserver' in window)) {
    revealItems.forEach((item) => item.classList.add('is-visible'));
} else {
    const revealObserver = new IntersectionObserver((entries, observer) => {
        entries.forEach((entry) => {
            if (!entry.isIntersecting) return;
            entry.target.classList.add('is-visible');
            observer.unobserve(entry.target);
        });
    // Long article blocks can be several viewport heights tall on phones. A
    // 10% intersection threshold would then never be reached and keep the
    // complete article invisible. The small threshold still waits until the
    // element enters the viewport while remaining reliable for long content.
    }, { rootMargin: '0px 0px -8% 0px', threshold: 0.01 });
    revealItems.forEach((item) => revealObserver.observe(item));
}

document.querySelectorAll('[data-configurator-switcher]').forEach((switcher) => {
    const track = switcher.querySelector('.configurator-switcher-track');
    const active = switcher.querySelector('[aria-current="page"]');
    const toggle = switcher.querySelector('[data-configurator-switcher-toggle]');

    const closeSwitcher = () => {
        switcher.classList.remove('is-open');
        toggle?.setAttribute('aria-expanded', 'false');
    };

    toggle?.addEventListener('click', () => {
        const willOpen = !switcher.classList.contains('is-open');
        switcher.classList.toggle('is-open', willOpen);
        toggle.setAttribute('aria-expanded', String(willOpen));
    });

    document.addEventListener('click', (event) => {
        if (!switcher.classList.contains('is-open') || switcher.contains(event.target)) return;
        closeSwitcher();
    });
    switcher.addEventListener('keydown', (event) => {
        if (event.key !== 'Escape') return;
        closeSwitcher();
        toggle?.focus();
    });

    if (!track || !active) return;

    window.requestAnimationFrame(() => {
        if (track.scrollWidth <= track.clientWidth) return;
        const target = active.offsetLeft - ((track.clientWidth - active.offsetWidth) / 2);
        track.scrollTo({ left: Math.max(0, target), behavior: reducedMotion ? 'auto' : 'smooth' });
    });
});

document.querySelectorAll('[data-contact-form]').forEach((form) => {
    const status = form.querySelector('[data-contact-status]');
    const topics = [...form.querySelectorAll('input[name="topics[]"]')];
    const name = form.querySelector('input[name="name"]');
    const email = form.querySelector('input[name="email"]');
    const phone = form.querySelector('input[name="phone"]');
    const message = form.querySelector('textarea[name="message"]');
    const consent = form.querySelector('input[name="consent"]');
    const configuratorEntry = form.querySelector('[data-contact-configurator-entry]');
    const configuratorLink = form.querySelector('[data-contact-configurator-link]');
    const configuratorTitle = form.querySelector('[data-contact-configurator-title]');
    const configuratorUrls = JSON.parse(form.dataset.configuratorUrls || '{}');

    const updateConfiguratorEntry = (preferredTopic = null) => {
        const selected = preferredTopic
            ? (preferredTopic.checked && configuratorUrls[preferredTopic.value] ? preferredTopic : null)
            : topics.find((input) => input.checked && configuratorUrls[input.value]);
        if (!configuratorEntry || !configuratorLink) return;
        configuratorEntry.hidden = !selected;
        if (!selected) return;
        configuratorLink.href = configuratorUrls[selected.value];
        if (configuratorTitle) configuratorTitle.textContent = `${selected.value} Schritt für Schritt planen`;
    };

    const updateContactProgress = () => {
        const hasTopic = topics.some((input) => input.checked);
        const hasName = (name?.value.trim().length || 0) >= 2;
        const hasEmail = email?.validity.valid && email.value.trim().length > 0;
        const hasPhone = (phone?.value.trim().length || 0) >= 5;
        const hasMessage = (message?.value.trim().length || 0) >= 10;
        const hasConsent = Boolean(consent?.checked);

        let progress = (hasTopic ? 15 : 0) + (hasName ? 20 : 0) + (hasEmail ? 25 : 0) + (hasPhone ? 5 : 0) + (hasMessage ? 15 : 0) + (hasConsent ? 20 : 0);
        const isReady = hasName && hasEmail && hasConsent;
        if (isReady) progress = 100;

        form.style.setProperty('--contact-progress-scale', String(progress / 100));
        form.style.setProperty('--contact-progress-width', `${progress}%`);

        if (!status) return;
        status.textContent = isReady
            ? 'Bereit zum Senden'
            : hasName || hasEmail
                ? 'Kontaktdaten ergänzen'
                : hasTopic
                    ? 'Thema gewählt'
                    : 'Startklar';
    };

    topics.forEach((input) => input.addEventListener('change', () => {
        const label = input.closest('label');
        label?.classList.remove('is-just-selected');
        if (input.checked && !reducedMotion) {
            window.requestAnimationFrame(() => label?.classList.add('is-just-selected'));
            window.setTimeout(() => label?.classList.remove('is-just-selected'), 450);
        }
        updateContactProgress();
        updateConfiguratorEntry(input);
    }));

    [name, email, phone, message].forEach((field) => field?.addEventListener('input', updateContactProgress));
    consent?.addEventListener('change', updateContactProgress);
    updateContactProgress();
    updateConfiguratorEntry();
});

document.querySelectorAll('[data-contact-modes]').forEach((section) => {
    const tabs = [...section.querySelectorAll('[data-contact-mode]')];
    const panels = [...section.querySelectorAll('[data-contact-panel]')];

    const activateMode = (mode, moveFocus = false) => {
        tabs.forEach((tab) => {
            const active = tab.dataset.contactMode === mode;
            tab.setAttribute('aria-selected', String(active));
            tab.tabIndex = active ? 0 : -1;
        });
        panels.forEach((panel) => {
            const active = panel.dataset.contactPanel === mode;
            panel.hidden = !active;
            panel.classList.toggle('is-active', active);
        });

        if (moveFocus) {
            const heading = section.querySelector(`[data-contact-panel="${mode}"] h3`);
            heading?.setAttribute('tabindex', '-1');
            window.requestAnimationFrame(() => heading?.focus({ preventScroll: true }));
        }
    };

    tabs.forEach((tab, index) => {
        tab.addEventListener('click', () => activateMode(tab.dataset.contactMode, true));
        tab.addEventListener('keydown', (event) => {
            if (!['ArrowLeft', 'ArrowRight'].includes(event.key)) return;
            event.preventDefault();
            const direction = event.key === 'ArrowRight' ? 1 : -1;
            const next = tabs[(index + direction + tabs.length) % tabs.length];
            activateMode(next.dataset.contactMode);
            next.focus();
        });
    });

    const initial = tabs.find((tab) => tab.getAttribute('aria-selected') === 'true')?.dataset.contactMode || 'quick';
    activateMode(initial);
});

document.querySelectorAll('[data-quick-contact-form]').forEach((form) => {
    const status = form.querySelector('[data-quick-contact-status]');
    const progress = form.querySelector('[data-quick-contact-progress]');
    const requiredFields = [...form.querySelectorAll('input[required]:not([type="checkbox"]), textarea[required]')];
    const consent = form.querySelector('input[name="consent"]');

    const updateQuickProgress = () => {
        const completed = requiredFields.filter((field) => field.validity.valid && field.value.trim().length > 0).length;
        const total = requiredFields.length + 1;
        const percentage = Math.round(((completed + (consent?.checked ? 1 : 0)) / total) * 100);
        const isReady = completed === requiredFields.length && Boolean(consent?.checked);

        form.style.setProperty('--contact-progress-scale', String(percentage / 100));
        form.style.setProperty('--contact-progress-width', `${percentage}%`);
        if (progress) progress.style.width = `${percentage}%`;
        if (status) status.textContent = isReady ? 'Bereit zum Senden' : completed > 0 ? `${percentage} % ausgefüllt` : 'Startklar';
    };

    form.querySelectorAll('input, textarea').forEach((field) => {
        field.addEventListener(field.type === 'checkbox' ? 'change' : 'input', updateQuickProgress);
    });
    updateQuickProgress();
});

document.querySelectorAll('[data-light-demo]').forEach((demo) => {
    const button = demo.querySelector('[data-light-switch]');
    const status = demo.querySelector('[data-light-status]');
    const instruction = demo.querySelector('[data-light-instruction]');
    if (!button || !status || !instruction) return;

    button.addEventListener('click', () => {
        const willBeOn = !demo.classList.contains('is-on');
        demo.classList.toggle('is-on', willBeOn);
        button.setAttribute('aria-checked', String(willBeOn));
        status.textContent = willBeOn ? 'Licht an' : 'Licht aus';
        instruction.textContent = willBeOn ? 'Zum Ausschalten drücken' : 'Zum Einschalten drücken';
        button.querySelector('em').textContent = willBeOn ? 'AN' : 'AUS';
    });
});

document.querySelectorAll('[data-lighting-switch]').forEach((switcher) => {
    const motion = switcher.closest('[data-service-motion]');
    const buttons = [...switcher.querySelectorAll('[data-lighting-choice]')];
    const status = switcher.querySelector('[data-lighting-status]');
    if (!motion || !buttons.length) return;

    const labels = { off: 'Licht aus', warm: 'Warmweiß', cool: 'Kaltweiß' };
    const activate = (button) => {
        motion.dataset.lightingState = button.dataset.lightingChoice;
        buttons.forEach((candidate) => candidate.setAttribute('aria-pressed', String(candidate === button)));
        if (status) status.textContent = labels[button.dataset.lightingChoice];
    };

    buttons.forEach((button, index) => {
        button.addEventListener('click', () => activate(button));
        button.addEventListener('keydown', (event) => {
            if (!['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.key)) return;
            event.preventDefault();
            const direction = ['ArrowRight', 'ArrowDown'].includes(event.key) ? 1 : -1;
            const next = buttons[(index + direction + buttons.length) % buttons.length];
            activate(next);
            next.focus();
        });
    });
});

document.querySelectorAll('[data-service-flow]').forEach((flow) => {
    const steps = [...flow.querySelectorAll('[data-flow-step]')];
    steps.forEach((step) => {
        const activate = () => {
            steps.forEach((candidate) => candidate.classList.toggle('is-active', candidate === step));
            flow.style.setProperty('--active-step', String(steps.indexOf(step)));
        };
        step.addEventListener('mouseenter', activate);
        step.addEventListener('focus', activate);
        step.addEventListener('click', activate);
    });
});

document.querySelectorAll('[data-pv-audiences]').forEach((audienceSwitcher) => {
    const tabs = [...audienceSwitcher.querySelectorAll('[data-pv-tab]')];
    const panels = [...audienceSwitcher.querySelectorAll('[data-pv-panel]')];

    const activateAudience = (tab, moveFocus = false) => {
        tabs.forEach((candidate) => {
            const isActive = candidate === tab;
            candidate.setAttribute('aria-selected', String(isActive));
            candidate.tabIndex = isActive ? 0 : -1;
        });
        panels.forEach((panel) => {
            panel.hidden = panel.dataset.pvPanel !== tab.dataset.pvTab;
        });
        if (moveFocus) tab.focus();
    };

    tabs.forEach((tab, index) => {
        tab.addEventListener('click', () => activateAudience(tab));
        tab.addEventListener('keydown', (event) => {
            if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return;
            event.preventDefault();
            let nextIndex = index;
            if (event.key === 'ArrowRight') nextIndex = (index + 1) % tabs.length;
            if (event.key === 'ArrowLeft') nextIndex = (index - 1 + tabs.length) % tabs.length;
            if (event.key === 'Home') nextIndex = 0;
            if (event.key === 'End') nextIndex = tabs.length - 1;
            activateAudience(tabs[nextIndex], true);
        });
    });
});

document.querySelectorAll('[data-pv-cost-chart]').forEach((chart) => {
    const points = [...chart.querySelectorAll('.pv-cost-point')];
    const year = chart.querySelector('[data-cost-year]');
    const withoutPv = chart.querySelector('[data-cost-without]');
    const withPv = chart.querySelector('[data-cost-with]');
    const currency = new Intl.NumberFormat('de-LU', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 });

    const activatePoint = (point) => {
        points.forEach((candidate) => candidate.classList.toggle('is-active', candidate === point));
        if (year) year.textContent = point.dataset.year;
        if (withoutPv) withoutPv.textContent = currency.format(Number(point.dataset.withoutPv));
        if (withPv) withPv.textContent = currency.format(Number(point.dataset.withPv));
    };

    points.forEach((point) => {
        point.addEventListener('pointerenter', () => activatePoint(point));
        point.addEventListener('focus', () => activatePoint(point));
        point.addEventListener('keydown', (event) => {
            if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return;
            event.preventDefault();
            const current = points.indexOf(point);
            const next = event.key === 'Home' ? 0 : event.key === 'End' ? points.length - 1 : event.key === 'ArrowLeft' ? Math.max(0, current - 1) : Math.min(points.length - 1, current + 1);
            points[next].focus();
            activatePoint(points[next]);
        });
    });
});

document.querySelectorAll('[data-pv-calculator]').forEach((calculator) => {
    const power = calculator.querySelector('[data-calc-power]');
    const storage = calculator.querySelector('[data-calc-storage]');
    const selfUse = calculator.querySelector('[data-calc-self-use]');
    const investment = calculator.querySelector('[data-calc-investment]');
    const gridPrice = calculator.querySelector('[data-calc-grid-price]');
    const feedPrice = calculator.querySelector('[data-calc-feed-price]');
    const consumption = calculator.querySelector('[data-calc-consumption]');
    const roofArea = calculator.querySelector('[data-calc-roof-area]');
    const ev = calculator.querySelector('[data-calc-ev]');
    const heatPump = calculator.querySelector('[data-calc-heat-pump]');
    const storageModes = [...calculator.querySelectorAll('[data-calc-storage-mode]')];
    const yieldPerKwp = Number(calculator.dataset.yield || 0);
    const areaPerKwp = Number(calculator.dataset.areaPerKwp || 0);
    const costPerKwp = Number(calculator.dataset.costPerKwp || 2500);
    const costPerStorageKwh = Number(calculator.dataset.costPerStorageKwh || 1000);
    const evConsumption = Number(calculator.dataset.evConsumption || 2500);
    const heatPumpConsumption = Number(calculator.dataset.heatPumpConsumption || 4500);
    const currency = new Intl.NumberFormat('de-LU', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 });
    const number = new Intl.NumberFormat('de-LU', { maximumFractionDigits: 0 });
    const decimal = new Intl.NumberFormat('de-LU', { minimumFractionDigits: 1, maximumFractionDigits: 1 });

    if (!power || !storage || !selfUse || !investment || !gridPrice || !feedPrice || !consumption || !roofArea) return;

    const write = (selector, value) => {
        const target = calculator.querySelector(selector);
        if (target) target.textContent = value;
    };

    const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
    const roundTo = (value, step) => Math.round(value / step) * step;
    const totalConsumption = () => Number(consumption.value) + (ev?.checked ? evConsumption : 0) + (heatPump?.checked ? heatPumpConsumption : 0);

    const calculate = () => {
        const kwp = Number(power.value);
        const storageKwh = Number(storage.value);
        const selfUseShare = Number(selfUse.value) / 100;
        const grossInvestment = Number(investment.value);
        const annualConsumption = totalConsumption();
        const gridEuro = Number(gridPrice.value) / 100;
        const feedEuro = Number(feedPrice.value) / 100;
        const generation = kwp * yieldPerKwp;
        const consumed = Math.min(generation * selfUseShare, annualConsumption);
        const exported = generation - consumed;
        const energyValue = consumed * gridEuro + exported * feedEuro;
        const pvAid = kwp >= 15 ? 10000 : Math.max(0, kwp * (1155 - (1155 / 35) * kwp));
        const storageAid = storageKwh <= 0 ? 0 : storageKwh >= 9 ? 2250 : Math.max(0, storageKwh * (500 - (500 / 18) * storageKwh));
        const netInvestment = Math.max(0, grossInvestment - pvAid - storageAid);
        const payback = energyValue > 0 ? netInvestment / energyValue : 0;

        write('[data-calc-power-output]', `${decimal.format(kwp)} kWp`);
        write('[data-calc-storage-output]', `${number.format(storageKwh)} kWh`);
        write('[data-calc-self-use-output]', `${number.format(selfUseShare * 100)} %`);
        write('[data-calc-investment-output]', currency.format(grossInvestment));
        write('[data-calc-total-consumption]', `${number.format(annualConsumption)} kWh`);
        write('[data-calc-model-system]', storageKwh > 0 ? `${decimal.format(kwp)} kWp + ${number.format(storageKwh)} kWh` : `${decimal.format(kwp)} kWp`);
        write('[data-calc-model-investment]', currency.format(grossInvestment));
        write('[data-calc-system]', storageKwh > 0 ? `${decimal.format(kwp)} kWp + ${number.format(storageKwh)} kWh` : `${decimal.format(kwp)} kWp`);
        write('[data-calc-system-note]', `${number.format(kwp * areaPerKwp)} m² Flächenbedarf · ${number.format(selfUseShare * 100)} % angenommener Eigenverbrauch`);
        write('[data-calc-generation]', `${number.format(generation)} kWh`);
        write('[data-calc-pv-aid]', currency.format(pvAid));
        write('[data-calc-storage-aid]', currency.format(storageAid));
        write('[data-calc-energy-value]', `${currency.format(energyValue)} / Jahr`);
        write('[data-calc-payback]', payback > 0 ? `ca. ${decimal.format(payback)} Jahre` : '–');
    };

    const selectMatchingPreset = (selector, value) => {
        calculator.querySelectorAll(selector).forEach((button) => {
            const matches = Number(button.dataset.calcConsumptionPreset || button.dataset.calcRoofPreset) === Number(value);
            button.classList.toggle('is-selected', matches);
            button.setAttribute('aria-pressed', String(matches));
        });
    };

    const applyGuidedModel = () => {
        const annualConsumption = totalConsumption();
        const availableRoof = Number(roofArea.value);
        const roofLimit = Math.floor((availableRoof / areaPerKwp) * 2) / 2;
        const demandSized = Math.ceil(((annualConsumption / yieldPerKwp) * 1.15) * 2) / 2;
        const suggestedPower = clamp(Math.min(roofLimit, demandSized), 2, 20);
        const storageMode = storageModes.find((item) => item.checked)?.value || 'none';
        const suggestedStorage = storageMode === 'auto' ? clamp(Math.round(suggestedPower * 0.8), 3, 9) : 0;
        const generation = suggestedPower * yieldPerKwp;
        const usageRatio = generation > 0 ? annualConsumption / generation : 0;
        const storageBoost = suggestedStorage > 0 ? Math.min(24, (suggestedStorage / suggestedPower) * 22) : 0;
        const flexibleLoadBoost = (ev?.checked ? 6 : 0) + (heatPump?.checked ? 5 : 0);
        const suggestedSelfUse = Math.round(clamp(28 + Math.min(18, usageRatio * 14) + storageBoost + flexibleLoadBoost, 25, 85));
        const suggestedInvestment = roundTo(suggestedPower * costPerKwp + suggestedStorage * costPerStorageKwh, 500);

        power.value = String(suggestedPower);
        storage.value = String(suggestedStorage);
        selfUse.value = String(suggestedSelfUse);
        investment.value = String(clamp(suggestedInvestment, Number(investment.min), Number(investment.max)));
        calculate();
    };

    calculator.querySelectorAll('[data-calc-consumption-preset]').forEach((button) => button.addEventListener('click', () => {
        consumption.value = button.dataset.calcConsumptionPreset;
        selectMatchingPreset('[data-calc-consumption-preset]', consumption.value);
        applyGuidedModel();
    }));
    calculator.querySelectorAll('[data-calc-roof-preset]').forEach((button) => button.addEventListener('click', () => {
        roofArea.value = button.dataset.calcRoofPreset;
        selectMatchingPreset('[data-calc-roof-preset]', roofArea.value);
        applyGuidedModel();
    }));
    consumption.addEventListener('input', () => {
        selectMatchingPreset('[data-calc-consumption-preset]', consumption.value);
        applyGuidedModel();
    });
    roofArea.addEventListener('input', () => {
        selectMatchingPreset('[data-calc-roof-preset]', roofArea.value);
        applyGuidedModel();
    });
    [ev, heatPump, ...storageModes].filter(Boolean).forEach((input) => input.addEventListener('change', applyGuidedModel));
    [power, storage, selfUse, investment, gridPrice, feedPrice].forEach((input) => input.addEventListener('input', calculate));
    selectMatchingPreset('[data-calc-consumption-preset]', consumption.value);
    selectMatchingPreset('[data-calc-roof-preset]', roofArea.value);
    applyGuidedModel();
});

document.querySelectorAll('[data-service-configurator]').forEach((configurator) => {
    const panels = [...configurator.querySelectorAll('[data-config-panel]')];
    const indicators = [...configurator.querySelectorAll('[data-config-step-indicator]')];
    const progressLabel = configurator.querySelector('[data-config-progress-label]');
    const progressLine = configurator.querySelector('[data-config-progress-line]');
    const summary = configurator.querySelector('[data-config-summary]');
    const dependencies = JSON.parse(configurator.dataset.configDependencies || '[]');
    const summaryDefinition = JSON.parse(configurator.dataset.configSummaryDefinition || '[]');
    let currentStep = Math.min(Number(configurator.dataset.startStep || 0), panels.length - 1);

    const fields = (name) => [...configurator.querySelectorAll(`[name="${name}"], [name="${name}[]"]`)];
    const fieldValue = (name) => {
        const matches = fields(name);
        if (!matches.length) return '';
        if (matches[0].type === 'checkbox') return matches.filter((field) => field.checked).map((field) => field.value);
        if (matches[0].type === 'radio') return matches.find((field) => field.checked)?.value || '';
        return matches[0].value.trim();
    };

    const triggerIsActive = (dependency) => fields(dependency.trigger_question).some((input) => input.checked && input.closest('[data-config-option-id]')?.dataset.configOptionId === dependency.trigger_id);

    const updateDependencies = () => {
        const affectedNames = new Set(dependencies.map((dependency) => dependency.affected));
        affectedNames.forEach((name) => {
            const question = configurator.querySelector(`[data-config-question="${name}"]`);
            if (!question) return;
            const matches = dependencies.filter((dependency) => dependency.affected === name);
            const visible = matches.some(triggerIsActive);
            question.hidden = !visible;
            question.querySelectorAll('input').forEach((input) => {
                input.disabled = !visible;
                if (!visible) {
                    if (['radio', 'checkbox'].includes(input.type)) input.checked = false;
                    else input.value = '';
                }
            });
        });
    };

    const setInvalid = (question, invalid) => {
        if (!question) return;
        question.classList.toggle('has-error', invalid);
        const message = question.querySelector('[data-config-error]');
        if (message) message.hidden = !invalid;
    };

    const validatePanel = (panel) => {
        updateDependencies();
        let firstInvalid = null;
        panel.querySelectorAll('[data-config-question]:not([hidden])').forEach((question) => {
            const required = [...question.querySelectorAll('[data-config-required]:not(:disabled)')];
            if (!required.length) return setInvalid(question, false);
            const valid = ['radio', 'checkbox'].includes(required[0].type) ? required.some((input) => input.checked) : required.every((input) => input.value.trim() !== '');
            setInvalid(question, !valid);
            if (!valid && !firstInvalid) firstInvalid = required[0];
        });
        panel.querySelectorAll('[data-config-required]:not([disabled])').forEach((input) => {
            if (input.closest('[data-config-question]')) return;
            const valid = input.type === 'checkbox' ? input.checked : input.value.trim() !== '' && input.checkValidity();
            input.classList.toggle('has-error', !valid);
            input.setAttribute('aria-invalid', String(!valid));
            if (!valid && !firstInvalid) firstInvalid = input;
        });
        firstInvalid?.focus();
        return !firstInvalid;
    };

    const escapeHtml = (value) => String(value).replace(/[&<>'"]/g, (character) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#039;', '"': '&quot;' })[character]);
    const renderSummary = () => {
        if (!summary) return;
        summary.innerHTML = summaryDefinition.map((group, index) => {
            const items = Object.entries(group.fields).map(([name, label]) => {
                const value = fieldValue(name);
                return {label, value: Array.isArray(value) ? value.join(', ') : value};
            }).filter((item) => item.value);
            if (!items.length) return '';
            return `<article><header><span>${String(index + 1).padStart(2, '0')}</span><h4>${escapeHtml(group.title)}</h4><button type="button" data-config-edit="${index}">Ändern</button></header><dl>${items.map((item) => `<div><dt>${escapeHtml(item.label)}</dt><dd>${escapeHtml(item.value)}</dd></div>`).join('')}</dl></article>`;
        }).join('');
        summary.querySelectorAll('[data-config-edit]').forEach((button) => button.addEventListener('click', () => showStep(Number(button.dataset.configEdit), true)));
    };

    const showStep = (step, focus = true) => {
        currentStep = Math.max(0, Math.min(step, panels.length - 1));
        panels.forEach((panel, index) => { panel.hidden = index !== currentStep; });
        indicators.forEach((indicator, index) => {
            indicator.classList.toggle('is-active', index === currentStep);
            indicator.classList.toggle('is-complete', index < currentStep);
            if (index === currentStep) indicator.setAttribute('aria-current', 'step'); else indicator.removeAttribute('aria-current');
        });
        if (progressLabel) progressLabel.textContent = `Schritt ${currentStep + 1} von ${panels.length}`;
        if (progressLine) progressLine.style.width = `${((currentStep + 1) / panels.length) * 100}%`;
        updateDependencies();
        if (currentStep === panels.length - 1) renderSummary();
        if (focus) {
            configurator.scrollIntoView({behavior: reducedMotion ? 'auto' : 'smooth', block: 'start'});
            window.setTimeout(() => panels[currentStep].querySelector('.pv-config-panel-content > header h3')?.focus({preventScroll: true}), reducedMotion ? 0 : 400);
        }
    };

    configurator.addEventListener('change', (event) => {
        if (!event.target.matches('input')) return;
        updateDependencies();
        setInvalid(event.target.closest('[data-config-question]'), false);
    });
    configurator.querySelectorAll('[data-config-next]').forEach((button) => button.addEventListener('click', () => {
        const panel = button.closest('[data-config-panel]');
        if (validatePanel(panel)) showStep(currentStep + 1);
    }));
    configurator.querySelectorAll('[data-config-back]').forEach((button) => button.addEventListener('click', () => showStep(currentStep - 1)));
    configurator.querySelector('[data-config-edit-first]')?.addEventListener('click', () => showStep(0));
    configurator.addEventListener('submit', (event) => {
        if (validatePanel(panels.at(-1))) return;
        event.preventDefault();
    });

    showStep(currentStep, false);
});

document.querySelectorAll('[data-pv-configurator]').forEach((configurator) => {
    const panels = [...configurator.querySelectorAll('[data-config-panel]')];
    const indicators = [...configurator.querySelectorAll('[data-config-step-indicator]')];
    const progressLabel = configurator.querySelector('[data-config-progress-label]');
    const progressLine = configurator.querySelector('[data-config-progress-line]');
    const summary = configurator.querySelector('[data-config-summary]');
    let currentStep = Math.min(Number(configurator.dataset.startStep || 0), panels.length - 1);

    const fieldValue = (name) => {
        const fields = [...configurator.querySelectorAll(`[name="${name}"], [name="${name}[]"]`)];
        if (!fields.length) return '';
        if (fields[0].type === 'checkbox') return fields.filter((field) => field.checked).map((field) => field.value);
        if (fields[0].type === 'radio') return fields.find((field) => field.checked)?.value || '';
        return fields[0].value.trim();
    };

    const updateDependencies = () => {
        const roofShape = fieldValue('roof_shape');
        const roofSides = configurator.querySelector('[data-config-dependency="roof-sides"]');
        const hidesRoofSides = ['Flachdach', 'Pultdach'].includes(roofShape);
        roofSides.hidden = hidesRoofSides;
        roofSides.querySelectorAll('input').forEach((input) => {
            input.disabled = hidesRoofSides;
            if (hidesRoofSides) input.checked = false;
        });
        roofSides.querySelectorAll('[data-config-option]').forEach((option) => {
            const disallowed = roofShape === 'Satteldach' && ['Drei', 'Vier'].includes(option.dataset.configOption);
            option.hidden = disallowed;
            option.querySelector('input').disabled = hidesRoofSides || disallowed;
            if (disallowed) option.querySelector('input').checked = false;
        });

        const otherRoof = configurator.querySelector('[data-config-dependency="other-roof-type"]');
        const needsOtherRoof = fieldValue('roof_type') === 'Sonstige (bitte spezifizieren)';
        otherRoof.hidden = !needsOtherRoof;
        otherRoof.querySelector('input').disabled = !needsOtherRoof;

        const otherEnergy = configurator.querySelector('[data-config-dependency="other-energy-usage"]');
        const needsOtherEnergy = fieldValue('energy_usage').includes('Sonstige (bitte spezifizieren)');
        otherEnergy.hidden = !needsOtherEnergy;
        otherEnergy.querySelector('input').disabled = !needsOtherEnergy;
    };

    const setInvalid = (question, invalid) => {
        if (!question) return;
        question.classList.toggle('has-error', invalid);
        const message = question.querySelector('[data-config-error]');
        if (message) message.hidden = !invalid;
    };

    const validatePanel = (panel) => {
        updateDependencies();
        let firstInvalid = null;
        panel.querySelectorAll('[data-config-question]:not([hidden])').forEach((question) => {
            const required = [...question.querySelectorAll('[data-config-required]:not(:disabled)')];
            if (!required.length) return setInvalid(question, false);
            const valid = ['radio', 'checkbox'].includes(required[0].type)
                ? required.some((input) => input.checked)
                : required.every((input) => input.value.trim() !== '');
            setInvalid(question, !valid);
            if (!valid && !firstInvalid) firstInvalid = required[0];
        });
        panel.querySelectorAll('[data-config-required]:not([disabled])').forEach((input) => {
            if (input.closest('[data-config-question]')) return;
            const valid = input.type === 'checkbox' ? input.checked : input.value.trim() !== '' && input.checkValidity();
            input.classList.toggle('has-error', !valid);
            input.setAttribute('aria-invalid', String(!valid));
            if (!valid && !firstInvalid) firstInvalid = input;
        });
        if (firstInvalid) firstInvalid.focus();
        return !firstInvalid;
    };

    const labels = {
        building_status: 'Gebäudestatus', building_type: 'Gebäudeart', roof_shape: 'Dachform', roof_sides: 'Dachseiten', roof_size: 'Nutzbare Dachfläche',
        roof_type: 'Dacheindeckung', roof_angle: 'Dachneigung', roof_direction: 'Dachausrichtung', natural_roof_barrier: 'Natürliche Verschattungen',
        building_roof_barrier: 'Bauwerkbedingte Verschattungen', structure_roof_barrier: 'Dachstrukturen', installation_roof_barrier: 'Dachinstallationen',
        security_roof_barrier: 'Sicherheits- und Schutzvorrichtungen', other_roof_barrier: 'Weitere Elemente', amount_of_users: 'Anzahl der Nutzer',
        power_consume: 'Jahresstromverbrauch', energy_usage: 'Stromnutzung', energy_storage: 'Speicherlösung',
    };
    const groups = [
        ['Fragen zum Gebäude', ['building_status', 'building_type']],
        ['Grundlagen des Dachs', ['roof_shape', 'roof_sides', 'roof_size']],
        ['Details zum Dach', ['roof_type', 'roof_angle', 'roof_direction']],
        ['Störfaktoren', ['natural_roof_barrier', 'building_roof_barrier', 'structure_roof_barrier', 'installation_roof_barrier', 'security_roof_barrier', 'other_roof_barrier']],
        ['Strombedarf und Energieplanung', ['amount_of_users', 'power_consume', 'energy_usage', 'energy_storage']],
    ];

    const displayValue = (name) => {
        let value = fieldValue(name);
        if (name === 'roof_type' && value === 'Sonstige (bitte spezifizieren)') value = `Sonstige: ${fieldValue('other_roof_type') || 'nicht näher angegeben'}`;
        if (name === 'energy_usage' && Array.isArray(value)) value = value.map((item) => item === 'Sonstige (bitte spezifizieren)' ? `Sonstige: ${fieldValue('other_energy_usage') || 'nicht näher angegeben'}` : item).join(', ');
        if (name === 'power_consume' && value) value = `${new Intl.NumberFormat('de-LU').format(Number(value))} kWh`;
        return Array.isArray(value) ? value.join(', ') : value;
    };
    const escapeHtml = (value) => String(value).replace(/[&<>'"]/g, (character) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#039;', '"': '&quot;' })[character]);

    const renderSummary = () => {
        if (!summary) return;
        summary.innerHTML = groups.map(([title, fields], index) => {
            const items = fields.map((name) => ({ label: labels[name], value: displayValue(name) })).filter((item) => item.value);
            if (!items.length) return '';
            return `<article><header><span>${String(index + 1).padStart(2, '0')}</span><h4>${escapeHtml(title)}</h4><button type="button" data-config-edit="${index}">Ändern</button></header><dl>${items.map((item) => `<div><dt>${escapeHtml(item.label)}</dt><dd>${escapeHtml(item.value)}</dd></div>`).join('')}</dl></article>`;
        }).join('');
        summary.querySelectorAll('[data-config-edit]').forEach((button) => button.addEventListener('click', () => showStep(Number(button.dataset.configEdit), true)));
    };

    const showStep = (step, focus = true) => {
        currentStep = Math.max(0, Math.min(step, panels.length - 1));
        panels.forEach((panel, index) => { panel.hidden = index !== currentStep; });
        indicators.forEach((indicator, index) => {
            indicator.classList.toggle('is-active', index === currentStep);
            indicator.classList.toggle('is-complete', index < currentStep);
            if (index === currentStep) indicator.setAttribute('aria-current', 'step'); else indicator.removeAttribute('aria-current');
        });
        if (progressLabel) progressLabel.textContent = `Schritt ${currentStep + 1} von ${panels.length}`;
        if (progressLine) progressLine.style.width = `${((currentStep + 1) / panels.length) * 100}%`;
        updateDependencies();
        if (currentStep === panels.length - 1) renderSummary();
        if (focus) {
            configurator.scrollIntoView({ behavior: reducedMotion ? 'auto' : 'smooth', block: 'start' });
            window.setTimeout(() => panels[currentStep].querySelector('.pv-config-panel-content > header h3')?.focus({ preventScroll: true }), reducedMotion ? 0 : 400);
        }
    };

    configurator.addEventListener('change', (event) => {
        if (!event.target.matches('input')) return;
        updateDependencies();
        setInvalid(event.target.closest('[data-config-question]'), false);
    });
    configurator.querySelectorAll('[data-config-next]').forEach((button) => button.addEventListener('click', () => {
        const panel = button.closest('[data-config-panel]');
        if (validatePanel(panel)) showStep(currentStep + 1);
    }));
    configurator.querySelectorAll('[data-config-back]').forEach((button) => button.addEventListener('click', () => showStep(currentStep - 1)));
    configurator.querySelector('[data-config-edit-first]')?.addEventListener('click', () => showStep(0));
    configurator.addEventListener('submit', (event) => {
        if (validatePanel(panels.at(-1))) return;
        event.preventDefault();
    });

    showStep(currentStep, false);
});

if (!reducedMotion) {
    document.querySelectorAll('[data-service-motion]').forEach((motion) => {
        motion.addEventListener('pointermove', (event) => {
            if (event.pointerType === 'touch') return;
            const bounds = motion.getBoundingClientRect();
            motion.style.setProperty('--pointer-x', `${event.clientX - bounds.left}px`);
            motion.style.setProperty('--pointer-y', `${event.clientY - bounds.top}px`);
        });

        motion.addEventListener('pointerleave', () => {
            motion.style.setProperty('--pointer-x', '50%');
            motion.style.setProperty('--pointer-y', '50%');
        });
    });
}

document.querySelectorAll('[data-storage-flow]').forEach((flow) => {
    const section = flow.closest('.storage-energy') ?? flow;
    const buttons = [...section.querySelectorAll('[data-storage-state-button]')];
    const canHover = window.matchMedia('(hover: hover) and (pointer: fine)');

    const setStorageState = (state) => {
        flow.dataset.storageState = state;
        buttons.forEach((button) => {
            const active = button.dataset.storageStateButton === state;
            button.classList.toggle('is-active', active);
            button.setAttribute('aria-pressed', String(active));
        });
    };

    buttons.forEach((button) => {
        const activate = () => setStorageState(button.dataset.storageStateButton);
        button.addEventListener('click', activate);
        button.addEventListener('focus', activate);
        if (canHover.matches) button.addEventListener('pointerenter', activate);
    });
});

document.querySelectorAll('[data-company-flip-grid]').forEach((grid) => {
    const cards = [...grid.querySelectorAll('[data-company-flip-card]')];
    const canHover = window.matchMedia('(hover: hover) and (pointer: fine)');

    const syncCard = (card) => {
        const isOpen = card.open;
        const front = card.querySelector('summary');
        const back = card.querySelector('.company-flip-back');
        front?.setAttribute('aria-expanded', String(isOpen));
        front?.setAttribute('aria-hidden', String(isOpen));
        if (front) front.inert = isOpen;
        back?.setAttribute('aria-hidden', String(!isOpen));
        if (back) back.inert = !isOpen;
    };

    const closeCard = (card, returnFocus = false) => {
        card.open = false;
        delete card.dataset.hoverOpen;
        syncCard(card);
        if (returnFocus) card.querySelector('summary')?.focus();
    };

    cards.forEach((card) => {
        const summary = card.querySelector('summary');
        const close = card.querySelector('[data-company-flip-close]');
        syncCard(card);

        card.addEventListener('toggle', () => {
            syncCard(card);
            if (!card.open) return;
            cards.filter((other) => other !== card && other.open).forEach((other) => closeCard(other));
            if (document.activeElement === summary) window.requestAnimationFrame(() => close?.focus());
        });

        card.addEventListener('pointerenter', (event) => {
            if (!canHover.matches || event.pointerType === 'touch' || card.open) return;
            card.dataset.hoverOpen = 'true';
            card.open = true;
        });

        card.addEventListener('pointerleave', (event) => {
            if (!canHover.matches || event.pointerType === 'touch' || !card.dataset.hoverOpen) return;
            closeCard(card);
        });

        close?.addEventListener('click', () => closeCard(card, true));
        card.addEventListener('keydown', (event) => {
            if (event.key !== 'Escape' || !card.open) return;
            event.preventDefault();
            closeCard(card, true);
        });
    });
});

document.querySelectorAll('a[href^="#"]').forEach((link) => {
    link.addEventListener('click', (event) => {
        const targetId = link.getAttribute('href');
        if (!targetId || targetId === '#') return;
        const target = document.querySelector(targetId);
        if (!target) return;
        event.preventDefault();
        target.scrollIntoView({ behavior: reducedMotion ? 'auto' : 'smooth', block: 'start' });
        if (history.replaceState) history.replaceState(null, '', targetId);
    });
});
