/* Multi-step Funnel + Footer */ function StepDot({ active, done }) { return (
); } function ChoiceCard({ icon: Icon, label, selected, onClick, emoji }) { return ( ); } function Field({ label, value, onChange, type = "text", error, placeholder }) { return ( ); } /* Calendar logic helpers */ function isSameDay(a, b) { return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate(); } function startOfDay(d) { const x = new Date(d); x.setHours(0,0,0,0); return x; } function addDays(d, n) { const x = new Date(d); x.setDate(x.getDate() + n); return x; } function getEarliestSelectable(today) { let candidate = addDays(startOfDay(today), 2); // Must be Mon–Fri while (candidate.getDay() === 0 || candidate.getDay() === 6) { candidate = addDays(candidate, 1); } return candidate; } function isDateDisabled(d, today, earliest) { const day = d.getDay(); if (day === 0 || day === 6) return true; if (startOfDay(d) < earliest) return true; return false; } function CustomCalendar({ value, onChange, today }) { const [view, setView] = useState(() => { const t = startOfDay(today); return new Date(t.getFullYear(), t.getMonth(), 1); }); const earliest = useMemo(() => getEarliestSelectable(today), [today]); const monthName = view.toLocaleDateString("de-DE", { month: "long", year: "numeric" }); // Build grid: Monday-first const firstDay = new Date(view.getFullYear(), view.getMonth(), 1); const lastDay = new Date(view.getFullYear(), view.getMonth() + 1, 0); const startWeekday = (firstDay.getDay() + 6) % 7; // Mon=0 const daysInMonth = lastDay.getDate(); const cells = []; for (let i = 0; i < startWeekday; i++) cells.push(null); for (let d = 1; d <= daysInMonth; d++) cells.push(new Date(view.getFullYear(), view.getMonth(), d)); while (cells.length % 7 !== 0) cells.push(null); const canPrev = !(view.getFullYear() === today.getFullYear() && view.getMonth() === today.getMonth()); return (
{monthName}
{["Mo","Di","Mi","Do","Fr","Sa","So"].map(d =>
{d}
)}
{cells.map((d, i) => { if (!d) return
; const disabled = isDateDisabled(d, today, earliest); const selected = value && isSameDay(d, value); const isToday = isSameDay(d, today); return ( ); })}
); } function TimeSlots({ value, onChange }) { const slots = useMemo(() => { const out = []; for (let h = 9; h <= 18; h++) { out.push(`${String(h).padStart(2,"0")}:00`); out.push(`${String(h).padStart(2,"0")}:30`); } return out; }, []); return (
{slots.map((s) => { const selected = value === s; return ( ); })}
); } /* TODO: Cal.com / Calendly Embed an dieser Stelle einsetzen — Custom-Kalender bis dahin als UI-Platzhalter */ function Funnel() { const today = useMemo(() => new Date(), []); const [step, setStep] = useState(1); const [data, setData] = useState({ type: "", size: "", budget: "", name: "", company: "", email: "", phone: "", date: null, time: "", }); const [errors, setErrors] = useState({}); const [submitted, setSubmitted] = useState(false); const totalSteps = 6; const upd = (k, v) => setData((d) => ({ ...d, [k]: v })); const canNext = () => { if (step === 1) return !!data.type; if (step === 2) return !!data.size; if (step === 3) return !!data.budget; if (step === 4) { const e = {}; if (!data.name.trim()) e.name = "Bitte angeben"; if (!data.company.trim()) e.company = "Bitte angeben"; if (!data.email.trim() || !/^\S+@\S+\.\S+$/.test(data.email)) e.email = "Gültige E-Mail bitte"; if (!data.phone.trim() || data.phone.replace(/\D/g, "").length < 6) e.phone = "Gültige Nummer bitte"; return Object.keys(e).length === 0; } if (step === 5) return !!data.date && !!data.time; return true; }; const next = () => { if (step === 4 && !canNext()) { const e = {}; if (!data.name.trim()) e.name = "Bitte angeben"; if (!data.company.trim()) e.company = "Bitte angeben"; if (!data.email.trim() || !/^\S+@\S+\.\S+$/.test(data.email)) e.email = "Gültige E-Mail bitte"; if (!data.phone.trim() || data.phone.replace(/\D/g, "").length < 6) e.phone = "Gültige Nummer bitte"; setErrors(e); return; } setErrors({}); setStep((s) => Math.min(totalSteps, s + 1)); }; const prev = () => setStep((s) => Math.max(1, s - 1)); const submit = () => setSubmitted(true); const ref = useReveal(); const progress = ((step - 1) / (totalSteps - 1)) * 100; return (
Erstgespräch

In 6 Schritten zum Termin.

Kein Sales-Pitch. Wir hören zu, bauen einen klaren Fahrplan und Sie entscheiden.

{!submitted ? ( <> {/* Progress */}
Schritt {step} von {totalSteps}
{Array.from({ length: totalSteps }).map((_, i) => ( ))}
{/* Steps */} {step === 1 && (

Was möchten Sie umsetzen?

Wählen Sie das Hauptprojekt — Mehrfaches geht später.

upd("type", "Webseite")} /> upd("type", "Software")} /> upd("type", "Funnel-System")} />
)} {step === 2 && (

Wie groß ist Ihr Unternehmen?

So können wir den Umfang einschätzen.

{["Solo / Einzelunternehmer", "2–10 Mitarbeiter", "11–50 Mitarbeiter", "50+ Mitarbeiter"].map((opt) => ( upd("size", opt)} /> ))}
)} {step === 3 && (

Ihr Budget?

Für eine erste Orientierung — kein Festpreis.

{[ ["149–299 € / Monat", "Starter / Standard"], ["299–599 € / Monat", "Standard / Growth"], ["599 € + / Monat", "Growth oder individuelle Lösung"], ].map(([opt, sub]) => { const selected = data.budget === opt; return ( ); })}
)} {step === 4 && (

Kontaktdaten

Damit wir den Termin bestätigen können.

upd("name", v)} placeholder="Max Mustermann" error={errors.name} /> upd("company", v)} placeholder="Mustermann GmbH" error={errors.company} /> upd("email", v)} placeholder="max@firma.de" error={errors.email} /> upd("phone", v)} placeholder="+49 1512 9683401" error={errors.phone} />
)} {step === 5 && (

Termin auswählen

Mo–Fr, 9:00–18:30 Uhr. Frühestens in 2 Werktagen.

upd("date", d)} today={today} />
{data.date ? ( <>
Verfügbare Zeiten am {data.date.toLocaleDateString("de-DE", { weekday: "long", day: "2-digit", month: "long" })}
upd("time", t)} /> ) : (
Wählen Sie zuerst ein Datum, um die verfügbaren Zeiten zu sehen.
)}
)} {step === 6 && (

Alles passt?

Hier die Übersicht Ihrer Anfrage.

{[ ["Projekt", data.type], ["Unternehmensgröße", data.size], ["Budget", data.budget], ["Name", data.name], ["Firma", data.company], ["E-Mail", data.email], ["Telefon", data.phone], ["Termin", data.date ? `${data.date.toLocaleDateString("de-DE", { weekday: "long", day: "2-digit", month: "long", year: "numeric" })} um ${data.time}` : ""], ].map(([k, v], i) => (
{k}
{v}
))}
)} {/* Nav */}
{step < totalSteps ? ( ) : ( )}
) : (

Vielen Dank!

Wir melden uns innerhalb von 24h bei Ihnen mit der Terminbestätigung.

)}
); } /* ---------------- Footer ---------------- */ function Footer() { return ( ); } Object.assign(window, { Funnel, Footer });