/* 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.tag}
))}
);
}
function ProjectMock({ name, tag, bg }) {
return (
);
}
function PricingCard({ tier, recommended, animStyle }) {
return (
);
}
function PricingCardStatic({ tier, recommended }) {
return (
);
}
function PricingCardInner({ tier, recommended }) {
return (
{recommended && (
★ EMPFOHLEN
)}
Paket {tier.num}
0{tier.num}
{tier.name}
{tier.tagline}
Setup einmalig: {tier.setup}€
{tier.features.map((f, i) => (
-
{f}
))}
);
}
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() {
return (
{/* Sidebar */}
{[
[LayoutDashboard, "Dashboard", true],
[ClipboardList, "Aufträge"],
[Users, "Mitarbeiter"],
[CalendarDays, "Kalender"],
[Settings, "Einstellungen"],
].map(([Icon, label, active], i) => (
{label}
))}
{/* Main */}
{/* Header */}
{/* Content */}
{/* Stat tiles */}
{[
["12", "offene Aufträge"],
["8", "Termine heute"],
["3", "Anfragen"],
["94%", "ausgelastet"],
].map(([v, l], i) => (
))}
{/* Table */}
Aktuelle Aufträge
5 Einträge
| Nr. | Kunde | Status | Datum |
{[
["#A-2148", "Müller GmbH", "In Arbeit", "blue", "27.04."],
["#A-2147", "Schmidt KG", "Geplant", "gray", "28.04."],
["#A-2146", "Wildschütz", "Abgeschl.", "green", "26.04."],
["#A-2145", "Koch Projekt", "In Arbeit", "blue", "25.04."],
["#A-2144", "Liah Fahrschule", "Anfrage", "amber", "24.04."],
].map(([nr, k, st, c, d], i) => (
| {nr} |
{k} |
{st}
|
{d} |
))}
{/* Mini calendar */}
{["M","D","M","D","F","S","S"].map((d, i) =>
{d}
)}
{[27,28,29,30,1,2,3].map((d, i) => (
= 5 ? "transparent" : "rgba(255,255,255,0.03)"), color: i === 0 ? "#000" : (i >= 5 ? "rgba(255,255,255,0.3)" : "rgba(255,255,255,0.85)") }}>{d}
))}
{[
["09:00", "Müller GmbH"],
["11:30", "Vor-Ort Termin"],
["14:00", "Schmidt KG"],
].map(([t, l], i) => (
))}
);
}
function IntrawarePhoneMock() {
return (
{/* Status bar */}
{/* Header */}
{/* Quick stats */}
{/* List */}
Aufträge heute
{[
["Müller GmbH", "Industriestr. 12, Kaarst", "09:00"],
["Schmidt KG", "Hauptstr. 45, Neuss", "11:30"],
["Wildschütz", "Bahnhofstr. 8, Düsseldorf", "14:00"],
].map(([k, a, t], i) => (
))}
{/* Bottom tab bar */}
{[Home, ClipboardList, CalendarDays, User].map((Icon, i) => (
))}
);
}
function Intraware() {
const ref = useReveal();
return (
Bald im Launch
Intraware. Software, die Handwerk versteht.
Unsere eigene Plattform für Auftragsmanagement, Mitarbeiter-Koordination und interne Prozesse — entwickelt für Handwerksbetriebe.
{[
[ClipboardList, "Auftragsmanagement"],
[Users, "Mitarbeiter-Koordination"],
[Smartphone, "Mobile App"],
[WifiOff, "Offline-fähig"],
[Lock, "DSGVO-konform"],
].map(([Icon, label], i) => (
-
{label}
))}
Mehr über Intraware
);
}
/* ---------------- Testimonials ---------------- */
function TestimonialCard({ name, company, quote }) {
return (
{[0,1,2,3,4].map(i => )}
{quote}
);
}
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 (
);
}
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 });