import { useState, useEffect, useRef } from "react"
import { addPropertyControls, ControlType } from "framer"
const GREEN = "#2db34a"
const GREEN_DARK = "#1a8a36"
const GREEN_LIGHT = "#e8f7ec"
const BLACK = "#1a1a1a"
const GRAY = "#f5f5f5"
const TEXT_GRAY = "#555"
const WHITE = "#ffffff"
const services = [
{
icon: "🏠",
title: "Hauswartung",
desc: "Professionelle und regelmässige Betreuung Ihrer Liegenschaft – von der Kontrolle technischer Anlagen bis zur Pflege der Aussenanlagen.",
benefits: [
"Regelmässige Kontrollen",
"Schnelle Reaktionszeiten",
"Zuverlässige Ausführung",
],
areas: ["Wohnliegenschaften", "Bürogebäude", "Gewerbeobjekte"],
},
{
icon: "✨",
title: "Reinigung",
desc: "Gründliche und sorgfältige Reinigung aller Bereiche – für ein sauberes, einladendes Umfeld.",
benefits: [
"Hochwertige Reinigungsmittel",
"Erfahrenes Team",
"Flexibel planbar",
],
areas: ["Privatwohnungen", "Büros", "Treppenhäuser"],
},
{
icon: "♻️",
title: "Entsorgung",
desc: "Fachgerechte und umweltbewusste Entsorgung von Sperrgut, Elektronikgeräten und Sondermüll.",
benefits: [
"Umweltgerecht",
"Schnell und unkompliziert",
"Alle Materialien",
],
areas: ["Wohnungsauflösungen", "Renovationen", "Umzüge"],
},
{
icon: "❄️",
title: "Winterdienst",
desc: "Zuverlässige Schneeräumung und Streudienste für Gehwege, Einfahrten und Parkplätze.",
benefits: [
"24/7 Bereitschaft",
"Schnelle Reaktion",
"Haftungssicherheit",
],
areas: ["Einfahrten", "Gehwege", "Parkplätze"],
},
{
icon: "📦",
title: "Hausräumung",
desc: "Vollständige Räumung von Wohnungen, Häusern, Kellern und Dachböden – schnell und diskret.",
benefits: [
"Komplettservice",
"Wertgegenstände werden geschont",
"Entsorgung inklusive",
],
areas: ["Wohnungsauflösungen", "Nachlässe", "Kellern & Dachböden"],
},
{
icon: "🌿",
title: "Gartenpflege",
desc: "Pflege und Gestaltung von Gärten und Grünflächen – saisonal und nachhaltig.",
benefits: [
"Saisonale Pflege",
"Fachkundige Beratung",
"Nachhaltige Methoden",
],
areas: ["Privatgärten", "Firmenareal", "Gemeinschaftsanlagen"],
},
{
icon: "🧹",
title: "Unterhaltsreinigung",
desc: "Regelmässige Reinigung von Büros, Treppenhäusern und Gewerbeflächen.",
benefits: [
"Individuelle Intervalle",
"Qualitätskontrolle",
"Zuverlässiges Team",
],
areas: ["Büros", "Treppenhäuser", "Gewerbe"],
},
{
icon: "🛋️",
title: "Wohnungsreinigung",
desc: "Professionelle Tiefenreinigung für Wohnungsübergaben, Einzug oder Periodenpflege.",
benefits: [
"Abnahmegarantie",
"Inkl. Fenster & Sanitär",
"Flexibel terminierbar",
],
areas: ["Endreinigung", "Einzugsreinigung", "Periodenreinigung"],
},
{
icon: "🏗️",
title: "Baureinigung",
desc: "Professionelle Reinigung nach Bau- und Renovationsarbeiten – termingerecht und gründlich.",
benefits: ["Baustaub entfernen", "Alle Oberflächen", "Termingerecht"],
areas: ["Neubauten", "Renovationen", "Umbau"],
},
]
const strengths = [
{
icon: "✓",
label: "Zuverlässigkeit",
desc: "Pünktlich, verlässlich und termingetreu – immer.",
},
{
icon: "★",
label: "Qualität",
desc: "Höchste Standards bei jeder Dienstleistung.",
},
{
icon: "⚡",
label: "Effizienz",
desc: "Schnelle Ausführung ohne Kompromisse.",
},
{
icon: "♡",
label: "Vertrauen",
desc: "Langjährige Kundenzufriedenheit in der Region.",
},
{
icon: "◎",
label: "Vollservice",
desc: "Alles aus einer Hand – von A bis Z.",
},
]
export default function JaruWebsite({ logoUrl, width = 1200 }: { logoUrl?: string; width?: number }) {
const FORMSPREE_ID = "YOUR_FORM_ID" // <-- hier Formspree-ID eintragen
const [page, setPage] = useState("ueber-uns")
const [formSent, setFormSent] = useState(false)
const [formError, setFormError] = useState(false)
const [formLoading, setFormLoading] = useState(false)
const [anfrageSent, setAnfrageSent] = useState(false)
const [anfrageError, setAnfrageError] = useState(false)
const [anfrageLoading, setAnfrageLoading] = useState(false)
const [selectedServices, setSelectedServices] = useState<string[]>([])
const [contactServices, setContactServices] = useState<string[]>([])
const [contactServicesError, setContactServicesError] = useState(false)
const [detailService, setDetailService] = useState<
(typeof services)[0] | null
>(null)
const [navOpen, setNavOpen] = useState(false)
const containerRef = useRef<HTMLDivElement>(null)
const [measuredWidth, setMeasuredWidth] = useState<number | null>(null)
useEffect(() => {
if (!containerRef.current) return
const observer = new ResizeObserver(entries => {
const w = entries[0]?.contentRect.width
if (w > 0) setMeasuredWidth(w)
})
observer.observe(containerRef.current)
return () => observer.disconnect()
}, [])
const effectiveWidth = measuredWidth ?? width
const isMobile = effectiveWidth < 640
const isTablet = effectiveWidth >= 640 && effectiveWidth < 1024
const px = isMobile ? 16 : isTablet ? 24 : 40
const sectionPy = isMobile ? 40 : isTablet ? 50 : 70
const navItems = [
{ id: "ueber-uns", label: "Über uns" },
{ id: "dienstleistungen", label: "Dienstleistungen" },
{ id: "kontakt", label: "Kontakt" },
{ id: "einsatzgebiet", label: "Einsatzgebiet" },
{ id: "anfrage", label: "Anfrage senden" },
]
const navigate = (id: string) => {
setPage(id)
setNavOpen(false)
window.scrollTo(0, 0)
}
const toggleService = (title: string) =>
setSelectedServices((p) =>
p.includes(title) ? p.filter((s) => s !== title) : [...p, title]
)
const toggleContactService = (title: string) => {
setContactServices((p) => {
const next = p.includes(title)
? p.filter((s) => s !== title)
: [...p, title]
if (next.length > 0) setContactServicesError(false)
return next
})
}
const s = {
root: {
fontFamily: "'Helvetica Neue', Arial, sans-serif",
color: BLACK,
background: WHITE,
minHeight: "100vh",
width: "100%",
overflowX: "hidden",
} as React.CSSProperties,
nav: {
position: "sticky" as const,
top: 0,
zIndex: 1000,
background: WHITE,
borderBottom: "1px solid #e8e8e8",
padding: `0 ${px}px`,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
height: 64,
boxShadow: "0 2px 8px rgba(0,0,0,0.06)",
},
navLogoImg: {
height: 44,
width: "auto",
objectFit: "contain" as const,
},
navLogoBadge: {
width: 40,
height: 40,
background: GREEN,
borderRadius: 8,
display: "flex",
alignItems: "center",
justifyContent: "center",
color: WHITE,
fontWeight: 900,
fontSize: 18,
},
input: {
width: "100%",
padding: "12px 16px",
borderRadius: 8,
border: "1px solid #ddd",
fontSize: 15,
outline: "none",
boxSizing: "border-box" as const,
},
textarea: {
width: "100%",
padding: "12px 16px",
borderRadius: 8,
border: "1px solid #ddd",
fontSize: 15,
minHeight: 120,
resize: "vertical" as const,
outline: "none",
boxSizing: "border-box" as const,
},
label: {
display: "block",
fontWeight: 600,
marginBottom: 6,
fontSize: 14,
} as React.CSSProperties,
formGroup: { marginBottom: 20 } as React.CSSProperties,
submitBtn: {
background: GREEN,
color: WHITE,
border: "none",
borderRadius: 8,
padding: "14px 32px",
fontWeight: 700,
fontSize: 16,
cursor: "pointer",
width: "100%",
} as React.CSSProperties,
successBox: {
background: GREEN_LIGHT,
borderRadius: 12,
padding: 32,
textAlign: "center" as const,
border: "1px solid #2db34a",
},
sectionLabel: {
color: GREEN,
fontWeight: 700,
fontSize: 12,
letterSpacing: 2,
textTransform: "uppercase" as const,
marginBottom: 10,
} as React.CSSProperties,
navCta: {
background: GREEN,
color: WHITE,
padding: "10px 20px",
borderRadius: 8,
fontWeight: 600,
fontSize: 15,
border: "none",
cursor: "pointer",
} as React.CSSProperties,
modalClose: {
background: GREEN,
color: WHITE,
border: "none",
borderRadius: 8,
padding: "12px 28px",
fontWeight: 700,
cursor: "pointer",
marginTop: 10,
} as React.CSSProperties,
}
const NavLogo = () =>
logoUrl ? (
<img src={logoUrl} alt="JARU Logo" style={s.navLogoImg} />
) : (
<div style={s.navLogoBadge}>J</div>
)
const Nav = () => (
<nav style={s.nav}>
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
<NavLogo />
<div>
<div
style={{
fontWeight: 800,
fontSize: isMobile ? 14 : 16,
color: BLACK,
}}
>
JARU
</div>
{!isMobile && (
<div
style={{
fontSize: 10,
color: TEXT_GRAY,
letterSpacing: 1,
}}
>
DIENSTLEISTUNGEN GMBH
</div>
)}
</div>
</div>
{!isMobile && !isTablet && (
<div style={{ display: "flex", gap: 4, alignItems: "center" }}>
{navItems.map((item) => (
<button
key={item.id}
style={{
padding: "8px 12px",
borderRadius: 6,
cursor: "pointer",
fontSize: 14,
border: "none",
background: "transparent",
color: page === item.id ? GREEN : BLACK,
fontWeight: page === item.id ? 700 : 500,
}}
onClick={() => navigate(item.id)}
>
{item.label}
</button>
))}
</div>
)}
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
{!isMobile && !isTablet && (
<button
style={s.navCta}
onClick={() => navigate("anfrage")}
>
Anfrage senden
</button>
)}
{(isMobile || isTablet) && (
<button
onClick={() => setNavOpen((o) => !o)}
style={{
background: "none",
border: "none",
cursor: "pointer",
fontSize: 24,
color: BLACK,
padding: 4,
}}
>
{navOpen ? "✕" : "☰"}
</button>
)}
</div>
{navOpen && (isMobile || isTablet) && (
<div
style={{
position: "absolute",
top: 64,
left: 0,
right: 0,
background: WHITE,
borderBottom: "1px solid #e8e8e8",
boxShadow: "0 8px 24px rgba(0,0,0,0.1)",
zIndex: 999,
}}
>
{navItems.map((item) => (
<button
key={item.id}
onClick={() => navigate(item.id)}
style={{
display: "block",
width: "100%",
padding: "16px 24px",
textAlign: "left" as const,
border: "none",
borderBottom: "1px solid #f0f0f0",
background:
page === item.id ? GREEN_LIGHT : WHITE,
color: page === item.id ? GREEN : BLACK,
fontWeight: page === item.id ? 700 : 500,
fontSize: 16,
cursor: "pointer",
}}
>
{item.label}
</button>
))}
<div style={{ padding: 16 }}>
<button
style={{
...s.navCta,
width: "100%",
padding: "14px",
}}
onClick={() => navigate("anfrage")}
>
Anfrage senden
</button>
</div>
</div>
)}
</nav>
)
const Footer = () => (
<footer
style={{
background: BLACK,
color: WHITE,
padding: `${sectionPy}px ${px}px 30px`,
}}
>
<div
style={{
display: "grid",
gridTemplateColumns: isMobile
? "1fr"
: isTablet
? "1fr 1fr"
: "2fr 1fr 1fr",
gap: isMobile ? 32 : 40,
maxWidth: 1100,
margin: "0 auto 40px",
}}
>
<div>
<p
style={{
color: WHITE,
fontWeight: 800,
fontSize: 18,
marginBottom: 8,
}}
>
JARU Dienstleistungen GmbH
</p>
<p style={{ color: "#999", fontSize: 14, lineHeight: 1.7 }}>
Ihr zuverlässiger Partner für professionelle
Dienstleistungen in der Region Basel und darüber hinaus.
</p>
</div>
<div>
<p
style={{
fontWeight: 700,
fontSize: 15,
marginBottom: 16,
color: WHITE,
}}
>
Dienstleistungen
</p>
{[
"Hauswartung",
"Reinigung",
"Winterdienst",
"Entsorgung",
"Gartenpflege",
].map((l) => (
<p
key={l}
style={{
color: "#999",
fontSize: 14,
marginBottom: 10,
cursor: "pointer",
}}
onClick={() => navigate("dienstleistungen")}
>
{l}
</p>
))}
</div>
{!isMobile && (
<div>
<p
style={{
fontWeight: 700,
fontSize: 15,
marginBottom: 16,
color: WHITE,
}}
>
Kontakt
</p>
<p style={{ color: "#999", fontSize: 14, lineHeight: 1.7 }}>
Herzentalstraße 3, 4143 Dornach
</p>
<p style={{ color: "#999", fontSize: 14, lineHeight: 1.7 }}>
076 208 36 34
</p>
<p style={{ color: "#999", fontSize: 14, lineHeight: 1.7 }}>
info@jaru-dienstleistungen.ch
</p>
</div>
)}
</div>
<div
style={{
borderTop: "1px solid #333",
paddingTop: 24,
textAlign: "center" as const,
color: "#666",
fontSize: 12,
maxWidth: 1100,
margin: "0 auto",
}}
>
<p>
© 2025 JARU Dienstleistungen GmbH · Herzentalstraße 3, 4143 Dornach
·{" "}
<a
href="mailto:info@jaru-dienstleistungen.ch"
style={{ color: GREEN }}
>
info@jaru-dienstleistungen.ch
</a>
</p>
<p style={{ marginTop: 8 }}>
<span
onClick={() => navigate("datenschutz")}
style={{ color: GREEN, cursor: "pointer", marginRight: 16 }}
>
Datenschutzerklärung
</span>
<span
onClick={() => navigate("agb")}
style={{ color: GREEN, cursor: "pointer" }}
>
AGB
</span>
</p>
</div>
</footer>
)
const ServiceCheckboxes = ({
selected,
onToggle,
error,
}: {
selected: string[]
onToggle: (t: string) => void
error?: boolean
}) => (
<div
style={{
display: "grid",
gridTemplateColumns: isMobile
? "1fr"
: isTablet
? "1fr 1fr"
: "1fr 1fr 1fr",
gap: 10,
}}
>
{services.map((svc) => {
const checked = selected.includes(svc.title)
return (
<label
key={svc.title}
style={{
display: "flex",
alignItems: "center",
gap: 10,
padding: "10px 14px",
borderRadius: 8,
border: checked
? `2px solid ${GREEN}`
: error
? "1px solid #e53e3e"
: "1px solid #ddd",
background: checked ? GREEN_LIGHT : WHITE,
cursor: "pointer",
fontSize: 14,
fontWeight: checked ? 600 : 400,
color: checked ? GREEN_DARK : BLACK,
userSelect: "none" as const,
}}
onClick={() => onToggle(svc.title)}
>
<span
style={{
width: 18,
height: 18,
borderRadius: 4,
border: checked
? `2px solid ${GREEN}`
: "2px solid #ccc",
background: checked ? GREEN : WHITE,
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
color: WHITE,
fontSize: 11,
fontWeight: 900,
}}
>
{checked ? "✓" : ""}
</span>
{svc.title}
</label>
)
})}
</div>
)
const pages: Record<string, JSX.Element> = {
"ueber-uns": (
<>
<div
style={{
background:
"linear-gradient(135deg, #2db34a 0%, #1a8a36 100%)",
color: WHITE,
padding: `${sectionPy}px ${px}px`,
textAlign: "center" as const,
}}
>
<div
style={{
display: "inline-block",
background: "rgba(255,255,255,0.2)",
borderRadius: 30,
padding: "8px 20px",
fontSize: 13,
fontWeight: 600,
marginBottom: 20,
}}
>
Ihr zuverlässiger Partner in der Region Basel
</div>
<h1
style={{
fontSize: isMobile ? 26 : isTablet ? 36 : 52,
fontWeight: 800,
margin: "0 0 16px",
lineHeight: 1.2,
}}
>
{isMobile ? (
<>
Zuverlässige
<br />
Dienstleistungen
<br />
für Privatpersonen
<br />
und Unternehmen
</>
) : (
<>
Zuverlässige Dienstleistungen
<br />
für Privatpersonen und Unternehmen
</>
)}
</h1>
<p
style={{
fontSize: isMobile ? 15 : 18,
opacity: 0.9,
maxWidth: 600,
margin: "0 auto 28px",
}}
>
JARU Dienstleistungen steht für Qualität,
Zuverlässigkeit und kundenorientierte Lösungen.
</p>
<div
style={{
display: "flex",
gap: 12,
justifyContent: "center",
flexWrap: "wrap" as const,
}}
>
<button
style={{
background: WHITE,
color: GREEN,
padding: isMobile ? "12px 20px" : "14px 28px",
borderRadius: 8,
fontWeight: 700,
fontSize: isMobile ? 14 : 16,
border: "none",
cursor: "pointer",
}}
onClick={() => navigate("kontakt")}
>
Jetzt Kontakt aufnehmen
</button>
<button
style={{
background: "transparent",
color: WHITE,
padding: isMobile ? "12px 20px" : "14px 28px",
borderRadius: 8,
fontWeight: 700,
fontSize: isMobile ? 14 : 16,
border: "2px solid rgba(255,255,255,0.7)",
cursor: "pointer",
}}
onClick={() => navigate("anfrage")}
>
Anfrage senden
</button>
</div>
</div>
<div
style={{
padding: `${sectionPy}px ${px}px`,
maxWidth: 1100,
margin: "0 auto",
}}
>
<p style={s.sectionLabel}>WER WIR SIND</p>
<div
style={{
display: "grid",
gridTemplateColumns:
isMobile || isTablet ? "1fr" : "1fr 1fr",
gap: isMobile ? 32 : 50,
alignItems: "start",
}}
>
<div>
<h2
style={{
fontSize: isMobile ? 26 : 34,
fontWeight: 800,
margin: "0 0 16px",
}}
>
Über uns
</h2>
<p
style={{
fontSize: 16,
lineHeight: 1.8,
color: TEXT_GRAY,
}}
>
JARU Dienstleistungen ist Ihr zuverlässiger
Partner für professionelle Dienstleistungen in
der Region Basel und darüber hinaus.
</p>
<p
style={{
fontSize: 16,
lineHeight: 1.8,
color: TEXT_GRAY,
marginTop: 14,
}}
>
Wir legen grossen Wert auf eine saubere
Ausführung, termingerechte Arbeiten und
persönliche Betreuung.
</p>
<button
style={{
...s.navCta,
marginTop: 24,
padding: "12px 28px",
}}
onClick={() => navigate("dienstleistungen")}
>
Unsere Leistungen →
</button>
</div>
<div
style={{
background: GREEN_LIGHT,
borderRadius: 16,
padding: isMobile ? 20 : 32,
border: "1px solid #2db34a22",
}}
>
<p
style={{
fontWeight: 700,
fontSize: 17,
marginBottom: 18,
}}
>
JARU Dienstleistungen GmbH
</p>
{[
["📍", "Herzentalstraße 3, 4143 Dornach"],
["📞", "076 208 36 34"],
["✉️", "info@jaru-dienstleistungen.ch"],
["🌐", "www.jaru-dienstleistungen.ch"],
].map(([icon, val]) => (
<div
key={val}
style={{
display: "flex",
alignItems: "center",
gap: 12,
marginBottom: 14,
fontSize: 15,
color: BLACK,
}}
>
<span
style={{
fontSize: 18,
width: 26,
textAlign: "center" as const,
}}
>
{icon}
</span>
{val}
</div>
))}
<div
style={{
borderTop: "2px solid #2db34a30",
margin: "20px 0",
}}
/>
<div
style={{
color: GREEN,
fontWeight: 600,
display: "flex",
alignItems: "center",
gap: 8,
}}
>
✅ Antwort innerhalb von 24 Stunden
</div>
</div>
</div>
</div>
<div
style={{
background: GREEN_LIGHT,
padding: `${sectionPy}px ${px}px`,
}}
>
<div style={{ maxWidth: 1100, margin: "0 auto" }}>
<p
style={{
...s.sectionLabel,
textAlign: "center" as const,
}}
>
UNSERE STÄRKEN
</p>
<h2
style={{
fontSize: isMobile ? 24 : 34,
fontWeight: 800,
margin: "0 0 12px",
textAlign: "center" as const,
}}
>
Warum JARU Dienstleistungen?
</h2>
<p
style={{
textAlign: "center" as const,
color: TEXT_GRAY,
maxWidth: 600,
margin: "0 auto 40px",
fontSize: 16,
}}
>
Wir bieten mehr als nur Dienstleistungen – wir
bieten Verlässlichkeit.
</p>
<div
style={{
display: "grid",
gridTemplateColumns: isMobile
? "1fr 1fr"
: isTablet
? "repeat(3, 1fr)"
: "repeat(5, 1fr)",
gap: 16,
}}
>
{strengths.map((st, i) => (
<div
key={i}
style={{
background: WHITE,
borderRadius: 14,
padding: "24px 16px",
textAlign: "center" as const,
border: "1px solid #2db34a22",
}}
>
<div
style={{
width: 48,
height: 48,
background: WHITE,
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
margin: "0 auto 12px",
fontSize: 20,
fontWeight: 900,
color: GREEN,
border: "2px solid #2db34a",
}}
>
{st.icon}
</div>
<p
style={{
fontWeight: 700,
fontSize: 13,
marginBottom: 6,
}}
>
{st.label}
</p>
<p
style={{
fontSize: 12,
color: TEXT_GRAY,
lineHeight: 1.5,
}}
>
{st.desc}
</p>
</div>
))}
</div>
</div>
</div>
</>
),
dienstleistungen: (
<>
{detailService && (
<div
style={{
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
background: "rgba(0,0,0,0.5)",
zIndex: 2000,
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: 16,
}}
onClick={() => setDetailService(null)}
>
<div
style={{
background: WHITE,
borderRadius: 20,
padding: isMobile ? 24 : 40,
maxWidth: 560,
width: "100%",
maxHeight: "85vh",
overflowY: "auto" as const,
}}
onClick={(e) => e.stopPropagation()}
>
<div style={{ fontSize: 36, marginBottom: 10 }}>
{detailService.icon}
</div>
<h2
style={{
fontSize: 24,
fontWeight: 800,
marginBottom: 12,
}}
>
{detailService.title}
</h2>
<p style={{ color: TEXT_GRAY, marginBottom: 16 }}>
{detailService.desc}
</p>
<div
style={{
background: GREEN_LIGHT,
borderRadius: 10,
padding: 16,
marginBottom: 16,
}}
>
<p style={{ fontWeight: 700, marginBottom: 8 }}>
Ihre Vorteile:
</p>
{detailService.benefits.map((b, i) => (
<p
key={i}
style={{ fontSize: 14, marginBottom: 6 }}
>
✅ {b}
</p>
))}
</div>
<div
style={{
display: "flex",
gap: 8,
flexWrap: "wrap" as const,
marginBottom: 16,
}}
>
{detailService.areas.map((a, i) => (
<span
key={i}
style={{
background: GREEN_LIGHT,
color: GREEN,
padding: "4px 12px",
borderRadius: 20,
fontSize: 13,
fontWeight: 600,
}}
>
{a}
</span>
))}
</div>
<div
style={{
background: GREEN_LIGHT,
padding: 14,
borderRadius: 10,
marginBottom: 14,
fontWeight: 600,
color: GREEN,
}}
>
Preis auf Anfrage
</div>
<div
style={{
display: "flex",
gap: 10,
flexWrap: "wrap" as const,
}}
>
<button
style={s.modalClose}
onClick={() => {
navigate("anfrage")
setDetailService(null)
}}
>
Anfrage senden
</button>
<button
style={{
...s.modalClose,
background: GRAY,
color: BLACK,
}}
onClick={() => setDetailService(null)}
>
Schliessen
</button>
</div>
</div>
</div>
)}
<div style={{ padding: `${sectionPy}px ${px}px` }}>
<div style={{ maxWidth: 1100, margin: "0 auto" }}>
<p style={s.sectionLabel}>WAS WIR ANBIETEN</p>
<h2
style={{
fontSize: isMobile ? 26 : 34,
fontWeight: 800,
margin: "0 0 12px",
}}
>
Unsere Dienstleistungen
</h2>
<p
style={{
color: TEXT_GRAY,
marginBottom: 32,
fontSize: 16,
}}
>
Professionell, zuverlässig und zu fairen
Konditionen. Alle Preise auf Anfrage.
</p>
<div
style={{
display: "grid",
gridTemplateColumns: isMobile
? "1fr"
: isTablet
? "1fr 1fr"
: "repeat(3, 1fr)",
gap: 20,
}}
>
{services.map((svc, i) => (
<div
key={i}
style={{
background: WHITE,
borderRadius: 14,
padding: 24,
border: "1px solid #e8e8e8",
cursor: "pointer",
}}
onClick={() => setDetailService(svc)}
>
<div
style={{ fontSize: 30, marginBottom: 12 }}
>
{svc.icon}
</div>
<p
style={{
fontWeight: 700,
fontSize: 17,
marginBottom: 8,
}}
>
{svc.title}
</p>
<p
style={{
fontSize: 14,
color: TEXT_GRAY,
lineHeight: 1.6,
marginBottom: 14,
}}
>
{svc.desc}
</p>
<div
style={{
color: GREEN,
fontWeight: 600,
fontSize: 14,
}}
>
Preis auf Anfrage →
</div>
</div>
))}
</div>
</div>
</div>
</>
),
kontakt: (
<div
style={{
padding: `${sectionPy}px ${px}px`,
maxWidth: 1100,
margin: "0 auto",
}}
>
<p style={s.sectionLabel}>KONTAKT</p>
<h2
style={{
fontSize: isMobile ? 26 : 34,
fontWeight: 800,
margin: "0 0 12px",
}}
>
Kontaktieren Sie uns
</h2>
<p style={{ color: TEXT_GRAY, marginBottom: 36, fontSize: 16 }}>
Füllen Sie das Formular aus oder nehmen Sie direkt Kontakt
mit uns auf.
</p>
<div
style={{
display: "grid",
gridTemplateColumns:
isMobile || isTablet ? "1fr" : "1fr 360px",
gap: isMobile ? 32 : 50,
alignItems: "start",
}}
>
<div>
{formSent ? (
<div style={s.successBox}>
<div style={{ fontSize: 48, marginBottom: 16 }}>
✅
</div>
<h3 style={{ color: GREEN }}>
Nachricht gesendet!
</h3>
<p style={{ color: TEXT_GRAY }}>
Wir melden uns innerhalb von 24 Stunden bei
Ihnen.
</p>
</div>
) : (
<form
onSubmit={async (e) => {
e.preventDefault()
const form = e.currentTarget as HTMLFormElement
const data: Record<string, string> = {}
new FormData(form).forEach((v, k) => { data[k] = v as string })
if (selectedServices.length > 0) data["Dienstleistungen"] = selectedServices.join(", ")
setFormLoading(true)
setFormError(false)
try {
const res = await fetch(`https://formspree.io/f/${FORMSPREE_ID}`, {
method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify(data),
})
if (res.ok) { setFormSent(true) } else { setFormError(true) }
} catch { setFormError(true) }
setFormLoading(false)
}}
>
<div style={s.formGroup}>
<label style={s.label}>Name *</label>
<input name="Name" style={s.input} required placeholder="Ihr vollständiger Name" />
</div>
<div
style={{
display: "grid",
gridTemplateColumns: isMobile ? "1fr" : "1fr 1fr",
gap: 16,
}}
>
<div style={s.formGroup}>
<label style={s.label}>E-Mail *</label>
<input name="E-Mail" style={s.input} type="email" required placeholder="ihre@email.ch" />
</div>
<div style={s.formGroup}>
<label style={s.label}>Telefon</label>
<input name="Telefon" style={s.input} placeholder="076 XXX XX XX" />
</div>
</div>
<div style={s.formGroup}>
<label style={s.label}>
Gewünschte Dienstleistungen
</label>
<div
style={{
display: "grid",
gridTemplateColumns: isMobile
? "1fr"
: "1fr 1fr",
gap: 8,
}}
>
{services.map((svc) => (
<label
key={svc.title}
style={{
display: "flex",
alignItems: "center",
gap: 8,
fontSize: 14,
cursor: "pointer",
padding: "4px 0",
}}
>
<input
type="checkbox"
checked={selectedServices.includes(
svc.title
)}
onChange={() =>
toggleService(svc.title)
}
style={{
accentColor: GREEN,
}}
/>
{svc.title}
</label>
))}
</div>
</div>
<div style={s.formGroup}>
<label style={s.label}>Beschreibung</label>
<textarea name="Beschreibung" style={s.textarea} placeholder="Beschreiben Sie Ihr Anliegen..." />
</div>
{formError && (
<p style={{ color: "#e53e3e", fontSize: 13, marginBottom: 12 }}>
Fehler beim Senden. Bitte versuchen Sie es erneut oder schreiben Sie uns direkt an info@jaru-dienstleistungen.ch
</p>
)}
<p style={{ fontSize: 12, color: TEXT_GRAY, marginBottom: 16 }}>
* Pflichtfelder.
</p>
<button type="submit" style={{ ...s.submitBtn, opacity: formLoading ? 0.7 : 1 }} disabled={formLoading}>
{formLoading ? "Wird gesendet..." : "Anfrage absenden"}
</button>
</form>
)}
</div>
<div
style={{
background: GREEN_LIGHT,
borderRadius: 16,
padding: isMobile ? 20 : 28,
border: "1px solid #2db34a33",
}}
>
<p
style={{
fontWeight: 800,
fontSize: 17,
marginBottom: 4,
color: BLACK,
}}
>
JARU Dienstleistungen GmbH
</p>
<p
style={{
fontSize: 13,
color: TEXT_GRAY,
marginBottom: 20,
}}
>
Ihr zuverlässiger Partner in der Region Basel
</p>
<div
style={{
borderTop: "1px solid #2db34a22",
paddingTop: 18,
}}
>
{[
{
icon: "📍",
label: "Adresse",
val: "Herzentalstraße 3, 4143 Dornach",
},
{
icon: "📞",
label: "Telefon",
val: "076 208 36 34",
},
{
icon: "✉️",
label: "E-Mail",
val: "info@jaru-dienstleistungen.ch",
isLink: true,
},
{
icon: "🌐",
label: "Website",
val: "www.jaru-dienstleistungen.ch",
},
].map(({ icon, label, val, isLink }) => (
<div
key={label}
style={{
display: "flex",
alignItems: "flex-start",
gap: 12,
marginBottom: 18,
}}
>
<span style={{ fontSize: 18, marginTop: 2 }}>
{icon}
</span>
<div>
<p
style={{
fontWeight: 600,
fontSize: 12,
color: TEXT_GRAY,
marginBottom: 2,
}}
>
{label}
</p>
{isLink ? (
<a
href={`mailto:${val}`}
style={{
fontWeight: 500,
fontSize: 14,
color: GREEN,
textDecoration: "none",
}}
>
{val}
</a>
) : (
<p
style={{
fontWeight: 500,
fontSize: 14,
color: BLACK,
}}
>
{val}
</p>
)}
</div>
</div>
))}
</div>
<div
style={{
background: WHITE,
borderRadius: 10,
padding: "12px 16px",
display: "flex",
alignItems: "center",
gap: 10,
}}
>
<span style={{ color: GREEN, fontSize: 16 }}>✅</span>
<p
style={{
fontWeight: 600,
fontSize: 13,
color: GREEN_DARK,
}}
>
Antwort innerhalb von 24 Stunden
</p>
</div>
</div>
</div>
</div>
),
einsatzgebiet: (
<div
style={{
padding: `${sectionPy}px ${px}px`,
maxWidth: 1100,
margin: "0 auto",
}}
>
<p style={s.sectionLabel}>WO WIR TÄTIG SIND</p>
<h2
style={{
fontSize: isMobile ? 26 : 34,
fontWeight: 800,
margin: "0 0 12px",
}}
>
Unser Einsatzgebiet
</h2>
<p style={{ color: TEXT_GRAY, marginBottom: 28, fontSize: 16 }}>
Hauptsächlich in der Region Basel – und schweizweit auf
Anfrage.
</p>
<div
style={{
background: GREEN_LIGHT,
borderRadius: 14,
height: isMobile ? 220 : 350,
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 40,
border: "2px dashed #2db34a44",
}}
>
🗺️
</div>
<div
style={{
display: "grid",
gridTemplateColumns: isMobile
? "1fr 1fr"
: isTablet
? "repeat(3, 1fr)"
: "repeat(4, 1fr)",
gap: 12,
marginTop: 24,
}}
>
{[
"Basel-Stadt",
"Basel-Landschaft",
"Dornach",
"Allschwil",
"Liestal",
"Aargau",
"Solothurn",
"Gesamte Schweiz (auf Anfrage)",
].map((area) => (
<div
key={area}
style={{
background: WHITE,
border: "1px solid #2db34a33",
borderRadius: 10,
padding: "14px 16px",
textAlign: "center" as const,
fontWeight: 600,
fontSize: 14,
}}
>
{area}
</div>
))}
</div>
</div>
),
anfrage: (
<div style={{ padding: `${sectionPy}px ${px}px` }}>
<div style={{ maxWidth: 720, margin: "0 auto" }}>
<p style={s.sectionLabel}>ANFRAGE</p>
<h2
style={{
fontSize: isMobile ? 26 : 34,
fontWeight: 800,
margin: "0 0 12px",
}}
>
Anfrage senden
</h2>
<p
style={{
color: TEXT_GRAY,
marginBottom: 36,
fontSize: 16,
}}
>
Füllen Sie das Formular aus – wir melden uns innerhalb
von 24 Stunden bei Ihnen.
</p>
{anfrageSent ? (
<div style={s.successBox}>
<div style={{ fontSize: 48, marginBottom: 16 }}>
✅
</div>
<h3 style={{ color: GREEN }}>Anfrage gesendet!</h3>
<p style={{ color: TEXT_GRAY }}>
Wir melden uns innerhalb von 24 Stunden bei
Ihnen zurück.
</p>
</div>
) : (
<form
onSubmit={async (e) => {
e.preventDefault()
if (contactServices.length === 0) {
setContactServicesError(true)
return
}
const form = e.currentTarget as HTMLFormElement
const data: Record<string, string> = {}
new FormData(form).forEach((v, k) => { data[k] = v as string })
data["Dienstleistungen"] = contactServices.join(", ")
setAnfrageLoading(true)
setAnfrageError(false)
try {
const res = await fetch(`https://formspree.io/f/${FORMSPREE_ID}`, {
method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify(data),
})
if (res.ok) { setAnfrageSent(true) } else { setAnfrageError(true) }
} catch { setAnfrageError(true) }
setAnfrageLoading(false)
}}
>
<div
style={{
display: "grid",
gridTemplateColumns: isMobile
? "1fr"
: "1fr 1fr",
gap: 16,
}}
>
<div style={s.formGroup}>
<label style={s.label}>Name *</label>
<input name="Name" style={s.input} required placeholder="Ihr Name" />
</div>
<div style={s.formGroup}>
<label style={s.label}>E-Mail *</label>
<input name="E-Mail" style={s.input} type="email" required placeholder="ihre@email.ch" />
</div>
</div>
<div style={s.formGroup}>
<label style={s.label}>Telefon</label>
<input name="Telefon" style={s.input} placeholder="076 XXX XX XX" />
</div>
<div style={s.formGroup}>
<label style={{ ...s.label, marginBottom: 12 }}>
Gewünschte Dienstleistung(en) *
</label>
<ServiceCheckboxes
selected={contactServices}
onToggle={toggleContactService}
error={contactServicesError}
/>
{contactServicesError && (
<p style={{ color: "#e53e3e", fontSize: 13, marginTop: 8 }}>
Bitte wählen Sie mindestens eine Dienstleistung aus.
</p>
)}
</div>
<div style={s.formGroup}>
<label style={s.label}>Nachricht *</label>
<textarea name="Nachricht" style={s.textarea} required placeholder="Wie können wir Ihnen helfen?" />
</div>
{anfrageError && (
<p style={{ color: "#e53e3e", fontSize: 13, marginBottom: 12 }}>
Fehler beim Senden. Bitte versuchen Sie es erneut oder schreiben Sie uns direkt an info@jaru-dienstleistungen.ch
</p>
)}
<p style={{ fontSize: 12, color: TEXT_GRAY, marginBottom: 16 }}>
* Pflichtfelder. Ihre Daten werden gemäss unserer{" "}
<span onClick={() => navigate("datenschutz")} style={{ color: GREEN, cursor: "pointer", textDecoration: "underline" }}>
Datenschutzerklärung
</span>{" "}
behandelt.
</p>
<button type="submit" style={{ ...s.submitBtn, opacity: anfrageLoading ? 0.7 : 1 }} disabled={anfrageLoading}>
{anfrageLoading ? "Wird gesendet..." : "Nachricht senden"}
</button>
</form>
)}
</div>
</div>
),
"datenschutz": (
<div style={{ padding: `${sectionPy}px ${px}px`, maxWidth: 860, margin: "0 auto" }}>
<h1 style={{ fontSize: isMobile ? 26 : 36, fontWeight: 800, marginBottom: 8 }}>Datenschutzerklärung</h1>
<p style={{ color: TEXT_GRAY, fontSize: 14, marginBottom: 40 }}>Stand: Januar 2025</p>
{[
{
title: "1. Verantwortlicher",
text: "Verantwortlicher im Sinne der Datenschutzgesetze ist:\n\nJARU Dienstleistungen GmbH\nHerzentalstraße 3\n4143 Dornach\nSchweiz\n\nTelefon: 076 208 36 34\nE-Mail: info@jaru-dienstleistungen.ch",
},
{
title: "2. Erhebung und Verarbeitung personenbezogener Daten",
text: "Wir erheben personenbezogene Daten nur, soweit dies für die Erbringung unserer Dienstleistungen erforderlich ist. Dies umfasst:\n\n• Kontaktdaten (Name, Adresse, E-Mail, Telefonnummer) bei Anfragen über unser Kontaktformular\n• Angaben zur gewünschten Dienstleistung\n• Kommunikationsdaten im Rahmen der Vertragsabwicklung\n\nDie Datenerhebung erfolgt auf Basis von Art. 31 DSG (Schweizer Datenschutzgesetz) oder Art. 6 Abs. 1 DSGVO, sofern anwendbar.",
},
{
title: "3. Zweck der Datenverarbeitung",
text: "Ihre Daten verwenden wir ausschliesslich für:\n\n• Bearbeitung Ihrer Anfragen und Aufträge\n• Kommunikation im Rahmen bestehender oder angebahnter Verträge\n• Rechnungsstellung und Buchhaltung\n• Einhaltung gesetzlicher Pflichten",
},
{
title: "4. Weitergabe von Daten",
text: "Ihre personenbezogenen Daten werden nicht an Dritte verkauft oder ohne Ihre ausdrückliche Einwilligung weitergegeben, es sei denn, dies ist zur Vertragserfüllung erforderlich (z. B. an Subunternehmer, die an Ihrem Auftrag beteiligt sind) oder gesetzlich vorgeschrieben.",
},
{
title: "5. Speicherdauer",
text: "Personenbezogene Daten werden nur so lange gespeichert, wie es für den jeweiligen Zweck erforderlich ist oder gesetzliche Aufbewahrungsfristen dies verlangen. Buchhaltungsrelevante Daten werden gemäss den gesetzlichen Vorschriften (in der Regel 10 Jahre) aufbewahrt.",
},
{
title: "6. Datensicherheit",
text: "Wir setzen technische und organisatorische Sicherheitsmassnahmen ein, um Ihre Daten gegen zufällige oder vorsätzliche Manipulation, Verlust, Zerstörung oder den Zugriff unberechtigter Personen zu schützen.",
},
{
title: "7. Ihre Rechte",
text: "Sie haben jederzeit das Recht auf:\n\n• Auskunft über Ihre bei uns gespeicherten Daten\n• Berichtigung unrichtiger Daten\n• Löschung Ihrer Daten (sofern keine gesetzlichen Aufbewahrungspflichten entgegenstehen)\n• Einschränkung der Verarbeitung\n• Datenübertragbarkeit\n• Widerspruch gegen die Verarbeitung\n\nZur Ausübung Ihrer Rechte wenden Sie sich bitte an: info@jaru-dienstleistungen.ch",
},
{
title: "8. Cookies und Website-Tracking",
text: "Unsere Website verwendet keine Tracking-Cookies und verzichtet auf Analyse-Tools wie Google Analytics. Es werden keine Nutzerprofile erstellt.",
},
{
title: "9. Kontakt bei Datenschutzfragen",
text: "Bei Fragen zum Datenschutz wenden Sie sich bitte direkt an uns:\n\nJARU Dienstleistungen GmbH\nHerzentalstraße 3, 4143 Dornach\nE-Mail: info@jaru-dienstleistungen.ch\nTelefon: 076 208 36 34",
},
].map(({ title, text }) => (
<div key={title} style={{ marginBottom: 36 }}>
<h2 style={{ fontSize: isMobile ? 17 : 20, fontWeight: 700, marginBottom: 12, color: BLACK }}>{title}</h2>
{text.split("\n").map((line, i) =>
line === "" ? <br key={i} /> : <p key={i} style={{ fontSize: 15, color: "#444", lineHeight: 1.7, marginBottom: 4 }}>{line}</p>
)}
</div>
))}
<button onClick={() => navigate("ueber-uns")} style={{ marginTop: 24, background: "none", border: `1px solid ${GREEN}`, color: GREEN, padding: "10px 24px", borderRadius: 6, cursor: "pointer", fontSize: 14 }}>
← Zurück zur Startseite
</button>
</div>
),
"agb": (
<div style={{ padding: `${sectionPy}px ${px}px`, maxWidth: 860, margin: "0 auto" }}>
<h1 style={{ fontSize: isMobile ? 26 : 36, fontWeight: 800, marginBottom: 8 }}>Allgemeine Geschäftsbedingungen (AGB)</h1>
<p style={{ color: TEXT_GRAY, fontSize: 14, marginBottom: 40 }}>JARU Dienstleistungen GmbH · Stand: Januar 2025</p>
{[
{
title: "1. Geltungsbereich",
text: "Diese Allgemeinen Geschäftsbedingungen gelten für alle Verträge zwischen der JARU Dienstleistungen GmbH, Herzentalstraße 3, 4143 Dornach (nachfolgend «JARU») und ihren Kunden (nachfolgend «Auftraggeber») über die Erbringung von Dienstleistungen in den Bereichen Hauswartung, Reinigung, Winterdienst, Entsorgung, Gartenpflege und weitere Dienstleistungen.",
},
{
title: "2. Vertragsschluss",
text: "Ein Vertrag kommt zustande durch:\n\n• Schriftliche oder elektronische Auftragsbestätigung durch JARU, oder\n• Beginn der Leistungserbringung durch JARU nach Erhalt eines Auftrags.\n\nOfferten von JARU sind freibleibend und unverbindlich, sofern nicht ausdrücklich eine Bindefrist genannt wird.",
},
{
title: "3. Leistungsumfang",
text: "Der Leistungsumfang richtet sich nach der schriftlichen Vereinbarung oder dem bestätigten Angebot. Änderungen oder Erweiterungen des vereinbarten Leistungsumfangs bedürfen der schriftlichen Bestätigung durch JARU und können zu einer Anpassung des Preises führen.",
},
{
title: "4. Preise und Zahlungsbedingungen",
text: "• Alle Preise verstehen sich in Schweizer Franken (CHF) zuzüglich der gesetzlichen Mehrwertsteuer, sofern nicht anders vereinbart.\n• Rechnungen sind innert 30 Tagen ab Rechnungsdatum netto zahlbar.\n• Bei Zahlungsverzug ist JARU berechtigt, Verzugszinsen von 5 % p.a. sowie Mahngebühren zu berechnen.\n• JARU behält sich das Recht vor, Vorauszahlungen oder Anzahlungen zu verlangen.",
},
{
title: "5. Pflichten des Auftraggebers",
text: "Der Auftraggeber verpflichtet sich:\n\n• JARU rechtzeitig Zugang zu den erforderlichen Räumlichkeiten und Liegenschaften zu gewähren.\n• Notwendige Informationen und Unterlagen bereitzustellen.\n• Eventuelle Gefahrenstellen oder besondere Bedingungen vor Auftragsbeginn mitzuteilen.\n• Dafür zu sorgen, dass Dritte (z. B. Mieter) über die Arbeiten informiert sind, sofern erforderlich.",
},
{
title: "6. Haftung",
text: "JARU haftet für Schäden, die durch nachweisliches Verschulden entstanden sind. Die Haftung ist auf den direkten Schaden begrenzt. Eine Haftung für Folgeschäden, entgangenen Gewinn oder indirekte Schäden ist ausgeschlossen, soweit gesetzlich zulässig.\n\nFür Schäden durch höhere Gewalt, unvorhersehbare Ereignisse oder Umstände ausserhalb des Einflussbereichs von JARU wird keine Haftung übernommen.",
},
{
title: "7. Kündigung und Rücktritt",
text: "• Einmalige Aufträge können bis 48 Stunden vor dem vereinbarten Termin kostenlos storniert werden. Bei späteren Stornierungen behält sich JARU vor, bis zu 50 % des vereinbarten Preises in Rechnung zu stellen.\n• Daueraufträge können mit einer Frist von 30 Tagen auf Ende eines Kalendermonats gekündigt werden, sofern nichts anderes vereinbart wurde.\n• Bei schwerwiegenden Vertragsverletzungen durch eine der Parteien ist die sofortige Kündigung aus wichtigem Grund möglich.",
},
{
title: "8. Vertraulichkeit",
text: "Beide Parteien verpflichten sich, vertrauliche Informationen der jeweils anderen Partei nicht an Dritte weiterzugeben und nur für die Zwecke des Vertrags zu verwenden. Diese Verpflichtung gilt auch nach Beendigung des Vertragsverhältnisses.",
},
{
title: "9. Datenschutz",
text: "Die Verarbeitung personenbezogener Daten erfolgt gemäss unserer Datenschutzerklärung und den anwendbaren gesetzlichen Bestimmungen (Schweizer DSG sowie DSGVO, sofern anwendbar).",
},
{
title: "10. Anwendbares Recht und Gerichtsstand",
text: "Diese AGB sowie alle daraus entstehenden Vertragsbeziehungen unterliegen schweizerischem Recht. Gerichtsstand für alle Streitigkeiten ist Dornach, Kanton Solothurn, Schweiz. JARU behält sich vor, den Auftraggeber auch an seinem Wohn- oder Geschäftssitz zu belangen.",
},
{
title: "11. Salvatorische Klausel",
text: "Sollten einzelne Bestimmungen dieser AGB ganz oder teilweise unwirksam sein oder werden, berührt dies die Wirksamkeit der übrigen Bestimmungen nicht. Die unwirksame Bestimmung wird durch eine rechtlich zulässige Regelung ersetzt, die dem wirtschaftlichen Zweck der unwirksamen Regelung möglichst nahekommt.",
},
].map(({ title, text }) => (
<div key={title} style={{ marginBottom: 36 }}>
<h2 style={{ fontSize: isMobile ? 17 : 20, fontWeight: 700, marginBottom: 12, color: BLACK }}>{title}</h2>
{text.split("\n").map((line, i) =>
line === "" ? <br key={i} /> : <p key={i} style={{ fontSize: 15, color: "#444", lineHeight: 1.7, marginBottom: 4 }}>{line}</p>
)}
</div>
))}
<button onClick={() => navigate("ueber-uns")} style={{ marginTop: 24, background: "none", border: `1px solid ${GREEN}`, color: GREEN, padding: "10px 24px", borderRadius: 6, cursor: "pointer", fontSize: 14 }}>
← Zurück zur Startseite
</button>
</div>
),
}
return (
<div ref={containerRef} style={s.root}>
<Nav />
{pages[page]}
<Footer />
</div>
)
}
addPropertyControls(JaruWebsite, {
logoUrl: { type: ControlType.Image, title: "Logo" },
})

