{/* v2.2.42 — QuickBookingBar entfernt: die Datums-Eingaben wurden
nie an die Buchungsseite weitergegeben (reine Deko), der Button
dupliziert „Jetzt buchen" oben. Naim's Wunsch: so einfach wie
möglich für den Kunden — eine Aktion pro Stelle. */}
{/* v2.2.6 — Hero-Stats Redesign: Bento-Cards mit Icons + Hover-Pulse */}
{/* DEALS — v2.2.19: Cream+Gold-Hero komplett raus. Stattdessen pro
Aktion eine grosse Magazin-Karte (Foto-2-Layout aus DealsPage):
Bild + „Aktion 0X" Eyebrow + Titel + Subtitle + Countdown +
„Verfügbare Fahrzeuge"-Liste + „Jetzt sichern"-CTA. Klick auf
eine Auto-Reihe in der Karte → Buchungs-Flow mit Aktion + Auto
vorbelegt. Vehicle-Karten-Grid darunter bleibt (Naim's Wunsch). */}
{deals.length > 0 && (
{/* v2.2.19 — Magazin-Karten (eine pro Aktion) */}
{deals.map((deal, idx) => {
// v2.2.22 — Magazin-Karte filtert auf homeVehicles, damit
// ausgeblendete Autos nicht als Avatar in der Aktion auftauchen.
const dvs = homeVehicles.filter(v => deal.vehicles.includes(v.id));
const heroVeh = dvs[0];
const heroImage = deal.image || (heroVeh && heroVeh.image);
const isWeekendDeal = !!(deal.isWeekend && deal.weekend);
// v2.2.41 — Aktion-Slot prominenter zeigen: für Weekend-Aktionen
// die nächste Slot-Periode (Pickup → Return) ausrechnen und im
// Karten-Header neben dem Countdown anzeigen, damit User vor
// dem Klick wissen, welcher Zeitraum gelockt wird.
let slotPickup = null;
let slotReturn = null;
if (isWeekendDeal) {
const w = deal.weekend;
const now = new Date();
slotPickup = acnNextDateForDow(now, Number(w.pickupDay), Number(w.pickupHour) || 17);
slotReturn = acnNextDateForDow(slotPickup, Number(w.returnDay), Number(w.returnHour) || 9);
if (slotReturn.getTime() <= slotPickup.getTime()) {
slotReturn.setDate(slotReturn.getDate() + 7);
}
}
return (
{/* v2.2.41 — Slot-Anzeige für Weekend-Aktionen.
Macht für den User transparent, welcher Zeitraum
beim Klick auf ein Fahrzeug fixiert wird. */}
{isWeekendDeal && slotPickup && slotReturn && (
Nächster Aktion-Slot
Abholung{acnFmtSlot(slotPickup)}
Rückgabe{acnFmtSlot(slotReturn)}
Slot ist fixiert. Nur in dieser Periode buchbar.
)}
{dvs.length > 0 && (
Verfügbare Fahrzeuge
{dvs.map(v => {
// v2.2.45 — Streichpreis-Berechnung:
// - Bei %-Rabatt-Aktionen: alter Tagespreis → neuer Tagespreis (1:1 Vergleich)
// - Bei Weekend-Pauschale: Tagespreis × Anzahl Tage des Slots
// ergibt den „normal gerechneten" Vergleichswert. Streichpreis
// wird nur gerendert, wenn das Pauschal-Angebot tatsächlich
// günstiger ist. Sonst gar kein Vergleich (Pauschale = Pauschale).
let oldP, newP, perLabel;
if (isWeekendDeal) {
const vp = (deal.weekend.vehiclePrices && typeof deal.weekend.vehiclePrices === "object")
? deal.weekend.vehiclePrices : {};
const wPrice = vp[v.id] && Number(vp[v.id]) > 0
? Number(vp[v.id])
: (deal.weekend.price > 0 ? Number(deal.weekend.price) : 0);
newP = wPrice > 0
? wPrice
: Math.round(v.pricePerDay * (1 - (Number(deal.discount) || 0) / 100));
perLabel = "Pauschale";
// Slot-Dauer in Tagen (Aufrunden — angefangener Tag zählt voll).
let slotDays = 0;
if (slotPickup && slotReturn) {
const ms = slotReturn.getTime() - slotPickup.getTime();
slotDays = Math.max(1, Math.ceil(ms / (1000 * 60 * 60 * 24)));
}
oldP = slotDays > 0 ? v.pricePerDay * slotDays : 0;
} else {
oldP = v.pricePerDay;
newP = Math.round(oldP * (1 - (Number(deal.discount) || 0) / 100));
perLabel = "/Tag";
}
// v2.2.19 — Klick → Buchung mit Aktion + Auto vorbelegt
const handleClick = () => onDealBook && onDealBook(deal, v);
return (
{
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleClick();
}
}}
title={`${v.brand} ${v.model} mit dieser Aktion buchen`}
>
{v.brand}
{v.model}
{/* v2.2.45 — Streichpreis nur dann anzeigen, wenn
die Pauschale (oder %-Aktion) tatsächlich günstiger
ist als der „normal gerechnete" Vergleichspreis. */}
{oldP > newP && (
CHF {chf(oldP)}
)}
CHF {chf(newP)}{perLabel}
);
})}
)}
{/* v2.2.20 — „Jetzt sichern"-Button entfernt: jede Auto-Reihe
ist klickbar (data-clickable) und führt direkt zur Buchung
mit Aktion + Auto vorbelegt — der CTA war redundant. */}
{dealVehicles.slice(0, 6).map((v) => {
const matchingDeal = deals.find(d =>
Array.isArray(d.vehicles) && d.vehicles.includes(v.id)
) || null;
return (
{
// v2.2.19 — Wenn das Auto in einer Aktion ist:
// Buchung mit Aktion + Auto vorbelegt.
// v2.2.23 — Bugfix: Klick auf eine Tier-Pille (3h / 6h /
// 1 Tag) leitete bei Weekend-Aktion-Fahrzeugen automatisch
// in den Weekend-Booking-Flow um (Datum gelockt aufs
// Wochenende, Tier-Wahl ging verloren). Naim's Wunsch:
// Weekend-Modus NUR via expliziten Weekend-Button oder
// Weekend-Datum. Wenn der User explizit einen Kurz-Tier
// (3h / 6h / 24h) gewählt hat, respektieren wir das und
// gehen den normalen Buchungs-Flow — die Aktion/Weekend
// wird nicht aufgezwungen.
const isWeekendDeal = !!(matchingDeal && matchingDeal.isWeekend);
const pickedShortTier = tier && tier > 0 && tier < 168;
if (matchingDeal && onDealBook && !(isWeekendDeal && pickedShortTier)) {
onDealBook(matchingDeal, veh);
} else {
onBook(veh, tier);
}
}}
onOpen={(veh) => onOpen && onOpen(veh, matchingDeal)}
contact={contact}
infoCardSettings={boot && boot.infoCard}
/>
);
})}
)}
)}
{/* v2.2.41 — ALLE FAHRZEUGE — Flotte-Page wurde entfernt, deshalb listet
die Startseite jetzt alle Fahrzeuge in einem dedizierten Grid. Diese
Sektion ersetzt die alte Flotte-Seite (Naim-Wunsch v2.2.41).
ID „alle-fahrzeuge" ist Sprung-Ziel für die „Flotte ansehen"- und
„Fahrzeug wählen"-Buttons im Hero und Why-Block.
v2.2.42 — Filter: Autos in einer Discount-Aktion erscheinen NICHT
mehr im Grid (sind oben in der Aktion-Sektion sichtbar — Duplikat
vermeiden). Autos in einer Package-Aktion (z.B. Weekend-Slot)
bleiben im Grid, kriegen aber einen klickbaren Hinweis-Streifen
unter der Karte, der zur Aktion-Sektion scrollt. So sind sie für
Mo-Do (außerhalb des Aktion-Slots) zum normalen Tier-Preis buchbar. */}
{(() => {
const allVehiclesFiltered = vehicles.filter(v => {
const d = vehicleDealMap[v.id];
if (d && d.dealType === 'discount') return false;
return true;
});
if (!(allVehiclesFiltered.length > 0 && window.VehicleCard)) return null;
return (
Unsere Flotte
Alle Fahrzeuge auf einen Blick
{allVehiclesFiltered.length} {allVehiclesFiltered.length === 1 ? "Fahrzeug" : "Fahrzeuge"} sofort buchbar — Sportwagen,
Luxuslimousinen und SUVs aus der Schweiz. Wählen Sie Ihr
Wunsch-Modell und buchen Sie in wenigen Klicks.
{allVehiclesFiltered.map((v) => {
const matchingDeal = vehicleDealMap[v.id] || null;
// v2.2.42 — Package-Hinweis-Info: nur wenn Auto in
// einer Package-Aktion ist (Weekend-Slot etc.).
let pkgHint = null;
if (matchingDeal && matchingDeal.dealType === 'package' && matchingDeal.weekend) {
const w = matchingDeal.weekend;
const vp = (w.vehiclePrices && typeof w.vehiclePrices === 'object') ? w.vehiclePrices : {};
const price = vp[v.id] && Number(vp[v.id]) > 0
? Number(vp[v.id])
: (Number(w.price) > 0 ? Number(w.price) : 0);
const now = new Date();
const pickup = acnNextDateForDow(now, Number(w.pickupDay), Number(w.pickupHour) || 17);
const ret = acnNextDateForDow(pickup, Number(w.returnDay), Number(w.returnHour) || 9);
if (ret.getTime() <= pickup.getTime()) ret.setDate(ret.getDate() + 7);
pkgHint = { price, pickup, ret };
}
return (
{
// v2.2.44 — Untere Flotte: Default = normaler Tier-Buchungs-Flow.
// Package-Deals (Weekend-Slot) werden hier NICHT mehr automatisch
// erzwungen. Wer den Deal will, klickt oben auf die Aktion-Karte
// (oder unten auf den Hint-Streifen). Discount-Aktionen werden
// ohnehin ausgefiltert (kommen hier gar nicht vor — siehe
// allVehiclesFiltered oben).
onBook(veh, tier);
}}
onOpen={(veh) => onOpen && onOpen(veh, matchingDeal ? Object.assign({}, matchingDeal, { __hint: true }) : null)}
contact={contact}
infoCardSettings={boot && boot.infoCard}
/>
{pkgHint && (
)}
Wählen Sie Fahrzeug und Mietdauer — der Preis erscheint sofort, transparent und inklusive MwSt.
Smart-Rabatte (Wochenende, längere Miete) werden automatisch angewendet.
{/* v2.2.5 — Why-Sektion: Bento-Grid mit großen Icons & Hover-Animationen */}
Warum ACN-Cars
Premium-Mieten, spürbar besser.
Vier Versprechen, die wir täglich einhalten — von Aufbereitung
über Preise bis 24/7-Service.
{/* v2.2.20 — Why-Cards lesen Title + Text aus boot.whyCards (Backend-editierbar).
Icons + Reihenfolge + Karten-Style bleiben hardcoded. */}
{whyCard(0).enabled && (
{whyCard(0).title}
{whyCard(0).text}
40+handselektierte Modelle
)}
{whyCard(1).enabled && (
{whyCard(1).title}
{whyCard(1).text}
)}
{whyCard(2).enabled && (
{whyCard(2).title}
{whyCard(2).text}
)}
{whyCard(3).enabled && (
{whyCard(3).title}
{whyCard(3).text}
)}
★★★★★
{ratingValue}
aus {ratingCount} Bewertungen
Hunderte zufriedene Kunden — vom Wochenend-Mieter bis zum
Stamm-Konzern-Account.
Bereit?
Jetzt unverbindlich anfragen
{/* v2.2.21 — Antwort-Versprechen realistischer („30 Min" war zu optimistisch). */}
Antwort innerhalb von 2 Stunden — werktags meist schneller.
{/* v2.2.41 — Flotte-Page komplett entfernt. Button scrollt jetzt
zur Fahrzeug-Grid-Sektion auf der Startseite (id="alle-fahrzeuge"),
statt auf eine separate Flotte-Seite zu navigieren. */}
);
}
// v2.2.6 — Hero-Stats: Bento-Card-Reihe mit Icons + Hover-Glow + Animation der Zahl
// v2.2.14 — Rating + Anzahl kommen jetzt aus dem Backend (boot.rating /
// boot.ratingCount); werden hier als Default für die letzte Stat
// verwendet (falls boot.heroStats nicht überschrieben wurde).
function HeroStats({ stats, rating, ratingCount }) {
const r = (rating != null && String(rating).trim() !== "") ? String(rating).trim() : "4.9";
const rc = (ratingCount != null && String(ratingCount).trim() !== "") ? String(ratingCount).trim() : "327";
const defaults = [
{ value: "5", label: "Standorte in der Schweiz" },
{ value: "40+", label: "Premium-Fahrzeuge" },
{ value: "24/7", label: "Lieferung & Abholung" },
{ value: r + " ★", label: "Google-Bewertung (" + rc + ")" },
];
const list = (stats && stats.length > 0 ? stats : defaults)
.filter(s => (s.value && String(s.value).trim() !== "") || (s.label && String(s.label).trim() !== ""));
// Icon-Heuristik: anhand des Labels oder Values das passende Icon wählen
const pickIcon = (s) => {
const t = ((s.label || "") + " " + (s.value || "")).toLowerCase();
if (t.includes("★") || t.includes("bewert") || t.includes("rating") || t.includes("google")) return "star";
if (t.includes("standort") || t.includes("location") || t.includes("filiale") || t.includes("ort")) return "map";
if (t.includes("24/7") || t.includes("liefer") || t.includes("zeit") || t.includes("std")) return "clock";
if (t.includes("fahrzeug") || t.includes("auto") || t.includes("flotte") || t.includes("car")) return "car";
if (t.includes("kund") || t.includes("client")) return "users";
if (t.includes("jahr") || t.includes("erfahr")) return "shield";
return "sparkle";
};
return (
{list.map((s, i) => {
const ic = pickIcon(s);
return (