// Aktivstall Mannheims — One-Pager (Langenfeld) // React component tree, mounted into #app const { useState, useEffect, useRef } = React; // ============================================================ // COLOR PRESETS for Tweaks // ============================================================ const COLOR_PRESETS = { espresso: { label: "Espresso", paper: "#F4F2EE", paper2: "#EAE6DD", paper3: "#D9D2C2", ink: "#1F1B17", accent: "#3A2418", accentOn: "#F2ECE0", sand: "#C9B892", cognac: "#8A5A36" }, forest: { label: "Wald", paper: "#F2EFE7", paper2: "#E5E0D2", paper3: "#D2CCB9", ink: "#1A1F18", accent: "#1F2E20", accentOn: "#F0EDE2", sand: "#BDB28A", cognac: "#6E7A4B" }, earth: { label: "Erde", paper: "#F5EFE3", paper2: "#EBE0CB", paper3: "#D9C9A9", ink: "#241A12", accent: "#5A3520", accentOn: "#F4ECDB", sand: "#D4B987", cognac: "#A6603A" } }; // ============================================================ // SHARED SMALL PARTS // ============================================================ function Eyebrow({ children, dark }) { return ( {children} ); } function PhotoPlaceholder({ aspect = "4/3", caption, dark = false, hue = 0, src, imgPosition = "center", children, style }) { // Warm-toned gradient placeholder for documentary photography. // If `src` is given, the image is rendered with object-fit: cover // and the gradient remains as a fallback while the image loads. const grads = [ "linear-gradient(135deg, var(--cognac) 0%, var(--espresso) 100%)", "linear-gradient(150deg, #6b4d2a 0%, #2a1810 100%)", "linear-gradient(120deg, #8a6a3f 0%, #3a2418 100%)", "linear-gradient(160deg, #a07a4a 0%, #4a2a18 100%)", "linear-gradient(140deg, #5a4030 0%, #1f1410 100%)", "linear-gradient(125deg, #7a5530 0%, #2a1a10 100%)"]; return (
{src && {caption } {children} {caption &&
{caption}
}
); } // ============================================================ // IMAGES — central config of placeholder Unsplash photos. // Easy swap-out later: replace these URLs with real Aktivstall photos. // All photos are from Unsplash and used under their free license. // ============================================================ const U = (id, w = 1600) => `https://images.unsplash.com/photo-${id}?auto=format&fit=crop&w=${w}&q=80`; // Confirmed working horse photos (visually verified). We rotate these // across all horse-content slots — placeholders for the user's own photos. const IMAGES = { // Real Aktivstall Mannheims photos (provided by customer) drone1: "assets/photos/drone-1.jpeg", drone2: "assets/photos/drone-2.jpeg", eingang: "assets/photos/eingang.jpeg", familyStrand: "assets/photos/family-strand.jpeg", grosselternKinder: "assets/photos/grosseltern-kinder.jpeg", fuehranlage: "assets/photos/fuehranlage.jpeg", ponybandeImg: "assets/photos/ponybande.jpeg", logo: "assets/photos/logo.jpeg", raphaela: "assets/photos/raphaela.jpeg", reithalleImg: "assets/photos/reithalle.jpeg", reitplatz: "assets/photos/reitplatz.jpeg", springen: "assets/photos/springen.jpeg", stallung: "assets/photos/stallung.jpeg", vielseitigkeit: "assets/photos/vielseitigkeit.jpeg", weide1: "assets/photos/weide-1.jpeg", weide2: "assets/photos/weide-2.jpeg", // Aliases — match service slots to the right real photos. get hero() {return this.drone2;}, get reithalle() {return this.reithalleImg;}, get paddock() {return this.weide1;}, get sport() {return this.reitplatz;}, // Sport- & Freizeitstall → Reitplatz get offenstall() {return this.stallung;}, // Offenstall → Stallung get gelaende() {return this.vielseitigkeit;}, // Geländestrecke → Vielseitigkeit get unterricht() {return this.springen;}, // Reitunterricht → Springen get beritt() {return this.logo;}, // Beritt → Logo get ponybande() {return this.ponybandeImg;}, // Ponybande → Ponybande-Logo get lehrgang() {return this.reithalleImg;}, // Lehrgänge → Reithalle // Familie collage — real family photos get fam1() {return this.familyStrand;}, get fam2() {return this.grosselternKinder;}, get fam3() {return this.eingang;}, // Galerie tiles — alle echten Fotos get g1() {return this.drone1;}, get g2() {return this.drone2;}, get g3() {return this.eingang;}, get g4() {return this.reithalleImg;}, get g5() {return this.reitplatz;}, get g6() {return this.stallung;}, get g7() {return this.vielseitigkeit;}, get g8() {return this.springen;}, get g9() {return this.weide1;}, get g10() {return this.weide2;}, get g11() {return this.fuehranlage;}, get g12() {return this.raphaela;}, // Team portraits (verified people photos) t1: U("1494790108377-be9c29b29330", 600), t2: U("1438761681033-6461ffad8d80", 600), t3: U("1535713875002-d1d0cf377fde", 600), t4: U("1517841905240-472988babdf9", 600) }; function Btn({ children, variant = "primary", onClick, style }) { const base = { fontFamily: "var(--font-sans)", fontSize: 14, fontWeight: 500, letterSpacing: 0, padding: "14px 28px", borderRadius: 999, cursor: "pointer", transition: "all 200ms cubic-bezier(0.22,1,0.36,1)", display: "inline-flex", alignItems: "center", gap: 10, ...style }; if (variant === "primary") { return ( ); } return ( ); } // ============================================================ // NAV // ============================================================ function Nav() { const [scrolled, setScrolled] = useState(false); const [menuOpen, setMenuOpen] = useState(false); const [isNarrow, setIsNarrow] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 40); const onResize = () => setIsNarrow(window.innerWidth < 1100); window.addEventListener("scroll", onScroll); window.addEventListener("resize", onResize); onScroll(); onResize(); return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onResize); }; }, []); const links = [ ["Konzept", "#konzept"], ["Anlage", "#anlage"], ["Familie", "#familie"], ["Angebot", "#angebot"], ["Galerie", "#galerie"], ["Stimmen", "#stimmen"]]; const linkStyle = { fontSize: 11, letterSpacing: "0.18em", textTransform: "uppercase", color: "var(--fg-2)", border: 0, fontFamily: "var(--font-sans)", fontWeight: 500 }; return ( ); } // ============================================================ // HERO // ============================================================ function Hero() { // Scroll-driven hero with a full-bleed photo background. // 0.0 → 0.50 : foreground text + chrome fade out, photo zooms slightly // 0.45 → 1.0 : "Pferde leben hier in Bewegung." caption swells in, // photo darkens for legibility, then holds. const [p, setP] = React.useState(0); React.useEffect(() => { let raf = 0; function update() { raf = 0; const el = document.getElementById("hero-scroll-track"); if (!el) return; const r = el.getBoundingClientRect(); const trackHeight = el.offsetHeight - window.innerHeight; const raw = trackHeight > 0 ? -r.top / trackHeight : 0; setP(Math.max(0, Math.min(1, raw))); } function onScroll() { if (!raf) raf = requestAnimationFrame(update); } update(); window.addEventListener("scroll", onScroll, { passive: true }); window.addEventListener("resize", onScroll); return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); if (raf) cancelAnimationFrame(raf); }; }, []); const easeOut = (x) => 1 - Math.pow(1 - x, 3); // ── Phase A: 0..0.5 — intro text fades, photo zooms ── const introOut = Math.min(1, p / 0.5); const textOpacity = 1 - easeOut(introOut); // ── Photo zoom: continuous through whole scroll ── const zoom = 1 + 0.18 * easeOut(p); // 1.0 → 1.18 // ── Photo darkening: ramps in second half for caption legibility ── const darken = Math.max(0, Math.min(1, (p - 0.35) / 0.4)); // ── Phase B: 0.45..0.85 — big caption swells in ── const captionIn = easeOut(Math.max(0, Math.min(1, (p - 0.45) / 0.4))); const captionScale = 0.92 + 0.08 * captionIn; // ── Phase C: 0.85..1.0 — sub-caption (stats) appears ── const subIn = Math.max(0, Math.min(1, (p - 0.78) / 0.18)); return (
{/* ── Layer 0: full-bleed photo background ── */}
{/* Darkening veil — grows with scroll */}
{/* Caption removed to avoid overlap with the nav brand */}
{/* ── Layer 1: intro foreground (centered text + bottom row) ── */}
0.05 ? "auto" : "none" }}> {/* top: eyebrow + headline + sub */}
Pferde leben hier in Bewegung · Langenfeld · Seit 2023

Aktivstall
Mannheims.

{/* bottom: paragraph + CTAs in two columns */}

Auf 9 Hektar Wiesen in Langenfeld halten wir 70 Pferde in einer artgerechten Gruppenhaltung — mit ständigem Zugang zu Heu, Bewegung und Sozialkontakt. Kein Kompromiss zwischen Komfort und Pferd.

70 Pferde
9 Hektar
3 Jahre Erfahrung
{/* ── Layer 2: scroll-revealed centered statement ── */}
— Aktivstall Mannheims —
Pferde leben hier{" "} in Bewegung.
70 Pferde · 9 Hektar · Eine Herde
{/* ── Scroll progress hint removed — was overlapping with nav menu ── */}
); } // ============================================================ // SECTION: KONZEPT — numbered Montfort-style list, on dark band // ============================================================ function Konzept() { const items = [ { n: "1", title: "Bewegung rund um die Uhr", body: "Unser Offenstall lässt die Pferde frei zwischen Liegehallen, Heuraufen und Auslauf wandern. Bewegung passiert von selbst — ohne Box, ohne Stillstand." }, { n: "2", title: "Heu, wann immer es nötig ist", body: "Mehrere Heuraufen mit Slow-Feeder-Netzen sind 24 Stunden zugänglich. Ein durchgehender Magen-Darm-Trakt entspricht der Natur des Pferdes." }, { n: "3", title: "Echte Herde, echter Sozialkontakt", body: "Pferde sind Herdentiere. Bei uns leben sie in stabilen Gruppen mit klaren Strukturen — kein Sichtkontakt durch Boxenstäbe, sondern echtes Miteinander." }, { n: "4", title: "Witterungsschutz auf hohem Niveau", body: "Großzügige, eingestreute Liegehallen und Unterstände erlauben jederzeit Rückzug. Der Boden ist trocken, planbefestigt und wird täglich gepflegt." }]; return (
01 · Konzept

Wir halten Pferde so, wie sie eigentlich {" "} leben wollen.

{items.map((it, i) =>
{it.n}

{it.title}

{it.body}

)}
); } // ============================================================ // SECTION: ANLAGE — split image + facility list // ============================================================ function Anlage() { // NOTE: Einige Zahlen/Ausstattung sind Platzhalter — vom Kunden noch zu bestätigen. const features = [ "9 Hektar Wiesen", "20 × 40 m Reithalle", "20 × 60 m Reitplatz", "Longierhalle, ⌀ 18 m", "Führanlage für 4 Pferde", "Geländestrecke 800 m mit 10 Sprüngen", "Solarium", "Waschbox", "Reiterstübchen mit Küche & Kamin", "Hängerstellplatz", "Allergiker-Herde", "Heuraufen", "Liegeflächen", "Vollpension"]; return (
02 · Anlage

Neun Hektar Wiesen — durchdacht {" "} aufgeteilt.

Trail, Trainingsflächen, Weiden und Sozialräume sind so angeordnet, dass Pferd und Reiter auf kurzen Wegen alles finden — ohne dass die Herde gestört wird.

    {features.map((f, i) =>
  • = features.length - 2 ? { borderBottom: "1px solid var(--border-1)" } : {}) }}> {String(i + 1).padStart(2, "0")} {f}
  • )}
); } // ============================================================ // SECTION: STATS BAND — quiet numbers // ============================================================ function Stats() { const stats = [ ["8–16", "Pferde pro Gruppe"], ["9 ha", "Wiesen in Langenfeld"], ["24/7", "Heu & Bewegung"], ["800 m", "Geländestrecke, 10 Sprünge"]]; return (
{stats.map(([n, l], i) =>
{n}
{l}
)}
); } // ============================================================ // SECTION: FAMILIE — family-run, personal pull-quote + photo collage // ============================================================ function Familie() { return (
{/* ── LEFT: Pull quote + family signature ── */}
03 · Familiengeführt

Ein Stall, geführt{" "} wie ein Zuhause.

„Wir sind selbst Pferdemenschen — jeden Tag, mit jedem Wetter. Unser Stall ist kein Job, sondern unser Leben. Genau deshalb behandeln wir jedes Pferd, das hier einzieht, wie unser eigenes."

— Familie Mannheims
Inhaber

Familiengeführt heißt für uns: kurze Wege, klare Ansprache, verlässliche Hände. Keine anonyme Verwaltung, keine wechselnden Schichten — sondern Menschen, die du kennst und denen du dein Pferd anvertraust.

{/* ── RIGHT: Single hero family photo ── */}
); } // ============================================================ // SECTION: ANGEBOT — what we offer (8 service cards on dark band) // ============================================================ function Angebot() { const services = [ { n: "01", t: "Sport- & Freizeitstall", body: "Für ambitionierte Turnierreiter genauso wie für entspannte Freizeitreiter. Top ausgestattete Trainingsflächen, professionelle Betreuung, lebendige Stallgemeinschaft.", tag: "Hauptangebot", img: IMAGES.sport }, { n: "02", t: "Offenstall", body: "Artgerechte Herdenhaltung mit 9 Hektar Wiesen, Heuraufen rund um die Uhr, Liegeflächen und permanenter Bewegungsfreiheit.", tag: "Haltungsform", img: IMAGES.offenstall }, { n: "03", t: "Geländestrecke", body: "Eigene Geländestrecke von 800 Metern mit 10 Sprüngen direkt auf der Anlage — Kondition, Technik und Vertrauen unter freiem Himmel.", tag: "Anlage", img: IMAGES.gelaende }, { n: "04", t: "Reitunterricht", body: "Dressur, Springen, Freispringen und Cavalettiarbeit — strukturierter Reitunterricht für alle Niveaus, individuell angepasst, mit Plan.", tag: "Unterricht", img: IMAGES.unterricht }, { n: "05", t: "Beritt", body: "Professioneller Beritt für dein Pferd — Jungpferde-Ausbildung, Umschulung oder gezieltes Training. Einfühlsam, pferdegerecht, individuell.", tag: "Ausbildung", img: IMAGES.beritt }, { n: "06", t: "Ponybande Mannheims", body: "Die Ponybande — der perfekte Einstieg für Kinder. Spielerischer Umgang mit Ponys, echte Gemeinschaft und Pferdeliebe von Anfang an.", tag: "Kinder", img: IMAGES.ponybande }, { n: "07", t: "Lehrgänge", body: "Regelmäßige Lehrgänge für Reiter aller Levels — von der Grundausbildung bis zur Verfeinerung. Lerne von erfahrenen Trainern und entwickle dich und dein Pferd weiter.", tag: "Events", img: IMAGES.lehrgang }, { n: "08", t: "Reiterfitness", body: "Reiterspezifisches Fitnesstraining — für mehr Balance, Stabilität und Beweglichkeit im Sattel. Ideal als Ergänzung zum Reitunterricht.", tag: "Training", img: IMAGES.reitplatz }]; // Horizontal-scroll progress: as user scrolls vertically through the // tall track, the inner row translates left. const [p, setP] = React.useState(0); const [active, setActive] = React.useState(0); const [maxShiftPx, setMaxShiftPx] = React.useState(0); // Compute exact translateX needed at p=1 by measuring the last card's // distance from the viewport center. Re-measure on resize. const rowRef = React.useRef(null); React.useEffect(() => { function measure() { const row = rowRef.current; if (!row) return; const cards = row.querySelectorAll(".angebot-card"); if (!cards.length) return; const last = cards[cards.length - 1]; const lr = last.getBoundingClientRect(); const rr = row.getBoundingClientRect(); // Current translateX from style isn't applied during measure (we set 0 baseline). // To get the natural offset, account for current transform by reading actual. const currentTx = function () { const t = getComputedStyle(row).transform; if (!t || t === "none") return 0; const m = t.match(/matrix\(([^)]+)\)/); return m ? parseFloat(m[1].split(",")[4]) : 0; }(); const lastCenter = lr.left + lr.width / 2 - currentTx; // baseline (no transform) const viewportCenter = window.innerWidth / 2; const shift = Math.max(0, lastCenter - viewportCenter - currentTx); // Actually simpler: lastCenter (baseline) - viewportCenter = total shift needed const baselineLastCenter = lr.left + lr.width / 2 - currentTx; setMaxShiftPx(Math.max(0, baselineLastCenter - viewportCenter)); } measure(); window.addEventListener("resize", measure); // re-measure after fonts/images settle const t = setTimeout(measure, 600); return () => { window.removeEventListener("resize", measure); clearTimeout(t); }; }, [services.length]); React.useEffect(() => { let raf = 0; function update() { raf = 0; const el = document.getElementById("angebot-track"); if (!el) return; const r = el.getBoundingClientRect(); const trackHeight = el.offsetHeight - window.innerHeight; const raw = trackHeight > 0 ? -r.top / trackHeight : 0; const clamped = Math.max(0, Math.min(1, raw)); setP(clamped); // active card index based on progress const idx = Math.round(clamped * (services.length - 1)); setActive(idx); } function onScroll() { if (!raf) raf = requestAnimationFrame(update); } update(); window.addEventListener("scroll", onScroll, { passive: true }); window.addEventListener("resize", onScroll); return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); if (raf) cancelAnimationFrame(raf); }; }, [services.length]); // Gap between cards in the horizontal track. const gapPx = 40; // Track is tall enough to scroll through all cards comfortably. // ~35vh of scroll runway per card after the first — keeps the section snappy. const trackVh = 100 + (services.length - 1) * 35; return (
{/* ── Header (stays pinned) ── */}
04 · Angebot

Unser{" "} Angebot

{ e.currentTarget.style.background = "var(--accent-on)"; e.currentTarget.style.color = "var(--accent)"; }} onMouseLeave={(e) => { e.currentTarget.style.background = "transparent"; e.currentTarget.style.color = "var(--accent-on)"; }}> Anfragen →
{/* ── Horizontal card track ── */}
{services.map((s, i) => { const isActive = i === active; const distance = Math.abs(i - p * (services.length - 1)); const dim = Math.min(0.55, distance * 0.18); return (
{s.tag} {s.n}

{s.t}

{s.body}

{/* hairline + label at bottom */}
Aktivstall Mannheims
); })}
{/* ── Footer: progress + counter + dots ── */}
{String(active + 1).padStart(2, "0")} / {String(services.length).padStart(2, "0")}
{/* progress bar */}
{/* dots */}
{services.map((_, i) => )}
{/* corner hint */}
Scrollen
); } // ============================================================ // SECTION: TESTIMONIALS — Was unsere Einsteller sagen // ============================================================ function Testimonials() { const reviews = [ { body: "Wir stehen seit der Eröffnung in diesem Stall und sind rundum zufrieden. Die Pferde leben ein glückliches Leben, werden stets gut versorgt und stehen an erster Stelle. Die Stallgemeinschaft ist prima und es werden sowohl im Sommer draußen als auch im Winter drinnen im gemütlichen Stübchen mit Kamin viele gemeinsame Stunden verbracht.", who: "Britta Schöder", when: "Vor einem Jahr" }, { body: "Wir möchten uns ganz herzlich bei Frau Lena für den wunderbaren Reitunterricht bedanken. Unser 8-jähriger Sohn ist begeistert vom Reiten unter ihrer Anleitung — jede Stunde ist für ihn eine große Freude. Auch während der Sommerferienreitwoche hat er dort eine fantastische Zeit verbracht, voller Spaß, Lernen und Kontakt mit den Pferden.", who: "Asia Zachajka", when: "Vor 5 Monaten" }, { body: "9 Hektar Wiesen — hier hat mein Pferd endlich den Platz, den es verdient. Artgerechte Haltung, tolle Gemeinschaft, familiengeführt mit Herzblut.", who: "Deine Bewertung könnte hier stehen", when: "Google · 4,0 ★", placeholder: true }]; return (
06 · Stimmen

Was unsere Einsteller{" "} sagen

{reviews.map((r, i) =>
{"★★★★★".split("").map((s, k) => {s} )}

„{r.body}"

— {r.who} · {r.when}
)}
); } // ============================================================ // SECTION: REITUNTERRICHT // ============================================================ function Unterricht() { return (
03 · Reitunterricht

Reiten lernen heißt — das Pferd {" "} verstehen.

Klassischer Unterricht für Erwachsene und Jugendliche, mit geprüften Trainern, ehrlich verbundenen Schulpferden und einem Fokus auf Sitz, Hilfengebung und das Gespräch mit dem Tier.

{[ { tag: "Einzeln", t: "Privatstunde", p: "60 €", s: "45 Min · auf eigenen oder Schulpferd", body: "Detailarbeit am Sitz, an einer einzelnen Lektion oder einem konkreten Trainingsziel. Kein Gruppendruck.", hue: 3 }, { tag: "Gruppe", t: "Reitstunde", p: "35 €", s: "60 Min · max. 4 Reiter", body: "Strukturierte Reihen-Lektion an Sitz und Hilfengebung. Schulpferde sind enthalten.", hue: 1 }, { tag: "Bodenarbeit", t: "Kurs Pferd&Mensch", p: "ab 120 €", s: "Wochenend-Format", body: "Vom Führen über Longe und Doppellonge bis zur Freiarbeit. Auch ohne Reiterfahrung.", hue: 5 }]. map((c, i) => )}
); } // ============================================================ // SECTION: GALERIE — editorial mosaic // ============================================================ function Galerie() { return (
05 · Galerie

Ein Tag auf dem Hof.

12 Bilder · Aktivstall Mannheims
{[ { c: "span 3", r: "span 4", h: 0, src: IMAGES.g1 }, { c: "span 3", r: "span 3", h: 1, src: IMAGES.g2 }, { c: "span 2", r: "span 3", h: 2, src: IMAGES.g3 }, { c: "span 2", r: "span 3", h: 3, src: IMAGES.g4 }, { c: "span 2", r: "span 3", h: 4, src: IMAGES.g5 }, { c: "span 4", r: "span 3", h: 5, src: IMAGES.g6 }, { c: "span 3", r: "span 3", h: 2, src: IMAGES.g9 }, { c: "span 3", r: "span 3", h: 3, src: IMAGES.g10 }, { c: "span 3", r: "span 4", h: 4, src: IMAGES.g11 }]. map((g, i) =>
)}
); } // ============================================================ // SECTION: TEAM // ============================================================ function Team() { const people = [ { n: "Eva Lindner", r: "Stallleitung & Inhaberin", b: "Pferdewirtin Klassische Reitausbildung. Seit 2014 verantwortlich für Konzept und Herde.", hue: 0, src: IMAGES.t1 }, { n: "Tobias Reiff", r: "Trainer A · Reitlehrer", b: "Trainer A Dressur, FN-geprüft. Spezialisiert auf jungen Pferden und Wiedereinsteigern.", hue: 2, src: IMAGES.t2 }, { n: "Jana Schaub", r: "Bodenarbeit · Pferd&Mensch", b: "Akademische Reitkunst, Centered Riding Instructor. Kursleitung Wochenendformate.", hue: 4, src: IMAGES.t3 }, { n: "Markus Holl", r: "Stallmeister", b: "Tägliche Pflege der Anlage, Hufpflege-Koordination, Heumanagement. Seit 2017 im Team.", hue: 1, src: IMAGES.t4 }]; return (
05 · Team

Vier Menschen, eine Herde — und sehr viel {" "} tägliche Arbeit.

{people.map((p, i) =>

{p.n}

{p.r}

{p.b}

)}
); } // ============================================================ // SECTION: KONTAKT — call to enroll // ============================================================ function Kontakt() { return (
07 · Anfrage

Lust auf einen {" "} Probetag?

Schreib uns kurz: Name, dein Pferd (oder dein Wunsch nach einem), und wann du vorbeikommen willst. Wir antworten innerhalb von 48 Stunden.

Adresse Aktivstall Mannheims
Heckenstraße 35
40764 Langenfeld (Rheinland)
Telefon 0177 9757880 E-Mail kontakt@aktivstall-mannheims.de Besuche Täglich geöffnet
Schließt 21:00 Uhr
{/* Form — wired to Web3Forms */}
); } // ============================================================ // CONTACT FORM (Web3Forms) // ============================================================ function Web3FormsContact() { const WEB3FORMS_KEY = "dbe5b4d9-8c2f-4cdb-a99c-9a6cccd4630e"; const [status, setStatus] = React.useState("idle"); // idle | sending | success | error const [errorMsg, setErrorMsg] = React.useState(""); async function handleSubmit(e) { e.preventDefault(); setStatus("sending"); setErrorMsg(""); const fd = new FormData(e.currentTarget); fd.append("access_key", WEB3FORMS_KEY); fd.append("subject", "Neue Anfrage über Aktivstall-Website"); fd.append("from_name", "Aktivstall Mannheims · Website"); // honeypot is already in the form (botcheck) try { const res = await fetch("https://api.web3forms.com/submit", { method: "POST", body: fd }); const data = await res.json().catch(() => ({})); if (data && data.success) { setStatus("success"); e.target.reset(); } else { setStatus("error"); setErrorMsg(data?.message || "Unbekannter Fehler beim Senden."); } } catch (err) { setStatus("error"); setErrorMsg("Netzwerkfehler. Bitte später erneut versuchen."); } } if (status === "success") { return (

Vielen Dank!

Deine Anfrage ist bei uns angekommen. Wir melden uns innerhalb von 48 Stunden bei dir.

); } return (
{/* Hidden honeypot — bots fill this, humans don't see it */} {/* Web3Forms reply-to */} {status === "error" &&

Hoppla — {errorMsg} Bitte versuche es erneut oder schreib uns direkt an{" "} info@aktivstall-mannheims.de .

}

Mit dem Absenden stimmst du der Verarbeitung deiner Daten zu.{" "} { e.preventDefault(); window.dispatchEvent(new CustomEvent("legal:open", { detail: "datenschutz" })); }} style={{ color: "rgba(244,236,224,0.7)", borderBottom: "1px solid rgba(244,236,224,0.4)", cursor: "pointer" }}> Datenschutz

); } function FormField({ label, placeholder, textarea, type = "text", required, options, span = 1, name }) { const labelEl = ; const fieldStyle = { width: "100%", background: "transparent", border: 0, borderBottom: "1px solid rgba(244,236,224,0.28)", color: "var(--accent-on)", fontFamily: "var(--font-sans)", fontSize: 16, padding: "10px 0", outline: "none", transition: "border-color 200ms" }; const onFocus = (e) => e.currentTarget.style.borderBottomColor = "var(--accent-on)"; const onBlur = (e) => e.currentTarget.style.borderBottomColor = "rgba(244,236,224,0.28)"; let control; if (textarea) { control =