/* ToolCTRL — Landing Page Interaktion */ (function () { "use strict"; /* ---- Jahr im Footer ---- */ var yearEl = document.getElementById("year"); if (yearEl) yearEl.textContent = String(new Date().getFullYear()); /* ---- Mobile-Navigation ---- */ var toggle = document.querySelector(".nav-toggle"); var mobileNav = document.querySelector(".mobile-nav"); var header = document.querySelector(".site-header"); function updateHeaderState() { if (!header) return; header.classList.toggle("is-scrolled", window.scrollY > 12); } updateHeaderState(); window.addEventListener("scroll", updateHeaderState, { passive: true }); if (toggle && mobileNav) { toggle.addEventListener("click", function () { var open = mobileNav.hasAttribute("hidden"); if (open) { mobileNav.removeAttribute("hidden"); toggle.setAttribute("aria-expanded", "true"); } else { mobileNav.setAttribute("hidden", ""); toggle.setAttribute("aria-expanded", "false"); } }); mobileNav.querySelectorAll("a").forEach(function (a) { a.addEventListener("click", function () { mobileNav.setAttribute("hidden", ""); toggle.setAttribute("aria-expanded", "false"); }); }); } /* ---- Console-Easter-Eggs fuer neugierige Inspektoren ---- */ var inspectAscii = [ " _______ _______ _______ ___ _______ _______ _______ ", "| || || || | | || || |", "|_ _|| _ || _ || | | ||_ _|| ___|", " | | | | | || | | || | | | | | | |___ ", " | | | |_| || |_| || |___ | _| | | | ___|", " | | | || || || |_ | | | |___ ", " |___| |_______||_______||_______||_______| |___| |_______|" ].join("\n"); var inspectHints = [ "CTRL-KEY EVENT: Inspect gedrueckt. Bitte keine Produktivsysteme anfassen.", "Hidden combo: window.toolctrl.press()", "Status: schwarz/weiss. Farbe wurde zurueck in den Werkzeugkoffer gelegt." ]; var inspectLogged = false; function logControlConsole(reason) { if (reason === "devtools" && inspectLogged) return; if (reason === "devtools") inspectLogged = true; console.log( "%c" + inspectAscii, "font-family: monospace; font-weight: 900; color: #000; background: #fff; padding: 10px; border: 3px solid #000;" ); console.log("%c" + inspectHints[Math.floor(Math.random() * inspectHints.length)], "font-family: monospace; color: #fff; background: #000; padding: 6px 8px;"); } window.toolctrl = window.toolctrl || {}; window.toolctrl.press = function () { console.log("%c[ CTRL ] *klack* Ablauf uebernommen.", "font-family: monospace; font-weight: 900;"); return "pressed"; }; window.addEventListener("load", function () { setTimeout(function () { logControlConsole("load"); }, 450); }); setInterval(function () { var open = Math.abs(window.outerWidth - window.innerWidth) > 160 || Math.abs(window.outerHeight - window.innerHeight) > 160; document.documentElement.toggleAttribute("data-inspecting", open); if (open) logControlConsole("devtools"); }, 1200); /* ---- Dezente Scroll-Reveals ---- */ var reduceMotion = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches; var revealEls = Array.prototype.slice.call(document.querySelectorAll([ ".hero-copy > *", ".hero-visual", ".trust-strip span", ".stance-head > *", ".stance-card", ".product-copy > *", ".product-ui", ".velocity-copy > *", ".velocity-item", ".section-head", ".card", ".industry-card", ".step", ".arbeit-copy > *", ".kanban-wrap", ".event-poster", ".mini-terminal", ".event-card", ".kontakt-copy > *", ".contact-promises li", ".lead-form", ".td-hero .wrap > *", ".td-feature > *", ".td-talk", ".speaker-card", ".shot", ".gj-teaser" ].join(","))); revealEls.forEach(function (el, index) { el.classList.add("reveal", "reveal-delay-" + (index % 5)); }); if (reduceMotion || !("IntersectionObserver" in window)) { revealEls.forEach(function (el) { el.classList.add("is-visible"); }); } else { var observer = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (!entry.isIntersecting) return; entry.target.classList.add("is-visible"); observer.unobserve(entry.target); }); }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" }); revealEls.forEach(function (el) { observer.observe(el); }); } /* ---- Hero Workflow Nodes: ziehbare Kontrollpunkte ---- */ var board = document.querySelector("[data-control-board]"); if (board) { var lineSvg = board.querySelector(".control-lines"); var logoNode = board.querySelector("[data-logo-node]"); var nodes = Array.prototype.slice.call(board.querySelectorAll("[data-node]")); var draggableItems = nodes.concat(logoNode ? [logoNode] : []); var activeDrag = null; var lineFrame = 0; if (logoNode) { logoNode.addEventListener("dragstart", function (e) { e.preventDefault(); }); } function boardRect() { return board.getBoundingClientRect(); } function centerOf(el, b) { var r = el.getBoundingClientRect(); return { x: r.left - b.left + r.width / 2, y: r.top - b.top + r.height / 2 }; } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } function updateLines() { if (!lineSvg || !logoNode) return; var b = boardRect(); var target = centerOf(logoNode, b); lineSvg.setAttribute("viewBox", "0 0 " + b.width + " " + b.height); nodes.forEach(function (node) { var path = lineSvg.querySelector('[data-line="' + node.dataset.node + '"]'); if (!path) return; var nodeCenter = centerOf(node, b); var isOutput = node.classList.contains("is-output"); var start = isOutput ? target : nodeCenter; var end = isOutput ? nodeCenter : target; var bend = Math.max(44, Math.abs(end.x - start.x) * 0.38); var c1x = start.x + (end.x > start.x ? bend : -bend); var c2x = end.x + (end.x > start.x ? -bend : bend); var d = "M " + start.x + " " + start.y + " C " + c1x + " " + start.y + ", " + c2x + " " + end.y + ", " + end.x + " " + end.y; path.classList.toggle("is-output", isOutput); path.setAttribute("d", d); }); lineFrame = 0; } function requestLineUpdate() { if (lineFrame) return; lineFrame = window.requestAnimationFrame(updateLines); } draggableItems.forEach(function (item) { item.addEventListener("pointerdown", function (e) { var r = item.getBoundingClientRect(); if (item === logoNode) item.classList.add("is-positioned"); activeDrag = { node: item, isLogo: item === logoNode, offsetX: e.clientX - r.left, offsetY: e.clientY - r.top }; item.classList.add("is-dragging"); item.setPointerCapture(e.pointerId); e.preventDefault(); }); item.addEventListener("pointermove", function (e) { if (!activeDrag || activeDrag.node !== item) return; var b = boardRect(); var x = clamp(e.clientX - b.left - activeDrag.offsetX, 8, b.width - item.offsetWidth - 8); var y = clamp(e.clientY - b.top - activeDrag.offsetY, 8, b.height - item.offsetHeight - 8); item.style.left = x + "px"; item.style.top = y + "px"; if (activeDrag.isLogo) { item.classList.add("is-positioned"); } else { item.style.removeProperty("--x"); item.style.removeProperty("--y"); } requestLineUpdate(); }); item.addEventListener("pointerup", function (e) { if (!activeDrag || activeDrag.node !== item) return; item.classList.remove("is-dragging"); item.releasePointerCapture(e.pointerId); activeDrag = null; requestLineUpdate(); }); item.addEventListener("pointercancel", function () { item.classList.remove("is-dragging"); activeDrag = null; }); }); window.addEventListener("resize", requestLineUpdate, { passive: true }); updateLines(); setTimeout(requestLineUpdate, 150); } /* ---- Produktwirkung: klickbare Workflow-Zustaende ---- */ var productUi = document.querySelector(".product-ui"); if (productUi) { var productStates = { eingang: { kicker: "Heute · 14 aktive Vorgänge", title: "Workflow Control", pill: "3 Blocker", flow: ["Anfrage", "Prüfung", "Angebot", "Termin", "Rechnung"], active: 1, done: 0, metrics: [["Antwortzeit", "18 min"], ["Auto-Schritte", "74%"], ["Offene Übergaben", "03"]], rows: [["Kunde wartet auf Rückmeldung", "Jetzt", "is-hot"], ["Dokumente aus Postfach übernommen", "Auto", ""], ["Team-Zuständigkeit gesetzt", "11:15", ""], ["Status an Leitung gemeldet", "Erledigt", "is-done"]] }, angebote: { kicker: "Vertrieb · 8 Angebote offen", title: "Angebote steuern", pill: "2 fällig", flow: ["Lead", "Kalkulation", "Freigabe", "Versand", "Follow-up"], active: 2, done: 1, metrics: [["Durchlauf", "2,4 h"], ["Vorlagen", "91%"], ["Rückfragen", "05"]], rows: [["Angebot braucht interne Freigabe", "Heute", "is-hot"], ["PDF aus Kundendaten erstellt", "Auto", ""], ["Nachfass-Termin gesetzt", "Morgen", ""], ["Alte Version archiviert", "Erledigt", "is-done"]] }, planung: { kicker: "Team · 21 Einsätze geplant", title: "Planung sichtbar", pill: "1 Konflikt", flow: ["Auftrag", "Ressource", "Slot", "Route", "Update"], active: 2, done: 1, metrics: [["Auslastung", "82%"], ["Auto-Routen", "68%"], ["Konflikte", "01"]], rows: [["Termin überschneidet sich", "Prüfen", "is-hot"], ["Route für Außendienst gebaut", "Auto", ""], ["Materialliste angehängt", "09:40", ""], ["Kunde informiert", "Erledigt", "is-done"]] }, abschluss: { kicker: "Abschluss · 36 Vorgänge fertig", title: "Betrieb im Blick", pill: "0 kritisch", flow: ["Abnahme", "Doku", "Rechnung", "Feedback", "Archiv"], active: 3, done: 2, metrics: [["Abnahmen", "96%"], ["Auto-Doku", "88%"], ["Offen", "00"]], rows: [["Feedback für nächste Version notiert", "Neu", ""], ["Rechnung vorbereitet", "Auto", ""], ["Abnahme protokolliert", "13:20", "is-done"], ["Projekt sauber archiviert", "Erledigt", "is-done"]] } }; var productTabs = Array.prototype.slice.call(productUi.querySelectorAll("[data-product-tab]")); var productKicker = productUi.querySelector("[data-product-kicker]"); var productTitle = productUi.querySelector("[data-product-title]"); var productPill = productUi.querySelector("[data-product-pill]"); var productFlow = productUi.querySelector("[data-product-flow]"); var productMetrics = productUi.querySelector("[data-product-metrics]"); var productTable = productUi.querySelector("[data-product-table]"); function setProductState(key) { var state = productStates[key]; if (!state) return; productUi.classList.add("is-switching"); productTabs.forEach(function (tab) { tab.classList.toggle("active", tab.dataset.productTab === key); }); window.setTimeout(function () { if (productKicker) productKicker.textContent = state.kicker; if (productTitle) productTitle.textContent = state.title; if (productPill) productPill.textContent = state.pill; if (productFlow) { productFlow.innerHTML = state.flow.map(function (label, index) { var cls = index === state.active ? "active" : (index <= state.done ? "done" : ""); return '' + label + ''; }).join(""); } if (productMetrics) { productMetrics.innerHTML = state.metrics.map(function (metric) { return "
" + metric[0] + "" + metric[1] + "
"; }).join(""); } if (productTable) { productTable.innerHTML = state.rows.map(function (row) { return '
' + row[0] + '' + row[1] + '
'; }).join(""); } productUi.classList.remove("is-switching"); }, 90); } productTabs.forEach(function (tab) { tab.addEventListener("click", function () { setProductState(tab.dataset.productTab); }); }); } /* ---- Lead-Formular ---- */ var form = document.getElementById("lead-form"); var status = document.getElementById("form-status"); if (!form) return; function setStatus(msg, kind) { if (!status) return; status.textContent = msg; status.className = "form-status " + (kind || ""); status.hidden = false; } function fieldRow(input) { return input.closest(".form-row"); } function validate() { var ok = true; ["f-name", "f-email", "f-nachricht"].forEach(function (id) { var el = document.getElementById(id); var row = fieldRow(el); var valid = el.value.trim() !== ""; if (id === "f-email" && valid) { valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(el.value.trim()); } if (row) row.classList.toggle("invalid", !valid); if (!valid) ok = false; }); return ok; } form.addEventListener("submit", function (e) { e.preventDefault(); if (!validate()) { setStatus("Bitte füllen Sie die markierten Pflichtfelder aus.", "err"); return; } var btn = form.querySelector("button[type=submit]"); var payload = { name: form.name.value.trim(), firma: form.firma.value.trim(), email: form.email.value.trim(), telefon: form.telefon.value.trim(), nachricht: form.nachricht.value.trim(), website: form.website.value.trim(), // Honeypot quelle: "Messe — Landing Page" }; if (btn) { btn.disabled = true; btn.textContent = "Wird gesendet …"; } setStatus("Anfrage wird übermittelt …", ""); fetch("/api/lead", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }) .then(function (res) { return res.json().then(function (data) { return { ok: res.ok, data: data }; }); }) .then(function (r) { if (r.ok) { form.reset(); setStatus( "Vielen Dank — Ihre Anfrage ist bei uns eingegangen. Wir melden uns zeitnah.", "ok" ); } else { setStatus( (r.data && r.data.error) || "Es ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.", "err" ); } }) .catch(function () { setStatus( "Verbindung fehlgeschlagen. Bitte schreiben Sie uns direkt an info@tool-ctrl.de.", "err" ); }) .finally(function () { if (btn) { btn.disabled = false; btn.textContent = "Anfrage senden"; } }); }); /* Markierung beim Tippen entfernen */ form.querySelectorAll("input, textarea").forEach(function (el) { el.addEventListener("input", function () { var row = fieldRow(el); if (row) row.classList.remove("invalid"); }); }); })();