// EU-strip voor Nederlandse kentekenplaat — blauw vlak, 12 gele sterren in cirkel, "NL" label. function EuPlateStrip() { // 12 sterren in cirkel — typische EU-vlag layout const stars = Array.from({ length: 12 }).map((_, i) => { const angle = (i / 12) * Math.PI * 2 - Math.PI / 2; const cx = 50 + Math.cos(angle) * 30; const cy = 50 + Math.sin(angle) * 30; return { cx, cy }; }); return (
NL
); } function Star({ cx, cy, r, fill }) { // Vijfpuntige ster const points = []; for (let i = 0; i < 10; i++) { const angle = (i / 10) * Math.PI * 2 - Math.PI / 2; const radius = i % 2 === 0 ? r : r * 0.4; points.push(`${cx + Math.cos(angle) * radius},${cy + Math.sin(angle) * radius}`); } return ; } // Keurmerk-badge — uniform formaat, gebruikt eigen logo-bestand of fallback // Drop logo-bestand in: photos/logos/keurmerken/[file] (bv. apk.png, rdw.png, bovag.png) function KeurmerkBadge({ label, sub, Icon, logo }) { const [imgFailed, setImgFailed] = React.useState(false); const showImage = logo && !imgFailed; return (
{ e.currentTarget.style.borderColor = "color-mix(in oklab, var(--accent) 40%, var(--border))"; e.currentTarget.style.transform = "translateY(-2px)"; }} onMouseLeave={(e) => { e.currentTarget.style.borderColor = "var(--border)"; e.currentTarget.style.transform = "translateY(0)"; }} > {showImage ? ( // Witte tile achter het logo zodat zwarte tekst leesbaar blijft op donkere theme
{label} setImgFailed(true)} style={{ maxWidth: "100%", maxHeight: "100%", objectFit: "contain", display: "block" }} />
) : (
{Icon && }
)}
{label}
{sub}
); } // Brand marquee — automerken in oneindige scroll // Drop logo-bestanden in: photos/logos/merken/[slug].svg (bv. bmw.svg, mercedes.svg) // Als bestand niet bestaat, valt component automatisch terug op tekst. const CAR_BRANDS = [ { name: "BMW", slug: "bmw", weight: 800, letterSpacing: "0.18em" }, { name: "Mercedes-Benz", slug: "mercedes", weight: 600, letterSpacing: "0.06em", italic: true }, { name: "AUDI", slug: "audi", weight: 700, letterSpacing: "0.22em" }, { name: "Volkswagen", slug: "volkswagen", weight: 700, letterSpacing: "0.04em" }, { name: "VOLVO", slug: "volvo", weight: 700, letterSpacing: "0.32em" }, { name: "Ford", slug: "ford", weight: 800, letterSpacing: "0.02em", italic: true }, { name: "RENAULT", slug: "renault", weight: 700, letterSpacing: "0.16em" }, { name: "PEUGEOT", slug: "peugeot", weight: 800, letterSpacing: "0.04em" }, { name: "TOYOTA", slug: "toyota", weight: 800, letterSpacing: "0.18em" }, { name: "HYUNDAI", slug: "hyundai", weight: 800, letterSpacing: "0.12em" }, { name: "OPEL", slug: "opel", weight: 800, letterSpacing: "0.16em" }, { name: "CITROËN", slug: "citroen", weight: 700, letterSpacing: "0.18em" }, { name: "TESLA", slug: "tesla", weight: 700, letterSpacing: "0.22em" }, { name: "NISSAN", slug: "nissan", weight: 800, letterSpacing: "0.16em" }, { name: "MAZDA", slug: "mazda", weight: 800, letterSpacing: "0.16em" }, { name: "KIA", slug: "kia", weight: 800, letterSpacing: "0.18em" }, { name: "SEAT", slug: "seat", weight: 800, letterSpacing: "0.18em" }, { name: "ŠKODA", slug: "skoda", weight: 700, letterSpacing: "0.16em" }, { name: "MINI", slug: "mini", weight: 800, letterSpacing: "0.18em" }, { name: "Fiat", slug: "fiat", weight: 800, letterSpacing: "0.04em", italic: true }, ]; function BrandLogo({ brand }) { const [failed, setFailed] = React.useState(false); if (!failed) { return ( {brand.name} setFailed(true)} style={{ height: 52, maxWidth: 130, objectFit: "contain", opacity: 0.9, transition: "opacity .25s ease, transform .25s ease, filter .25s ease", }} onMouseEnter={(e) => { e.currentTarget.style.opacity = "1"; e.currentTarget.style.transform = "scale(1.08)"; e.currentTarget.style.filter = "brightness(1.15)"; }} onMouseLeave={(e) => { e.currentTarget.style.opacity = "0.9"; e.currentTarget.style.transform = "scale(1)"; e.currentTarget.style.filter = "brightness(1)"; }} /> ); } return ( e.currentTarget.style.opacity = "1"} onMouseLeave={(e) => e.currentTarget.style.opacity = "0.7"} > {brand.name} ); } function BrandMarquee() { const items = [...CAR_BRANDS, ...CAR_BRANDS]; return (
{items.map((b, i) => (
))}
); } // Google Reviews — fetcht echte reviews via Places API (cached server-side) function GoogleReviewsSection() { const [data, setData] = React.useState(null); const [loading, setLoading] = React.useState(true); React.useEffect(() => { if (!window.api) { setLoading(false); return; } window.api.get('/reviews/google') .then(d => { setData(d); setLoading(false); }) .catch(() => setLoading(false)); }, []); // Fallback waardes als API niet werkt of nog niet geconfigureerd const rating = data?.rating || 4.9; const total = data?.total || 247; const reviews = data?.reviews || []; const fallback = [ { author: "Sanne D.", rating: 5, text: "Eindelijk een garage die uitlegt wat ze doen. Vaste prijs, opgehaald op tijd. Top." }, { author: "Marco V.", rating: 5, text: "Diagnose op mijn BMW die 3 andere garages niet vonden. Echt vakwerk." }, { author: "Linde K.", rating: 5, text: "APK in 40 minuten, koffie was prima. Reminder gekregen 4 weken voor verloop." }, { author: "Joris H.", rating: 4, text: "Goed werk, eerlijk advies. Wel even wachten op afspraak — populair." }, ]; const display = reviews.length > 0 ? reviews.slice(0, 4) : fallback; const isReal = reviews.length > 0; return (
{rating.toFixed(1)}
{[1,2,3,4,5].map(i => )}
{total} Google reviews
{data?.mapUrl && ( Bekijk alle reviews op Google )}
{display.map((r, i) => (
{Array.from({ length: r.rating || 5 }).map((_, j) => )}

"{(r.text || '').slice(0, 220)}{(r.text || '').length > 220 ? '…' : ''}"

{r.photo && } — {r.author || r.name} {r.timeAgo && · {r.timeAgo}}
))}
); } // Home screen — premium & strak split-hero with kenteken-snelboeking function Home({ onNav, brand }) { const [plate, setPlate] = React.useState(""); const [vehicle, setVehicle] = React.useState(null); // van RDW lookup const [vehicleLoading, setVehicleLoading] = React.useState(false); const [vehicleError, setVehicleError] = React.useState(null); // RDW lookup wanneer kenteken minimaal 6 tekens telt React.useEffect(() => { const clean = plate.replace(/[^A-Z0-9]/gi, ""); if (clean.length < 6) { setVehicle(null); setVehicleError(null); return; } setVehicleLoading(true); setVehicleError(null); const ctrl = new AbortController(); const t = setTimeout(async () => { try { const data = await window.api.kenteken.lookup(plate); setVehicle(data); } catch (e) { setVehicle(null); setVehicleError(e.status === 404 ? "Geen voertuig gevonden" : null); } finally { setVehicleLoading(false); } }, 400); return () => { clearTimeout(t); ctrl.abort(); }; }, [plate]); const services = [ { id: "apk", icon: I.Apk, name: "APK keuring", duration: "45 min", price: 55, hot: true, desc: "RDW-erkend keuringsstation. Inclusief uitgebreid rapport." }, { id: "klein", icon: I.Wrench, name: "Onderhoudsbeurt", duration: "1 uur", price: 89, desc: "Olie + filter + 30-punts inspectie. Klein of groot." }, { id: "diag", icon: I.Diag, name: "Diagnose", duration: "30 min", price: 45, desc: "OEM software én universeel — Bosch, Autel, VCDS, ISTA." }, { id: "airco", icon: I.Snow, name: "Airco service", duration: "30 min", price: 69, desc: "F-gassen gecertificeerd. R134a + R1234yf." }, { id: "banden", icon: I.Tire, name: "Banden & wielen", duration: "30 min", price: 40, desc: "Verkoop, montage en seizoensopslag." }, { id: "rep", icon: I.Cog, name: "Reparaties", duration: "op afspraak", price: null, desc: "Mechanisch en elektronisch — t/m BVE 1." }, ]; return (
{/* HERO */}
{/* ambient grid */}
Autobedrijf · Vijfhuizen

Vakwerk
zonder
verrassingen.

Wij behandelen uw auto zoals een specialist hoort: heldere prijs vooraf, eerlijk advies en gecertificeerde monteurs. APK, onderhoud, complexe diagnose — vakkundig geregeld.

{/* trust bar */}
{[1,2,3,4,5].map(i => )}
4.9 · 247 Google reviews
RDW erkend APK keuringsstation
Sinds 2008
{/* Right: kenteken snelboeking card */}
SNELBOEKING

Plan direct uw APK of service

3 slots vrij deze week

Voer uw kenteken in — we halen merk, model en APK-vervaldatum automatisch op via RDW.

setPlate(e.target.value.toUpperCase())} placeholder="XX-NN-XX" className="plate-input" maxLength={10} aria-label="Kenteken" />
{plate && plate.length >= 6 && (vehicleLoading || vehicle || vehicleError) && (
{vehicleLoading && (
Even kijken…
)} {vehicle && !vehicleLoading && ( <>
{[vehicle.make, vehicle.model].filter(Boolean).join(" ") || "Onbekend voertuig"}
{[ vehicle.year ? `Bouwjaar ${vehicle.year}` : null, vehicle.fuel_type, vehicle.apk_expiry ? `APK verloopt ${new Date(vehicle.apk_expiry).toLocaleDateString('nl-NL')}` : null, ].filter(Boolean).join(" · ")}
)} {vehicleError && !vehicleLoading && (
{vehicleError} — controleer het kenteken
)}
{vehicle && !vehicleLoading && }
)}
Vaste prijs vooraf
Bevestiging binnen 1 min
{/* TRUST WALL — keurmerken (uniform formaat) */}
Erkend & gecertificeerd
{/* DIAGNOSE IN ACTIE — echte foto */}
Diagnose op dealer-niveau

Wij vinden wat anderen missen.

Met professionele uitleesapparatuur (Autel, Bosch KTS, Texa, VCDS) én originele dealer-software lezen we elk merk uit. Mechanisch én elektronisch — tot op modulniveau.

{["Autel", "Bosch KTS", "Texa", "VCDS", "BMW ISTA", "Mercedes XENTRY"].map(t => ( {t} ))}
{/* DIENSTEN GRID */}
{services.map(s => (
onNav("boeking")}> {s.hot && ( Populair )}

{s.name}

{s.desc}


{s.duration} {s.price ? `vanaf €${s.price}` : "op maat"}
))}
{/* HOE WERKT HET — met werkplaats-foto achtergrond */}
{[ { n: "01", t: "Plan online", d: "Voer uw kenteken in, kies een dienst en pak een tijdslot dat u uitkomt. Bevestiging direct per e-mail.", icon: I.Calendar }, { n: "02", t: "Wij doen het werk", d: "Inclusief 30-punts inspectie en eerlijk advies. Bij vondsten bellen we vóór we doorgaan — geen verrassingen.", icon: I.Wrench }, { n: "03", t: "Klaar binnen 24u", d: "Notificatie zodra de auto klaar is. Factuur per e-mail, 12 maanden garantie op het werk.", icon: I.Check }, ].map((s, i) => (
STAP {s.n}

{s.t}

{s.d}

))}
{/* SOCIAL PROOF — Google reviews */} {/* BRAND MARQUEE — automerken */}
{/* PRE-FOOTER CTA */}
Tijd voor uw APK?

Boek vandaag, rij morgen weer.

Gemiddelde wachttijd: 2-4 werkdagen. Spoed? Bel ons direct — vaak passen we u nog dezelfde week in.

); } window.Home = Home;