// home.jsx — landing page
const homeUseCases = [
{
num: "01",
icon: "mail",
title: "E-Mail & Kommunikation",
body: "KI klassifiziert eingehende E-Mails, leitet sie ans richtige Team weiter und erstellt einen ersten Antwortentwurf.",
tools: ["gmail", "n8n", "outlook"],
hours: "3–6",
},
{
num: "02",
icon: "file",
title: "Dokumente & Rechnungen",
body: "Betrag, Datum und Lieferant aus PDFs auslesen. Direkter Export in DATEV, SAP oder Sheets.",
tools: ["datev", "sap", "n8n"],
hours: "5–10",
},
{
num: "03",
icon: "chart",
title: "Reporting & Daten",
body: "Vollautomatischer Reportversand aus euren Quellsystemen — inkl. KI-Kommentar zu Auffälligkeiten.",
tools: ["sheets", "powerbi", "n8n"],
hours: "4–8",
},
{
num: "04",
icon: "users",
title: "HR & Onboarding",
body: "Vertrag unterschrieben — und alles läuft: Account, Slack, IT, Checkliste, Willkommensmail. Ohne einen Klick.",
tools: ["workspace", "slack", "notion"],
hours: "2–4",
},
{
num: "05",
icon: "handshake",
title: "Vertrieb & CRM",
body: "Lead-Qualifizierung, Angebotsentwurf auf Knopfdruck, CRM-Einträge schreiben sich selbst.",
tools: ["hubspot", "pipedrive", "n8n"],
hours: "5–8",
},
];
const homeFaqs = [
{
q: "Wie schnell läuft so ein Workflow?",
a: "Wir fangen klein an, damit ihr sofort Ergebnisse seht. Einfache Lösungen (wie E-Mail-Sortierung) stehen in 3–5 Tagen. Komplexere Vernetzungen dauern meist 1–2 Wochen. Wir bauen agil: Der wichtigste Zeitfresser wird zuerst eliminiert.",
},
{
q: "Bleiben unsere Daten wirklich in der EU?",
a: "Ja, ohne Wenn und Aber. Wir nutzen Hosting-Partner in Deutschland oder Finnland und Modelle von europäischen Anbietern (wie Mistral oder Aleph Alpha). Wenn gewünscht, installieren wir alles on-premise direkt auf euren eigenen Servern. Eure Daten verlassen niemals euren Kontrollbereich.",
},
{
q: "Brauchen wir eigenes IT-Personal dafür?",
a: "Nein. Wir sind eure externe Engineering-Abteilung. Wir kümmern uns um die Einrichtung und Anbindung. Euer Team muss keine einzige Zeile Code verstehen — ihr nutzt einfach das Ergebnis. Falls ihr eine eigene IT habt, arbeiten wir gerne mit ihnen zusammen, um den Zugang sicherzustellen.",
},
{
q: "Was passiert, wenn ein Workflow mal stockt?",
a: "Wir bauen robust, Error-Handling inklusive. Sollte sich eine Schnittstelle ändern (z.B. ein Update eurer Software), merken wir das oft, bevor ihr es tut. Wir bieten Wartungspakete an, damit eure Automatisierung so zuverlässig läuft wie euer Stromanschluss.",
},
{
q: "Können wir Änderungen auch selbst machen?",
a: "Unbedingt. Wir nutzen Low-Code-Tools wie n8n. Das bedeutet: Nach der Übergabe gehört der Prozess euch. Wir dokumentieren alles so einfach, dass eine technisch affine Person in eurem Team kleine Anpassungen selbst vornehmen kann. Ihr seid nicht an uns gekettet.",
},
];
// "Warum Treibiq" - 4 Reasons mit Click-to-Modal-Popup
const whyReasons = [
{
num: "01",
icon: "shield",
title: "DSGVO-konform per Default",
teaser: "Eure Daten bleiben bei euch — nicht in einer amerikanischen Cloud, nicht auf fremden Servern.",
detail: "Wir bauen mit EU-Hosting und EU-Modellen wie Mistral und Aleph Alpha. Wo nötig, richten wir alles komplett on-premise ein — direkt auf eurer Infrastruktur, ohne dass ein einziges Dokument euer Unternehmen verlässt. DSGVO ist bei uns kein Häkchen in einer Checkliste, sondern der Ausgangspunkt jeder Lösung.",
tags: ["EU-Hosting", "On-Premise möglich", "Keine US-Cloud"],
},
{
num: "02",
icon: "server",
title: "Open-Source-Stack ohne Abo-Falle",
teaser: "Wir setzen auf n8n: Open Source, selbst gehostet und komplett in eurem Besitz statt monatlicher Miet-Tools.",
detail: "Andere bauen Workflows in Zapier oder Make — und ihr zahlt monatlich für die reine Ausführung. Mit unserem n8n-Ansatz gibt es keine laufenden Tool-Kosten und keine Anbieter-Abhängigkeit. Der Workflow gehört euch — auch wenn wir morgen nicht mehr da sind. Ihr könnt ihn jederzeit lesen, anpassen und erweitern.",
tags: ["n8n statt Zapier", "Keine Abo-Kosten", "Ihr besitzt den Code"],
},
{
num: "03",
icon: "users",
title: "Technisch und persönlich",
teaser: "Kein Mittelsmann, keine Folien-Schubser. Ihr sprecht direkt mit den Engineers, die euren Code schreiben.",
detail: "Wir sprechen direkt mit den Menschen, die den Workflow täglich nutzen — nicht nur mit der Geschäftsführung. Wir schreiben den Code selbst, sitzen bei Fragen am Tisch und übergeben eine vollständige Dokumentation. Was wir gebaut haben, versteht ihr. Was ihr nicht versteht, erklären wir — so lange, bis es sitzt.",
tags: ["Kein Mittelsmann", "Direkter Draht", "Vollständig dokumentiert"],
},
{
num: "04",
icon: "zap",
title: "Pragmatisch, nicht überdimensioniert",
teaser: "Automatisierung in 1–2 Wochen statt monatelanger Steering Committees.",
detail: "Wir bauen in eurem Tempo und mit eurem vorhandenen Stack — egal ob Mittelstand, Start-up oder Konzern-Tochter. Keine Überdimensionierung, keine unnötigen Features, kein Projekt-Overhead. Die erste Automatisierung läuft schnell — dann schauen wir gemeinsam datenbasiert, was als nächster Schritt sinnvoll ist.",
tags: ["1–2 Wochen Umsetzung", "Euer Stack", "Kein Overhead"],
},
];
// "Was wir umsetzen" - 6 Workflows mit Click-to-Modal-Popup
const workflowsList = [
{
icon: "mail",
title: "Die intelligente Inbox",
sub: "E-Mail-Triage",
teaser: "KI sortiert eure Mails und schreibt Antwort-Entwürfe vor. Ihr klickt nur noch auf Senden.",
problem: "Das info@-Postfach ist ein schwarzes Loch. Anfragen gehen unter, Antworten kommen zu spät.",
loesung: "Die Flut an Anfragen wird automatisch kategorisiert. Rechnungen landen in der Buchhaltung, Termine im Kalender. Ihr startet den Tag nicht mit 100 Mails, sondern mit fertigen Lösungen.",
bauzeit: "1–2 Wochen",
ersparnis: "3–6 Std / Woche",
},
{
icon: "file",
title: "Digitale Poststelle",
sub: "Dokumenten-Ablage",
teaser: "Nie wieder Scan_123.pdf händisch umbenennen und verschieben. Die KI weiß, wo es hingehört.",
problem: "Scans landen im Download-Ordner, müssen einzeln umbenannt und in Projektordner verschoben werden.",
loesung: "Scans und Anhänge werden gelesen, erkannt und mit logischen Namen (Datum_Kunde_Typ) direkt in eurem Zielordner abgelegt. DATEV-Export inklusive.",
bauzeit: "1–2 Wochen",
ersparnis: "5–10 Std / Woche",
},
{
icon: "handshake",
title: "Angebots-Assistent",
sub: "Vertrieb",
teaser: "Aus euren Notizen oder Sprachnachrichten wird in Sekunden ein fertiges Angebot im Firmenlayout.",
problem: "Nach jedem Kundentermin sitzt jemand 30 Minuten am Angebot, kopiert aus Vorlagen, vertippt sich.",
loesung: "Ihr diktiert die Eckdaten nach dem Kundentermin ein — die KI formuliert daraus ein professionelles PDF, das sofort rausgehen kann.",
bauzeit: "1–2 Wochen",
ersparnis: "4–8 Std / Woche",
},
{
icon: "chart",
title: "Smartes Reporting",
sub: "Auswertung",
teaser: "Echte Zahlen statt Bauchgefühl. Automatische Berichte aus euren Daten direkt auf euer Handy.",
problem: "Reports werden monatlich aus Excel, CSV und drei Systemen zusammengeklickt. Immer wieder.",
loesung: "Wir ziehen Daten aus CRM, Excel oder Buchhaltung zusammen und erstellen Dashboards, die euch sagen, wo ihr profitabel seid — ohne dass ihr eine Liste öffnen müsst.",
bauzeit: "1–2 Wochen",
ersparnis: "4–8 Std / Woche",
},
{
icon: "users",
title: "Mitarbeiter-Onboarding",
sub: "HR",
teaser: "Neue Mitarbeiter im Team? Alle Konten, Verträge und Checklisten werden automatisch erstellt.",
problem: "Bei jedem neuen Mitarbeiter klickt jemand 2 Stunden durch 8 Tools, vergisst die Hälfte, IT chaotisiert los.",
loesung: "Ein Klick und der Workflow legt E-Mail, Slack und Zugriffsberechtigungen an. Kein IT-Chaos mehr am ersten Arbeitstag.",
bauzeit: "1–2 Wochen",
ersparnis: "2–4 Std / Woche",
},
{
icon: "sparkle",
title: "Das Company Brain",
sub: "Wissens-Chat",
teaser: "Stellt Fragen an eure eigenen Dokumente. Schluss mit dem Suchen in alten Handbüchern.",
problem: "Wissen liegt in 200 PDFs, Sharepoint-Ordnern und alten Protokollen. Niemand findet was.",
loesung: "Wir machen eure PDFs und Protokolle durchsuchbar. Mitarbeiter fragen einen Chatbot: 'Wie war die Regelung für X?' und erhalten sofort die Antwort aus euren Quellen.",
bauzeit: "1–2 Wochen",
ersparnis: "3–5 Std / Woche",
},
];
// -----------------------------
// HERO with hot-swappable visual
// -----------------------------
function Hero() {
const { tweaks } = useTweaks();
return (
Eure Routinen laufen. Eure Zeit gehört wieder euch.
Mit Treibiq automatisiert ihr alles, was sich täglich wiederholt und euch Zeit kostet.
);
}
// Mobile-only: 6 Tool-Logos verstreut platziert (wie Desktop floating),
// poppen nacheinander auf + schweben subtil, dann werden die Linien
// zwischen ihnen sequenziell als SVG-Pfade "gezeichnet" (stroke-dashoffset).
// Sitzt im Hero zwischen H1 und Lede. Auf Desktop per CSS versteckt.
function HeroMobileFlow() {
// Float-Parameter pro Logo: Frequenzen + Phasen + Amplituden fuer
// asynchrone Sinus-Bewegung. Werte sind so gewaehlt, dass keine zwei
// Logos synchron laufen (verschiedene Primzahl-aehnliche Frequenzen).
// ampX/ampY in % vom Container (200px hoch x ~360px breit auf Mobile).
// Amplituden (aX/aY) bewusst klein gehalten - User-Wunsch 2026-05-12:
// Bewegung soll auf Mobile dezenter sein. War 1.8-2.4 / 2.6-3.4, jetzt
// 0.9-1.2 / 1.3-1.7 -> ca. halbiert, sanftes Atmen statt Schweben.
const tools = [
{ name: "gmail", top: 22, left: 12, fX: 0.18, fY: 0.22, aX: 1.1, aY: 1.7, pX: 0.0, pY: 0.5 },
{ name: "n8n", top: 14, left: 50, fX: 0.22, fY: 0.16, aX: 0.9, aY: 1.5, pX: 1.2, pY: 2.0 },
{ name: "openai", top: 32, left: 86, fX: 0.20, fY: 0.24, aX: 1.2, aY: 1.4, pX: 2.4, pY: 0.8 },
{ name: "hubspot", top: 68, left: 78, fX: 0.17, fY: 0.20, aX: 1.0, aY: 1.5, pX: 0.6, pY: 1.4 },
{ name: "slack", top: 78, left: 38, fX: 0.25, fY: 0.18, aX: 1.0, aY: 1.3, pX: 1.8, pY: 2.6 },
{ name: "sheets", top: 54, left: 18, fX: 0.19, fY: 0.21, aX: 1.2, aY: 1.6, pX: 3.0, pY: 1.0 },
];
// Interconnected: n8n als Hub.
const connections = [
[0, 1], // gmail → n8n
[1, 2], // n8n → openai
[2, 3], // openai → hubspot
[3, 1], // hubspot → n8n (Quer-Verbindung)
[1, 4], // n8n → slack
[4, 5], // slack → sheets
];
const [phase, setPhase] = useState("logos");
const [step, setStep] = useState(0);
// Refs auf DOM-Elements - wir updaten sie pro RAF-Frame direkt (kein
// React re-render bei 60fps).
const posRefs = useRef([]);
const linePathRefs = useRef([]);
const pulsePathRefs = useRef([]);
useEffect(() => {
if (phase === "logos") {
if (step < tools.length) {
const t = setTimeout(() => setStep(step + 1), 220);
return () => clearTimeout(t);
} else {
const t = setTimeout(() => { setPhase("lines"); setStep(0); }, 400);
return () => clearTimeout(t);
}
}
if (phase === "lines" && step < connections.length) {
const t = setTimeout(() => setStep(step + 1), 480);
return () => clearTimeout(t);
}
}, [phase, step]);
// RAF-Loop: aktualisiert Logo-Positionen UND Pfad-Endpoints in einem
// Schritt. Damit folgen die Linien dem Float der Logos synchron.
// Performance-Fix 2026-05-13: Loop pausiert sobald der Hero-Container
// ausserhalb des Viewports ist (IntersectionObserver-Guard). Vorher
// lief der Loop permanent 60fps mit ~24 DOM-Writes pro Frame, auch
// wenn der User schon laengst weiter unten gescrollt war.
const containerRef = useRef(null);
useEffect(() => {
if (typeof window === "undefined") return;
const mediaReduced = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
if (mediaReduced) return;
let raf = null;
let isVisible = true;
let flowWidth = 0;
let flowHeight = 0;
const t0 = performance.now();
const TAU = Math.PI * 2;
const measureFlow = () => {
const rect = containerRef.current?.getBoundingClientRect();
flowWidth = rect?.width || 0;
flowHeight = rect?.height || 0;
};
measureFlow();
window.addEventListener("resize", measureFlow, { passive: true });
const bob = (tool, t) => ({
x: tool.aX * Math.sin(t * tool.fX * TAU + tool.pX),
y: tool.aY * Math.sin(t * tool.fY * TAU + tool.pY),
});
const buildD = (a, b) => {
const midX = (a.left + b.left) / 2;
const midY = (a.top + b.top) / 2;
const dx = b.left - a.left;
const dy = b.top - a.top;
const cx = midX - dy * 0.12;
const cy = midY + dx * 0.12;
return `M ${a.left.toFixed(2)} ${a.top.toFixed(2)} Q ${cx.toFixed(2)} ${cy.toFixed(2)} ${b.left.toFixed(2)} ${b.top.toFixed(2)}`;
};
const loop = () => {
if (!isVisible) { raf = null; return; }
const t = (performance.now() - t0) / 1000;
const positions = tools.map(tool => {
const b = bob(tool, t);
return {
top: tool.top + b.y,
left: tool.left + b.x,
xPx: (b.x / 100) * flowWidth,
yPx: (b.y / 100) * flowHeight,
};
});
for (let i = 0; i < tools.length; i++) {
const el = posRefs.current[i];
if (el) {
el.style.transform =
`translate3d(${positions[i].xPx.toFixed(2)}px, ${positions[i].yPx.toFixed(2)}px, 0) translate(-50%, -50%)`;
}
}
for (let i = 0; i < connections.length; i++) {
const [from, to] = connections[i];
const d = buildD(positions[from], positions[to]);
const lineEl = linePathRefs.current[i];
const pulseEl = pulsePathRefs.current[i];
if (lineEl) lineEl.setAttribute("d", d);
if (pulseEl) pulseEl.setAttribute("d", d);
}
raf = requestAnimationFrame(loop);
};
// IntersectionObserver: nur RAF starten wenn Hero im Viewport
const io = new IntersectionObserver((entries) => {
const entry = entries[0];
isVisible = entry.isIntersecting;
if (isVisible && raf === null) {
raf = requestAnimationFrame(loop);
}
}, { threshold: 0, rootMargin: "100px" });
const target = containerRef.current;
if (target) io.observe(target);
// Initial start (falls IO erst spaeter feuert)
raf = requestAnimationFrame(loop);
return () => {
isVisible = false;
if (raf) cancelAnimationFrame(raf);
window.removeEventListener("resize", measureFlow);
io.disconnect();
};
}, []);
// Static initial d-strings (fuer reduced-motion und ersten Render bevor
// RAF zuschlaegt)
const initialD = (from, to) => {
const a = tools[from];
const b = tools[to];
const midX = (a.left + b.left) / 2;
const midY = (a.top + b.top) / 2;
const dx = b.left - a.left;
const dy = b.top - a.top;
return `M ${a.left} ${a.top} Q ${midX - dy * 0.12} ${midY + dx * 0.12} ${b.left} ${b.top}`;
};
return (
{connections.map(([from, to], i) => {
const visible = phase === "lines" && i < step;
return (
{ linePathRefs.current[i] = el; }}
d={initialD(from, to)}
pathLength="100"
className={`hmf-line ${visible ? "is-in" : ""}`}
/>
{ pulsePathRefs.current[i] = el; }}
d={initialD(from, to)}
pathLength="100"
className={`hmf-pulse ${visible ? "is-in" : ""}`}
style={{ animationDelay: `${i * 0.7}s` }}
/>
);
})}
{tools.map((tool, i) => (
{ posRefs.current[i] = el; }}
className={`hero-mobile-flow__pos ${i < step || phase === "lines" ? "is-in" : ""}`}
style={{ top: `${tool.top}%`, left: `${tool.left}%` }}
>
))}
);
}
function HeroVisual({ variant }) {
if (variant === "stat") return ;
if (variant === "flow") return ;
return ;
}
// Variant A: video slot (default) — drop a video into
function HeroVideo() {
return (
{/*
Hier kann später ein Video eingebunden werden:
*/}
Eingang
KI klassifiziert
CRM-Eintrag
Slack-Notiz
Live-Workflow · Video-Slot
−7 Std
/ Woche im Schnitt
);
}
// Variant B: animated workflow nodes (no video frame)
function HeroFlow() {
return (
Gmail
Outlook
n8n + GPT-4
HubSpot
Slack
);
}
// Variant C: big stat card
function HeroStat() {
return (
Durchschnittliche Ersparnis
7 Std
pro Woche · pro Kunde
Über 20 Workflows live. Keine Theorie — gemessene Stunden, die sich Geschäftsführer zurückgeholt haben.
);
}
// -----------------------------
// Trust strip
// -----------------------------
function TrustStrip() {
const tools = ["gmail", "outlook", "n8n", "openai", "hubspot", "slack", "notion", "sheets", "datev"];
return (
Wir verbinden Tools wie
{tools.map((t) => )}
);
}
// -----------------------------
// Services section (Zenith-Pattern: 2x3 Grid mit Floating-Icon-Badges)
// 6 Service-Bereiche - jede Card hat Badge oben links (out-of-card),
// Title, Body, Link mit Pfeil. Section-Tint mit Dot-Pattern via background-image.
// -----------------------------
function ServicesSection() {
const services = [
{
icon: "mail",
title: "E-Mail-Triage & Antworten",
body: "Eingehende Mails klassifizieren, weiterleiten, Antwortentwurf schreiben. KI macht die Vorarbeit, ihr entscheidet final.",
href: "leistungen.html#email",
},
{
icon: "file",
title: "Dokumente & Rechnungen",
body: "PDFs auslesen, in DATEV/SAP/Sheets ablegen — fehlerfrei und sofort. Keine manuelle Tipparbeit mehr.",
href: "leistungen.html#dokumente",
},
{
icon: "chart",
title: "Reporting & Auswertung",
body: "Reports automatisch aus euren Quellsystemen — mit KI-Kommentar zu Auffälligkeiten und Trends.",
href: "leistungen.html#reporting",
},
{
icon: "users",
title: "HR & Onboarding",
body: "Vom unterschriebenen Vertrag bis zum Slack-Account: alle Anlage-Schritte automatisch, ohne einen Klick.",
href: "leistungen.html#hr",
},
{
icon: "handshake",
title: "Lead-Qualifizierung",
body: "Eingehende Leads scoren, qualifizieren, ins CRM schreiben, weiterleiten. Kein Interessent geht mehr verloren.",
href: "leistungen.html#vertrieb",
},
{
icon: "server",
title: "Custom-Workflows",
body: "Eure Spezial-Anforderung passt nirgendwo rein? Wir bauen individuelle Workflows mit n8n und LLMs nach Maß.",
href: "leistungen.html#custom",
},
];
return (
Was wir tun
Sechs Workflow-Bausteine, die wir in Unternehmen produktiv eingerichtet haben.
Wir bauen euch genau das Stück, das euch am meisten Zeit zurückgibt — kein Bauchladen.
);
}
// -----------------------------
// Welcome section (Zenith-Pattern: asymmetric Intro)
// Links: Pill + H2 + Body + Button. Rechts: Bild-Platzhalter mit Dot-Pattern.
// -----------------------------
function WelcomeSection() {
return (
Über uns
Zwei Leute aus Hamburg, die Workflows tatsächlich bauen — nicht nur erklären.
Wir sind kein Beratungs-Theater. Wir kombinieren echtes Engineering-Handwerk mit KI-Erfahrung und liefern Workflows, die ab Tag eins laufen — DSGVO-konform, dokumentiert, übergebbar.
Mehr über uns
);
}
// -----------------------------
// Problem section
// -----------------------------
function ProblemSection() {
const items = [
{ icon: "mail", t: "E-Mails sortieren und beantworten", b: "Das info@-Postfach ist ein schwarzes Loch. Anfragen gehen unter, Antworten kommen zu spät." },
{ icon: "file", t: "Rechnungen & Dokumente ablegen", b: "PDFs manuell abtippen, Beträge ins System eintragen — teuer, langsam, fehleranfällig." },
{ icon: "clock", t: "Termine koordinieren", b: "Hin-und-Her per Mail, bis ein Slot passt. Kalender-Tetris ohne Mehrwert." },
{ icon: "chart", t: "Reports von Hand schreiben", b: "Jeden Monat aus Excel, CSV und drei Systemen zusammenkopiert. Wieder. Und wieder." },
];
return (
Das Problem
Ihr erledigt täglich Aufgaben, die eine KI übernehmen könnte.
Jede Stunde davon kostet euch bares Geld. Wir stoppen das.
{items.map((it, i) => (
))}
);
}
// -----------------------------
// Use cases preview (5 cards on home, link to overview)
// -----------------------------
function UseCasesPreview({ onOpen, openCase }) {
// Zenith-Pattern: Service-Card mit Bild-Platzhalter oben + Text + Tools + Metric-Pills + CTA
// Wir zeigen 4 von 5 hier (geradzahlig fuer 2-Spalten-Grid).
const featured = homeUseCases.slice(0, 4);
// Icon-Map fuer Visuelle Repraesentation in der Card-Image-Area
const iconForCase = { "01": "📨", "02": "📄", "03": "📊", "04": "👥", "05": "🤝" };
return (
Use Cases
Was wir automatisieren, und was das für euch bedeutet.
Alle 5 Use Cases
{featured.map((c) => (
onOpen(c)}
aria-haspopup="dialog"
aria-expanded={openCase?.num === c.num}
>
{iconForCase[c.num] || "✨"}
{c.num} · USE CASE
{c.title}
{c.body}
{c.tools.map((t) => )}
{c.hours} Std/Woche gespart
Workflow ansehen
))}
);
}
// -----------------------------
// Process (4 steps)
// -----------------------------
function ProcessSection() {
const steps = [
{ n: "01", t: "Analyse", body: "Wir schauen uns eure Abläufe an und finden die größten Zeitfresser.", meta: "Kostenlos · 30 Min" },
{ n: "02", t: "Konzept", body: "Wir zeigen euch genau, was wir bauen und was es bringt.", meta: "Festpreis-Angebot" },
{ n: "03", t: "Umsetzung", body: "In 1–2 Wochen läuft euer Workflow. Wir testen mit euren echten Daten.", meta: "5–10 Werktage" },
{ n: "04", t: "Übergabe", body: "Ihr bekommt alles erklärt und dokumentiert. Wir bleiben ansprechbar.", meta: "Inkl. 30 Tage Support" },
];
return (
So läuft das ab
In 4 Schritten zu eurem automatisierten Prozess.
{steps.map((s) => (
{s.n}
{s.t}
{s.body}
{s.meta}
))}
);
}
// -----------------------------
// Stats (4 Datenpunkte)
// -----------------------------
function StatsSection() {
// Zenith-Pattern: 4 helle Ghost-Cards mit grossen Gradient-Numbers
// Content laut PDF: Realisierte Projekte, Zeitersparnis, Bauzeit, Sicherheit
const stats = [
{ num: "20+", label: "Workflows live", desc: "Bei kleinen und mittleren Unternehmen — vom Handwerksbetrieb bis zur Kanzlei." },
{ num: "7 Std", label: "Pro Woche gespart", desc: "Gemessene Zeitersparnis pro Kunde, die wir durch einen einzigen Workflow zurückgeben." },
{ num: "1–2", label: "Wochen Bauzeit", desc: "Von der ersten Skizze bis zum produktiv laufenden Workflow in eurer Infrastruktur." },
{ num: "100%", label: "DSGVO-konform", desc: "EU-Hosting, eure Daten bleiben bei euch. Keine US-Cloud per Default." },
];
return (
Konkrete Stunden. Echte Kunden. Keine Werbeversprechen.
{stats.map((s, i) => (
))}
);
}
// -----------------------------
// USP / Why Treibiq - 4 Karten, click-to-modal
// -----------------------------
function WhySection({ onOpen, openReason }) {
return (
Vier Gründe
Warum Treibiq?
{whyReasons.map((r) => (
onOpen(r)}
aria-haspopup="dialog"
aria-expanded={openReason?.num === r.num}
>
{r.num}
{r.title}
{r.teaser}
Details ansehen
))}
);
}
// Modal fuer Why-Reasons
function WhyModal({ reason, onClose }) {
if (!reason) return null;
// Exit-Animation: isClosing setzt .is-closing CSS-Klasse, dann unmount
// ueber onClose nach 180ms (= modal-out keyframe-Duration).
const [isClosing, setIsClosing] = useState(false);
const handleClose = () => {
if (isClosing) return;
setIsClosing(true);
setTimeout(onClose, 180);
};
useEffect(() => {
const onKey = (e) => { if (e.key === "Escape") handleClose(); };
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [handleClose]);
return (
e.stopPropagation()}>
Warum Treibiq · {reason.num}
{reason.title}
{reason.detail}
{reason.tags.map((t, i) => (
{t}
))}
);
}
// -----------------------------
// Workflow-Canvas-Demo: interaktiver Live-Flow im n8n-Stil.
// Statt 6 statischen Cards ein konkretes Beispiel (E-Mail-Triage) das der
// Besucher anfassen und ziehen kann. Story: "Genau so bauen wir das bei euch."
// Inspiriert von shadcn/framer-motion Vorlage, neu gebaut ohne Tailwind/
// framer-motion, mit Treibiq-Branding (violet+peach Gradient).
// -----------------------------
function WorkflowCanvasDemo() {
// Beispiel-Workflow "E-Mail-Triage" - 4 Nodes als End-to-End-Flow.
// Zwei Layout-Varianten: DESKTOP horizontal mit Branches, MOBILE vertikal
// mit Chain. So bleiben die Nodes auf dem Handy voll lesbar (kein
// Down-Scaling), und Desktop-User sehen den Branch-Style.
const NODE_W_D = 200, NODE_H_D = 96;
const NODE_W_M = 248, NODE_H_M = 84;
const CANVAS_W_D = 980, CANVAS_H_D = 440;
const CANVAS_W_M = 300, CANVAS_H_M = 860;
// Mobile-Mode aus Viewport-Width, mit Resize-Listener
const detectMobile = () => typeof window !== "undefined" && window.innerWidth <= 720;
const [isMobile, setIsMobile] = useState(detectMobile);
// Initial-Nodes mit beiden Layouts. id, type, title etc bleiben gleich -
// nur x/y sind layout-spezifisch. Lookup-Object damit der Switch sauber ist.
const initialNodeData = [
{ id: "n1", type: "Trigger", title: "Mail-Eingang", desc: "Gmail / Outlook Webhook", icon: "mail", color: "violet" },
{ id: "n2", type: "Aktion", title: "KI-Klassifikation", desc: "Mistral analysiert Inhalt", icon: "sparkle", color: "peach" },
{ id: "n3", type: "Logik", title: "Routing", desc: "An passendes Team weiterleiten", icon: "users", color: "violet" },
{ id: "n4", type: "Aktion", title: "Antwort-Entwurf", desc: "Draft zurück ins Postfach", icon: "file", color: "peach" },
];
const desktopPositions = { n1:{x:30,y:170}, n2:{x:270,y:110}, n3:{x:510,y:220}, n4:{x:750,y:170} };
const mobilePositions = { n1:{x:26,y:24}, n2:{x:26,y:128}, n3:{x:26,y:232}, n4:{x:26,y:336} };
const buildInitialNodes = (mobile) => initialNodeData.map((n) => ({
...n,
...(mobile ? mobilePositions[n.id] : desktopPositions[n.id]),
}));
const initialConns = [
{ from: "n1", to: "n2" },
{ from: "n2", to: "n3" },
{ from: "n3", to: "n4" },
];
// 3 Add-Slots, beide Layouts. Desktop verzweigt (fromDesktop), Mobile
// chained vertikal (immer am letzten Node).
const addSlots = [
{
fromDesktop: "n4",
desktop: { x: 750, y: 320 },
mobile: { x: 26, y: 440 },
tpl: { type: "Aktion", title: "Slack-Notify", desc: "Team-Channel benachrichtigen", icon: "bell", color: "violet" },
},
{
fromDesktop: "n3",
desktop: { x: 510, y: 20 },
mobile: { x: 26, y: 544 },
tpl: { type: "Aktion", title: "Auswertung", desc: "Reporting in Sheets schreiben", icon: "chart", color: "peach" },
},
{
fromDesktop: "n2",
desktop: { x: 270, y: 320 },
mobile: { x: 26, y: 648 },
tpl: { type: "Logik", title: "Wenn-Dann-Logik", desc: "Bedingung pruefen + verzweigen", icon: "zap", color: "violet" },
},
];
const [nodes, setNodes] = useState(() => buildInitialNodes(detectMobile()));
const [conns, setConns] = useState(initialConns);
const [draggingId, setDraggingId] = useState(null);
const [addIdx, setAddIdx] = useState(0);
const [lastAddedId, setLastAddedId] = useState(null);
const dragOffset = useRef({ x: 0, y: 0 });
const canvasRef = useRef(null);
const innerRef = useRef(null);
// Resize-Listener: bei Mode-Wechsel Nodes/Conns auf initial-Layout
// der neuen Variante resetten, damit nichts schief sitzt.
useEffect(() => {
const onResize = () => {
const nowMobile = detectMobile();
setIsMobile((wasMobile) => {
if (wasMobile === nowMobile) return wasMobile;
setNodes(buildInitialNodes(nowMobile));
setConns(initialConns);
setAddIdx(0);
setLastAddedId(null);
return nowMobile;
});
};
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
const NODE_W = isMobile ? NODE_W_M : NODE_W_D;
const NODE_H = isMobile ? NODE_H_M : NODE_H_D;
const CANVAS_W = isMobile ? CANVAS_W_M : CANVAS_W_D;
const CANVAS_H = isMobile ? CANVAS_H_M : CANVAS_H_D;
// Helper: aktuelle CSS-Scale des inner-Containers (kann auf Mobile via
// CSS media-query auf z.B. 0.36 gesetzt sein). Wir korrigieren die
// Pointer-Math entsprechend, sodass Drag bei jeder Skalierung passt.
const getInnerScale = () => {
if (!innerRef.current) return 1;
const r = innerRef.current.getBoundingClientRect();
return r.width / CANVAS_W || 1;
};
// Pointer-Handlers: vanilla statt framer-motion. Funktioniert auf
// Maus + Touch + Pen automatisch via PointerEvents.
const handlePointerDown = (nodeId, e) => {
e.preventDefault();
const node = nodes.find((n) => n.id === nodeId);
const inner = innerRef.current;
if (!node || !inner) return;
const rect = inner.getBoundingClientRect();
const scale = getInnerScale();
dragOffset.current = {
x: (e.clientX - rect.left) / scale - node.x,
y: (e.clientY - rect.top) / scale - node.y,
};
setDraggingId(nodeId);
};
useEffect(() => {
if (!draggingId) return;
const onMove = (e) => {
const inner = innerRef.current;
if (!inner) return;
const rect = inner.getBoundingClientRect();
const scale = getInnerScale();
const newX = Math.max(0, Math.min(CANVAS_W - NODE_W,
(e.clientX - rect.left) / scale - dragOffset.current.x));
const newY = Math.max(0, Math.min(CANVAS_H - NODE_H,
(e.clientY - rect.top) / scale - dragOffset.current.y));
setNodes((prev) =>
prev.map((n) => (n.id === draggingId ? { ...n, x: newX, y: newY } : n))
);
};
const onUp = () => setDraggingId(null);
window.addEventListener("pointermove", onMove);
window.addEventListener("pointerup", onUp);
window.addEventListener("pointercancel", onUp);
return () => {
window.removeEventListener("pointermove", onMove);
window.removeEventListener("pointerup", onUp);
window.removeEventListener("pointercancel", onUp);
};
}, [draggingId]);
// Bezier-Pfad zwischen Nodes. Desktop = horizontal (right-edge -> left-edge),
// Mobile = vertikal (bottom-edge -> top-edge). Macht den Flow auf dem
// Handy "von oben nach unten" lesbar.
const buildPath = (from, to) => {
const a = nodes.find((n) => n.id === from);
const b = nodes.find((n) => n.id === to);
if (!a || !b) return "";
if (isMobile) {
const sx = a.x + NODE_W / 2;
const sy = a.y + NODE_H;
const ex = b.x + NODE_W / 2;
const ey = b.y;
const cp1y = sy + (ey - sy) * 0.5;
const cp2y = ey - (ey - sy) * 0.5;
return `M${sx},${sy} C${sx},${cp1y} ${ex},${cp2y} ${ex},${ey}`;
}
const sx = a.x + NODE_W;
const sy = a.y + NODE_H / 2;
const ex = b.x;
const ey = b.y + NODE_H / 2;
const cp1x = sx + (ex - sx) * 0.5;
const cp2x = ex - (ex - sx) * 0.5;
return `M${sx},${sy} C${cp1x},${sy} ${cp2x},${ey} ${ex},${ey}`;
};
const handleAddNode = () => {
if (addIdx >= addSlots.length) return; // max 3 adds
const slot = addSlots[addIdx];
const pos = isMobile ? slot.mobile : slot.desktop;
const newId = `n-${Date.now()}`;
const newNode = {
id: newId,
...slot.tpl,
x: pos.x,
y: pos.y,
};
// Mobile: chain ans letzte (added oder initial n4). Desktop: branch via slot.fromDesktop.
const fromId = isMobile ? (lastAddedId || "n4") : slot.fromDesktop;
setNodes((prev) => [...prev, newNode]);
setConns((prev) => [...prev, { from: fromId, to: newId }]);
setLastAddedId(newId);
setAddIdx((i) => i + 1);
};
const canAddMore = addIdx < addSlots.length;
return (
Live-Workflow zum Anfassen
Genau so sehen die Workflows aus, die wir bei euch bauen.
Drag and Drop, durchklicken, verstehen. Bei euch live in 1–2 Wochen — mit euren Tools, in eurer Cloud.
{/* Header: Status-Badge + Workflow-Label + Add-Button */}
Live
E-Mail-Triage · Beispiel-Flow
{canAddMore ? "Knoten hinzufügen" : "Workflow voll"}
{/* Canvas mit SVG-Linien + absolut positionierten Nodes.
Fixed-size content (CANVAS_W x CANVAS_H), kein horizontaler
Scroll noetig - Add platziert Nodes in vordefinierte
Branch-Slots oberhalb/unterhalb der Initial-Reihe. */}
{conns.map((c) => (
))}
{nodes.map((n) => (
handlePointerDown(n.id, e)}
role="article"
aria-label={`${n.type}: ${n.title}`}
>
{n.type}
{n.title}
{n.desc}
))}
{/* Footer: Stats + Hint */}
{nodes.length} {nodes.length === 1 ? "Knoten" : "Knoten"}
{conns.length} {conns.length === 1 ? "Verbindung" : "Verbindungen"}
Knoten anfassen und ziehen
{/* CTA zu Leistungen (statt 6 Cards inline) */}
);
}
// -----------------------------
// "Was wir umsetzen" - 6 Workflows mit Click-to-Modal
// (auf Home aktuell ersetzt durch WorkflowCanvasDemo, bleibt erhalten fuer
// andere Pages oder Re-Aktivierung)
// -----------------------------
function WorkflowsSection({ onOpen, openWorkflow }) {
return (
Eure Zeitfresser — von uns automatisiert
Lösungen nach Maß.
Wir verkaufen keine Software von der Stange. Wir bauen die digitalen Brücken, die eure manuellen Routineaufgaben übernehmen. Pragmatisch, schnell und sicher.
{workflowsList.map((w, i) => (
onOpen(w)}
aria-haspopup="dialog"
aria-expanded={openWorkflow?.title === w.title}
>
{w.title}
{w.sub}
{w.teaser}
Wie es funktioniert
))}
Euer spezieller Prozess ist nicht dabei? Wir bauen auch Custom-Workflows.
Projekt anfragen
);
}
// Modal fuer Workflows
function WorkflowModal({ workflow, onClose }) {
if (!workflow) return null;
const [isClosing, setIsClosing] = useState(false);
const handleClose = () => {
if (isClosing) return;
setIsClosing(true);
setTimeout(onClose, 180);
};
useEffect(() => {
const onKey = (e) => { if (e.key === "Escape") handleClose(); };
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [handleClose]);
return (
e.stopPropagation()}>
{workflow.sub}
{workflow.title}
Der Use Case
{workflow.problem}
Bauzeit {workflow.bauzeit}
Ersparnis {workflow.ersparnis}
);
}
// -----------------------------
// Testimonials section (Zenith-Pattern: Quote-Cards mit Author + Role)
// Platzhalter-Testimonials bis echte Pilot-Kunden da sind.
// -----------------------------
function TestimonialsSection() {
const items = [
{
quote: "Innerhalb von zwei Wochen lief der Mail-Triage-Workflow. Seitdem sind unsere Antwortzeiten von 2 Tagen auf 4 Stunden runter, ohne dass jemand mehr arbeitet.",
name: "Pilot-Kunde 1",
role: "Geschäftsführer, Handwerksbetrieb",
},
{
quote: "Endlich jemand der nicht 80 Folien Powerpoint aufmacht, sondern den Workflow direkt baut. Festpreis, 10 Tage, fertig. Das war einfach angenehm.",
name: "Pilot-Kunde 2",
role: "Operations, B2B-SaaS",
},
{
quote: "Die Kombination aus echtem Coding-Skill und Verständnis für unsere Abläufe macht den Unterschied zu allen anderen Anbietern, die wir uns angeschaut haben.",
name: "Pilot-Kunde 3",
role: "Inhaber, Steuerkanzlei",
},
{
quote: "Wir haben drei Tage Aufwand pro Woche gespart, ohne dass wir IT-Know-how aufbauen mussten. Genau das, was wir gesucht haben.",
name: "Pilot-Kunde 4",
role: "Inhaberin, Marketing-Agentur",
},
{
quote: "Die Übergabe war unfassbar sauber: Doku, Loom-Walkthrough, klare Verantwortlichkeiten. So professionell hat uns vorher noch niemand etwas übergeben.",
name: "Pilot-Kunde 5",
role: "CTO, Maschinenbau",
},
{
quote: "Hat sich nach drei Monaten amortisiert. Was wir vorher in 5 Stunden mailten, läuft jetzt im Hintergrund — wir merken's nicht mal mehr.",
name: "Pilot-Kunde 6",
role: "Geschäftsführer, Logistik-Dienstleister",
},
];
// Duplikat fuer seamless Marquee-Loop: Track haelt 2 Sets der Items.
// Translation -6 * (card-width + gap) ueber 42s bringt das zweite Set
// exakt in die Startposition des ersten - kein sichtbarer Sprung.
return (
Stimmen
Was unsere Kund:innen sagen.
Echte Workflows, echte Stunden gespart. Hier ein paar Worte aus laufenden Pilotprojekten.
{[...items, ...items].map((t, i) => (
= items.length ? "true" : "false"}>
"
{t.quote}
))}
);
}
// -----------------------------
// FAQ
// -----------------------------
function FaqSection({ faqs = homeFaqs }) {
const [open, setOpen] = useState(0);
return (
FAQ
Was Geschäftsführer:innen uns oft fragen.
Steht eure Frage nicht dabei? Schreibt uns.
Direkt fragen
{faqs.map((f, i) => {
const isOpen = open === i;
return (
setOpen(isOpen ? -1 : i)} aria-expanded={isOpen}>
{f.q}
);
})}
);
}
// -----------------------------
// Use case modal (used by home + use cases page)
// -----------------------------
function UseCaseModal({ wf, onClose }) {
if (!wf) return null;
const [isClosing, setIsClosing] = useState(false);
const handleClose = () => {
if (isClosing) return;
setIsClosing(true);
setTimeout(onClose, 180);
};
useEffect(() => {
const onKey = (e) => { if (e.key === "Escape") handleClose(); };
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [handleClose]);
return (
e.stopPropagation()}>
Use Case · {wf.num}
{wf.title}
{wf.body}
{wf.tools.map((t, i) => (
{i < wf.tools.length - 1 && }
))}
Zeitersparnis {wf.hours} Std / Woche
Aufbauzeit 1–2 Wochen
Status ● Live bei Kunden
);
}
// -----------------------------
// Partner-Logos-Carousel
// Echte Partner-Logos aus /assets/partners/. Einheitliche Hoehe via
// CSS, Greyscale-Filter fuer visuelle Beruhigung. Marquee infinite loop.
// -----------------------------
function PartnerLogos() {
const partners = [
{ slug: "basilika", name: "Basilika Mannheim" },
{ slug: "bellavie", name: "BellaVie" },
{ slug: "triebskorn", name: "Pflegedienst Triebskorn" },
{ slug: "pilatte", name: "Pilatte" },
];
// Duplizieren fuer nahtlosen Marquee-Loop.
const track = [...partners, ...partners, ...partners];
return (
Unsere Partner
{track.map((p, i) => (
))}
);
}
// -----------------------------
// App
// -----------------------------
function HomeApp() {
const [openWhy, setOpenWhy] = useState(null);
const [openWorkflow, setOpenWorkflow] = useState(null);
return (
{/* Partner-Logos direkt unter Hero, kleine Heading, danach Karussell. */}
setOpenWhy(r)} openReason={openWhy} />
{/* WorkflowCanvasDemo (n8n-style interactive flow) entfernt 2026-05-14 -
live demo gibt's auf /leistungen.html via Quiz. Komponente bleibt
im Code (shared.jsx hat keine Abhaengigkeit, hier weiter unten in
home.jsx definiert) falls Re-Aktivierung gewuenscht. */}
{openWhy && setOpenWhy(null)} />}
{openWorkflow && setOpenWorkflow(null)} />}
);
}
Object.assign(window, {
homeUseCases, homeFaqs, whyReasons, workflowsList,
Hero, HeroVisual, TrustStrip, WelcomeSection, ServicesSection, ProblemSection, UseCasesPreview,
ProcessSection, StatsSection, WhySection, WhyModal, WorkflowsSection, WorkflowModal,
TestimonialsSection, FaqSection, UseCaseModal,
});
ReactDOM.createRoot(document.getElementById("app")).render( );