/* HEB Bauelemente: Intro-Tor, Navigation, Einblendungen, 24-h-Uhr, Galerie, Formular. Ohne Bibliotheken. */ (function () { "use strict"; var root = document.documentElement; root.classList.add("ready"); var reduced = matchMedia("(prefers-reduced-motion: reduce)").matches; /* ---------- Intro: Sektionaltor fährt hoch (einmal pro Sitzung) ---------- */ var gate = document.querySelector(".gate"); if (gate && root.classList.contains("has-intro")) { try { sessionStorage.setItem("heb-intro", "1"); } catch (e) {} var started = false; var open = function () { if (started) return; started = true; gate.classList.add("is-moving"); requestAnimationFrame(function () { gate.classList.add("is-open"); root.classList.add("intro-go"); }); }; gate.addEventListener("transitionend", function (e) { if (e.target === gate && e.propertyName === "transform") gate.remove(); }); gate.addEventListener("click", open); var img = document.querySelector(".hero__img img"); setTimeout(function () { if (!img || img.complete) return open(); img.addEventListener("load", open); setTimeout(open, 1200); }, 650); } else if (gate) { gate.remove(); } /* ---------- Header ---------- */ var header = document.querySelector(".site-header"); var onScroll = function () { header.classList.toggle("is-scrolled", scrollY > 8); }; addEventListener("scroll", onScroll, { passive: true }); onScroll(); var menuBtn = document.querySelector(".menu-btn"); var mobileNav = document.getElementById("mobile-nav"); if (menuBtn && mobileNav) { var setMenu = function (open) { menuBtn.setAttribute("aria-expanded", String(open)); menuBtn.setAttribute("aria-label", open ? "Menü schließen" : "Menü öffnen"); mobileNav.hidden = !open; }; menuBtn.addEventListener("click", function () { setMenu(mobileNav.hidden); }); mobileNav.addEventListener("click", function (e) { if (e.target.closest("a")) setMenu(false); }); addEventListener("keydown", function (e) { if (e.key === "Escape" && !mobileNav.hidden) { setMenu(false); menuBtn.focus(); } }); } document.querySelectorAll("[data-year]").forEach(function (el) { el.textContent = new Date().getFullYear(); }); /* ---------- Einblendungen ---------- */ var reveals = document.querySelectorAll(".reveal"); if ("IntersectionObserver" in window && !reduced) { var io = new IntersectionObserver(function (entries) { entries.forEach(function (en) { if (en.isIntersecting) { en.target.classList.add("is-in"); io.unobserve(en.target); } }); }, { threshold: 0.12, rootMargin: "0px 0px -40px 0px" }); reveals.forEach(function (el) { io.observe(el); }); } else { reveals.forEach(function (el) { el.classList.add("is-in"); }); } /* ---------- 24-h-Uhr: füllt sich mit den Arbeitsschritten ---------- */ var steps = Array.prototype.slice.call(document.querySelectorAll(".step[data-hours]")); var bar = document.querySelector(".clock__bar"); var num = document.querySelector("[data-clock]"); if (steps.length && bar && num) { var shown = 0, target = 0, tween = null; var animateTo = function (h) { target = h; bar.style.strokeDashoffset = String(100 - (h / 24) * 100); if (reduced) { shown = h; num.textContent = h; return; } if (tween) return; tween = setInterval(function () { if (shown === target) { clearInterval(tween); tween = null; return; } shown += shown < target ? 1 : -1; num.textContent = shown; }, 45); }; var ticking = false; var update = function () { ticking = false; var line = innerHeight * 0.6, active = -1; steps.forEach(function (s, i) { if (s.getBoundingClientRect().top < line) active = i; }); steps.forEach(function (s, i) { s.classList.toggle("is-active", i === active); s.classList.toggle("is-done", i < active); }); var h = active < 0 ? 0 : +steps[active].dataset.hours; if (h !== target) animateTo(h); }; addEventListener("scroll", function () { if (!ticking) { ticking = true; requestAnimationFrame(update); } }, { passive: true }); update(); } /* ---------- Galerie: Filter ---------- */ var chips = document.querySelectorAll(".chip[data-filter]"); var items = Array.prototype.slice.call(document.querySelectorAll(".gallery__item")); var count = document.querySelector("[data-count]"); chips.forEach(function (chip) { chip.addEventListener("click", function () { var f = chip.dataset.filter, n = 0; chips.forEach(function (c) { c.setAttribute("aria-pressed", String(c === chip)); }); items.forEach(function (it) { var show = f === "alle" || it.dataset.tags.split(" ").indexOf(f) > -1; it.hidden = !show; if (show) n++; }); if (count) count.textContent = n; }); }); if (location.hash && items.length) { var hit = document.getElementById(location.hash.slice(1)); if (hit && hit.classList.contains("gallery__item")) hit.classList.add("is-target"); } /* ---------- Galerie: Großansicht ---------- */ var box = document.querySelector(".lightbox"); if (box && typeof box.showModal === "function") { var bImg = box.querySelector("img"), bTitle = box.querySelector("figcaption b"), bSub = box.querySelector("figcaption span"); var list = [], idx = 0, opener = null; var show = function (i) { idx = (i + list.length) % list.length; var b = list[idx]; bImg.src = b.dataset.full; bImg.alt = b.dataset.title + ", " + b.dataset.sub; bTitle.textContent = b.dataset.title; bSub.textContent = b.dataset.sub; }; document.querySelectorAll(".gallery__btn").forEach(function (btn) { btn.addEventListener("click", function () { list = items.filter(function (it) { return !it.hidden; }).map(function (it) { return it.querySelector(".gallery__btn"); }); opener = btn; show(list.indexOf(btn)); box.showModal(); }); }); box.querySelector(".lightbox__close").addEventListener("click", function () { box.close(); }); box.querySelector(".lightbox__prev").addEventListener("click", function () { show(idx - 1); }); box.querySelector(".lightbox__next").addEventListener("click", function () { show(idx + 1); }); box.addEventListener("click", function (e) { if (e.target === box) box.close(); }); box.addEventListener("close", function () { if (opener) opener.focus(); }); box.addEventListener("keydown", function (e) { if (e.key === "ArrowLeft") show(idx - 1); if (e.key === "ArrowRight") show(idx + 1); }); var x0 = null; box.addEventListener("touchstart", function (e) { x0 = e.touches[0].clientX; }, { passive: true }); box.addEventListener("touchend", function (e) { if (x0 === null) return; var dx = e.changedTouches[0].clientX - x0; if (Math.abs(dx) > 50) show(idx + (dx < 0 ? 1 : -1)); x0 = null; }); } /* ---------- Anfrageformular ---------- */ var form = document.getElementById("anfrage"); if (form) { var pre = new URLSearchParams(location.search).get("anliegen"); var preInput = pre && form.querySelector('input[name="anliegen"][value="' + pre.replace(/[^a-z]/g, "") + '"]'); if (preInput) preInput.checked = true; var setError = function (input, msg) { var wrap = input.closest(".field, .check"); var old = wrap.querySelector(".field__error"); if (old) old.remove(); wrap.classList.toggle("is-invalid", !!msg); input.setAttribute("aria-invalid", msg ? "true" : "false"); if (msg && wrap.classList.contains("field")) { var err = document.createElement("span"); err.className = "field__error"; err.id = "err-" + input.name; err.textContent = msg; wrap.appendChild(err); input.setAttribute("aria-describedby", err.id); } else { input.removeAttribute("aria-describedby"); } }; var check = function (input) { var v = input.type === "checkbox" ? input.checked : input.value.trim(); var msg = ""; if (input.required && !v) msg = input.type === "checkbox" ? "Bitte bestätigen." : "Bitte ausfüllen."; else if (input.name === "telefon" && v && v.replace(/\D/g, "").length < 6) msg = "Bitte eine gültige Telefonnummer angeben."; else if (input.type === "email" && v && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v)) msg = "Bitte eine gültige E-Mail-Adresse angeben."; setError(input, msg); return !msg; }; var fields = form.querySelectorAll("input[required], input[type=email]"); fields.forEach(function (f) { f.addEventListener(f.type === "checkbox" ? "change" : "blur", function () { check(f); }); }); form.addEventListener("submit", function (e) { e.preventDefault(); var firstBad = null; fields.forEach(function (f) { if (!check(f) && !firstBad) firstBad = f; }); if (firstBad) { firstBad.focus(); return; } var done = form.querySelector(".form__done"); if (form.querySelector(".form__demo")) { done.querySelector("[data-done-text]").textContent = "Im Entwurf wird nichts verschickt. Nach dem Launch landet die Anfrage direkt bei Wolfgang Haas."; } form.querySelector("button[type=submit]").hidden = true; done.hidden = false; done.focus(); }); } })();