/* References (horizontal scroll), Pricing card-stack, Intraware, Testimonials, FAQ */ function HorizontalReferences() { const headerRef = useReveal(); const wrapRef = useRef(null); const trackRef = useRef(null); const [trackTx, setTrackTx] = useState(0); const [trackWidth, setTrackWidth] = useState(0); const projects = [ { name: "Elektro Wildschütz GmbH", branch: "Elektrotechnik", tag: "Web Entwicklung", img: "ref-wildschuetz.png", color: "#0e3a8a" }, { name: "Intraware", branch: "Eigenentwicklung", tag: "Software", img: "Intraware Bild.png", color: "#0a1855" }, { name: "Elektrotechnik Müllers", branch: "Elektrotechnik", tag: "Design", img: "6.png", color: "#1c1c20" }, { name: "Fahrschule Liah UG", branch: "Fahrschule", tag: "Web Entwicklung", img: "5.png", color: "#103a3a" }, { name: "Elektrotechnik Koch Projekt", branch: "Elektrotechnik", tag: "Web Entwicklung", img: "ref-koch.png", color: "#2a1a36" }, ]; useLayoutEffect(() => { const t = trackRef.current; if (!t) return; const measure = () => { setTrackWidth(Math.max(0, t.scrollWidth - window.innerWidth + 40)); }; measure(); const ro = new ResizeObserver(measure); ro.observe(t); window.addEventListener("resize", measure); // Re-measure after fonts/images load const t1 = setTimeout(measure, 200); const t2 = setTimeout(measure, 800); return () => { ro.disconnect(); window.removeEventListener("resize", measure); clearTimeout(t1); clearTimeout(t2); }; }, []); useEffect(() => { const update = () => { const el = wrapRef.current; if (!el) return; const rect = el.getBoundingClientRect(); const vh = window.innerHeight; const total = rect.height - vh; const p = Math.min(1, Math.max(0, (-rect.top) / total)); setTrackTx(-p * trackWidth); }; update(); let raf = 0; const onScroll = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(update); }; window.addEventListener("scroll", onScroll, { passive: true }); return () => { window.removeEventListener("scroll", onScroll); cancelAnimationFrame(raf); }; }, [trackWidth]); // Sync scroll-distance to translate-distance: 1px scroll ≈ 1px translate. // Add ~30vh padding on either side so first/last card breathe. const sectionHeight = `calc(100vh + ${trackWidth + 200}px)`; return (
Projekte

Projekte, die beeindrucken.

Ein Auszug aktueller Arbeiten — von Handwerksbetrieben bis zur eigenen Plattform.

{projects.map((p, i) => (
{p.img ? ( {p.name} ) : ( )}
{p.tag}
{p.name}
{p.branch}
))}
); } function ProjectMock({ name, tag, bg }) { return (
{tag}
{name}
); } function PricingCard({ tier, recommended, animStyle }) { return (
); } function PricingCardStatic({ tier, recommended }) { return (
); } function PricingCardInner({ tier, recommended }) { const budgetMap = { 1: "149–299 € / Monat", 2: "299–599 € / Monat", 3: "599 € + / Monat" }; const selectPackage = () => { window.dispatchEvent(new CustomEvent("selectPackage", { detail: { budget: budgetMap[tier.num], type: "Webseite" } })); document.getElementById("kontakt")?.scrollIntoView({ behavior: "smooth" }); }; return ( {recommended && (
★ EMPFOHLEN
)}
Paket {tier.num}
0{tier.num}
{tier.name}
{tier.tagline}
{tier.monthly}€
/ Monat
Setup einmalig: {tier.setup}€
{recommended ? ( ) : ( )}
); } function PricingStack() { const wrapRef = useRef(null); const [progress, setProgress] = useState(0); useEffect(() => { const update = () => { const el = wrapRef.current; if (!el) return; const rect = el.getBoundingClientRect(); const vh = window.innerHeight; const total = rect.height - vh; const p = Math.min(1, Math.max(0, (-rect.top) / total)); setProgress(p); }; update(); let raf = 0; const onScroll = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(update); }; window.addEventListener("scroll", onScroll, { passive: true }); window.addEventListener("resize", onScroll); return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); cancelAnimationFrame(raf); }; }, []); const tiers = [ { num: 1, name: "Starter", tagline: "Für Einzelunternehmer und kleine Teams mit klarem Auftritt.", monthly: "149", setup: "990", features: ["Website 1–3 Seiten", "Hosting + SSL", "Backups", "Updates", "1 h Änderungen / Monat"], }, { num: 2, name: "Standard", tagline: "Unsere meistgewählte Option für etablierte Mittelständler.", monthly: "299", setup: "990", features: ["Website 5–8 Seiten", "Alles aus Starter", "3 h Änderungen / Monat", "SEO-Basics", "Quartals-Strategie-Call"], }, { num: 3, name: "Growth", tagline: "Für Unternehmen, die aktiv über die Website wachsen wollen.", monthly: "599", setup: "1.990", features: ["Alles aus Standard", "Aktive SEO-Arbeit", "1 Landingpage / Monat", "Monats-Strategie-Call", "Priorisierter Support"], }, ]; // Phase 1 (0..0.45): cards stacked // Phase 2 (0.45..1): fan out to side-by-side const ease = (t) => 1 - Math.pow(1 - t, 3); const fan = ease(Math.max(0, Math.min(1, (progress - 0.15) / 0.7))); // Stacked offsets when fan=0 const stackOffsets = [ { x: 0, y: 30, scale: 0.92, rot: -3 }, { x: 0, y: 0, scale: 1.00, rot: 0 }, { x: 0, y: -30, scale: 0.92, rot: 3 }, ]; // Responsive spread — keeps cards centered around viewport midpoint const vw = typeof window !== "undefined" ? window.innerWidth : 1200; const isMobile = vw < 768; const spread = Math.min(360, (vw - 360) / 2 - 20); const finalOffsets = [ { x: -spread, y: 0, scale: 0.95, rot: 0 }, { x: 0, y: -10, scale: 1.06, rot: 0 }, { x: spread, y: 0, scale: 0.95, rot: 0 }, ]; // On mobile, render a simple vertical stack (no scroll-jacking) so all cards are visible. if (isMobile) { return (
Pakete & Preise

3 Pakete.
Klare Preise.

Webdesign und laufende Betreuung — alle Pakete mit 12 Monaten Mindestlaufzeit.

{tiers.map((t, i) => (
))}
{[ [Calendar, "Setup: 2–4 Wochen"], [Clock, "12 Monate Laufzeit"], [Zap, "< 24h Reaktionszeit"], ].map(([Icon, label], i) => (
{label}
))}
Alle Preise zzgl. MwSt.
); } return (
Pakete & Preise

3 Pakete. Klare Preise. Keine Überraschungen.

Webdesign und laufende Betreuung — alle Pakete mit 12 Monaten Mindestlaufzeit.

{tiers.map((t, i) => { const start = stackOffsets[i]; const end = finalOffsets[i]; const x = start.x + (end.x - start.x) * fan; const y = start.y + (end.y - start.y) * fan; const sc = start.scale + (end.scale - start.scale) * fan; const rot = start.rot + (end.rot - start.rot) * fan; const z = i === 1 ? 30 : (fan > 0.5 ? 10 : (i === 2 ? 20 : 5)); return ( ); })}
{[ [Calendar, "Setup-Phase: 2–4 Wochen"], [Clock, "Mindestlaufzeit: 12 Monate"], [Zap, "Reaktionszeit: < 24 Stunden"], ].map(([Icon, label], i) => (
{label}
))}
Alle Preise zzgl. MwSt.
); } /* ---------------- Intraware ---------------- */ function IntrawareLaptopMock() { const pur = "#6d28d9"; const purDark = "#4c1d95"; return (
{/* Sidebar — very dark */}
IW
Intraware
{[ [LayoutDashboard, "Dashboard", true], [ClipboardList, "Aufträge"], [Users, "Mitarbeiter"], [MapPin, "Fuhrpark"], [FileText, "Personalakte"], [LayoutDashboard, "Inventar"], [CalendarDays, "Kalender"], [Settings, "Einstellungen"], ].map(([Icon, label, active], i) => (
{label}
))}
{/* Main — light */}
{/* Header */}
Übersicht
Deine Data GmbH
Suchen…
LK
{/* Content */}
{/* Left column */}
{/* Purple user card */}
LK
Laurin Henze
Mitarbeiter
1
Flotte
0
{/* Metric grid */}
{[["9","Aufträge gesamt"],["0","Rechnungen offen"],["0","IT-Services"],["0","Aktive Anfragen"]].map(([v,l],i)=>(
{v}
{l}
))}
{/* Right column */}
{/* Dark card */}
Aktive Aufträge
2
Einnahmen 0,00 €
Ausgaben 0,00 €
{/* White card */}
Nächster Termin
Müller GmbH
09:00 Uhr
{/* Inventory card */}
Inventar
48 Artikel
2 Nachbestellung
); } function IntrawarePhoneMock() { const pur = "#6d28d9"; const purDark = "#4c1d95"; return (
{/* Status bar */}
9:41
{/* Purple header card */}
Mo, 27. Apr
Guten Morgen,
Laurin!
3
Aufträge
2
Termine
{/* List */}
Aufträge heute
{[ ["Müller GmbH", "Kaarst", "09:00"], ["Schmidt KG", "Neuss", "11:30"], ["Wildschütz", "Düsseldorf", "14:00"], ].map(([k, a, t], i) => (
{k}
{a}
{t}
))}
{/* Bottom tab bar */}
{[Home, ClipboardList, CalendarDays, User].map((Icon, i) => (
))}
); } function Intraware() { const ref = useReveal(); return (
Bald im Launch

Intraware. Software, die Handwerk versteht.

Die All-in-One Lösung für Handwerk & KMU — Auftragsmanagement, Fuhrparkverwaltung, Personalakte und Inventar-Tracking in einer DSGVO-konformen Cloud aus Deutschland.

    {[ [ClipboardList, "Auftragsmanagement"], [Users, "Mitarbeiter-Koordination"], [MapPin, "Fuhrparkverwaltung"], [FileText, "Personalakte & HR"], [LayoutDashboard, "Inventar-Tracking"], [Smartphone, "Mobile App"], [WifiOff, "Offline-fähig"], [Lock, "DSGVO-konform · Cloud aus Deutschland"], ].map(([Icon, label], i) => (
  • {label}
  • ))}
Mehr über Intraware
); } /* ---------------- Testimonials ---------------- */ function TestimonialCard({ name, company, quote }) { return (
{[0,1,2,3,4].map(i => )}

{quote}

{name}
{company}
); } function Testimonials() { const ref = useReveal(); const items = [ { name: "Mike Koch", company: "Elektrotechnik Koch Projekt GmbH", quote: "Unsere neue Webseite begeistert! Der geschützte Login-Bereich und interne Event-Kalender sind perfekt gelöst. Modernes Design, intuitive Bedienung und professionelle Zusammenarbeit überzeugen auf ganzer Linie. Vielen Dank!" }, { name: "Caner Yaylacioglu", company: "Fahrschule Liah UG", quote: "Unsere neue Fahrschul-Webseite begeistert! Das moderne Design und die eins zu eins umgesetzten Wünsche sind perfekt gelöst. Erstklassiger Social Media Content, positive Rückmeldungen und aktive Neukunden überzeugen auf ganzer Linie. Vielen Dank!" }, { name: "Volker Müllers", company: "Elektrotechnik Müllers", quote: "Hervorragende Arbeit für unsere Elektrotechnik-Firma! Die neue Webseite ist modern, übersichtlich und genau so, wie wir sie uns vorgestellt haben. Alle Wünsche wurden berücksichtigt und schnell umgesetzt. Vielen Dank für die erstklassige Betreuung!" }, ]; // Duplicate for marquee loop const doubled = [...items, ...items]; return (
Stimmen

Was Kunden sagen.

{doubled.map((t, i) => )}
); } /* ---------------- FAQ ---------------- */ function FAQItem({ q, a, open, onClick }) { return (

{a}

); } function FAQ() { const ref = useReveal(); const [openIdx, setOpenIdx] = useState(0); const items = [ { q: "Wie viel Zeit wird das kommende Projekt in Anspruch nehmen?", a: "Wir wissen, dass Ihre Zeit begrenzt ist und wollen Sie in Sachen Digitalisierung so gut wie möglich entlasten. Deshalb benötigen wir nur 30-45 Minuten Gesprächszeit mit Ihnen, um Ihre Vorstellung zu strukturieren und abzusprechen." }, { q: "Wann können Sie mit ersten Ergebnissen rechnen?", a: "Bereits nach wenigen Wochen können Sie mit einer überzeugenden Webseite und einer stabilen IT-Infrastruktur rechnen. Die genaue Dauer hängt jedoch stark vom jeweiligen Projekt ab: Umfang, Komplexität und individuelle Anforderungen bestimmen, wie lange die Entwicklung einer maßgeschneiderten Software oder Webseite dauert. Nach einer gemeinsamen Bedarfsanalyse erhalten Sie von uns einen realistischen Zeitplan." }, { q: "Wie sieht ein Start bei euch aus?", a: "Ein Start bei uns ist grundsätzlich ganz leicht: Buchen Sie vorab über unsere Webseite ein kostenloses Erstgespräch. Anschließend melden wir uns bei Ihnen mit einer Terminbestätigung. Gerne können Sie sich vor dem Gespräch bereits Gedanken machen, was Sie sich für Ihr Projekt wünschen — sei es eine Webseite oder individuelle Software. Gemeinsam erstellen wir im Gespräch einen klaren Fahrplan, wie wir Sie und Ihr Projekt bestmöglich unterstützen können. Sobald Sie uns Ihr Okay geben, starten wir direkt mit der Umsetzung und setzen Ihre Wünsche schnellstmöglich und zielgerichtet um." }, { q: "Wie nutzen / erstellen wir Content für Ihre Webseite?", a: "Wenn Sie bereits Content für Ihre Webseite haben, können Sie uns diesen gerne zur Verfügung stellen. Wichtig zu wissen ist, dass wir die Auflösung vorhandener Bilder nicht nachträglich verbessern können. Sollten ältere Bilder verwendet werden, können wir diese daher nur in der bestehenden Qualität einsetzen. Professionelle Bilder und Videos in hoher Qualität lassen Ihre Webseite deutlich hochwertiger und überzeugender wirken. Sollte bei Ihnen Bedarf bestehen, verfügen wir über ein eigenes Content-Team, das gerne zu Ihrem Standort kommt." }, ]; return (
FAQ

Häufige Fragen.

{items.map((it, i) => ( setOpenIdx(openIdx === i ? -1 : i)} /> ))}
); } Object.assign(window, { HorizontalReferences, PricingStack, Intraware, Testimonials, FAQ });