Files
smb-online/templates/landing/demo-pizzeria/index.html
T
mivanchenko cde0cb0fa8 Add pizzeria demo w/ online ordering; fix gcal dots; reword chooser
- New demo-pizzeria (Bella Napoli): interactive menu -> cart (qty +/-,
  live total) -> order form (pickup/delivery, address, time, notes) that
  POSTs the order summary to the lead-intake webhook (client_id
  DEMO-PIZZA, source order-pizzeria). Verified end-to-end (~5s).
- Fix barbershop "Google Kalender" header: the 3 dots were faked via
  box-shadow with no layout width, so text overlapped them. Reserve space.
- Chooser: client-friendly wording (no "self-hosted"/Cal.com jargon, no
  "~0 EUR"), reframed as example businesses, 3-up responsive layout.
- TODO.md: decouple tier from business type; demos as stack-agnostic
  starters; hosting (Google vs self-host) decided per client. Plus notes
  on n8n auth hardening and a real Orders flow/tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 08:32:01 +02:00

498 lines
30 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bella Napoli — Pizzeria | Online bestellen</title>
<meta name="description" content="Echte neapolitanische Pizza aus dem Holzofen. Online bestellen — Abholung oder Lieferung, ohne Telefonwarteschleife." />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<style>
:root {
--bg: #fbf6f0;
--surface: #ffffff;
--ink: #2a1812;
--muted: #8a6f63;
--red: #d6453a;
--red-2: #b5342b;
--red-soft: #fdece9;
--green: #3f7d4f;
--sand: #f5ece1;
--line: #efe1d6;
--radius: 16px;
--shadow: 0 16px 44px rgba(60,24,16,.12);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body { font-family: 'Inter', system-ui, -apple-system, Segoe UI, Roboto, sans-serif; background: var(--bg); color: var(--ink); line-height: 1.65; -webkit-font-smoothing: antialiased; }
h1, h2, h3 { font-family: 'Fraunces', Georgia, serif; line-height: 1.12; font-weight: 600; }
a { color: inherit; text-decoration: none; }
.wrap { width: min(1120px, 92vw); margin-inline: auto; }
section { padding: 84px 0; }
.eyebrow { color: var(--red); font-weight: 600; letter-spacing: 2px; text-transform: uppercase; font-size: .72rem; }
/* demo ribbon */
.demo-bar { background: #3a140f; color: #ffcfc6; font-size: .76rem; letter-spacing: 1px; text-align: center; padding: 7px 12px; }
/* nav */
header.nav { position: sticky; top: 0; z-index: 50; background: rgba(251,246,240,.88); backdrop-filter: blur(10px); border-bottom: 1px solid var(--line); }
.nav-inner { display: flex; align-items: center; justify-content: space-between; height: 72px; gap: 16px; }
.brand { display: flex; align-items: center; gap: 9px; font-family: 'Fraunces', serif; font-size: 1.4rem; font-weight: 600; }
.brand .logo { width: 32px; height: 32px; border-radius: 9px; background: linear-gradient(135deg, var(--red), var(--red-2)); display: grid; place-items: center; color: #fff; font-size: 1.05rem; }
nav ul { display: flex; gap: 26px; list-style: none; align-items: center; }
nav a { color: var(--muted); font-size: .92rem; font-weight: 500; transition: color .2s; }
nav a:hover { color: var(--ink); }
.btn { display: inline-block; background: var(--red); color: #fff; font-weight: 600; padding: 12px 22px; border-radius: 999px; border: 0; cursor: pointer; font-size: .95rem; transition: transform .15s, background .2s; }
.btn:hover { transform: translateY(-2px); background: var(--red-2); }
.btn.ghost { background: #fff; color: var(--red-2); border: 1px solid var(--line); }
.cart-pill { display: inline-flex; align-items: center; gap: 7px; background: var(--red-soft); color: var(--red-2); border: 1px solid #f4cfc8; font-weight: 700; padding: 9px 15px; border-radius: 999px; font-size: .9rem; cursor: pointer; }
.cart-pill .badge { background: var(--red); color: #fff; border-radius: 999px; min-width: 20px; height: 20px; display: grid; place-items: center; font-size: .72rem; padding: 0 5px; }
.nav-toggle { display: none; background: none; border: 0; color: var(--ink); font-size: 1.6rem; cursor: pointer; }
/* hero */
.hero { background: linear-gradient(180deg, #fff 0%, var(--bg) 100%); border-bottom: 1px solid var(--line); }
.hero-grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: 50px; align-items: center; padding: 74px 0 82px; }
.hero h1 { font-size: clamp(2.5rem, 5.4vw, 3.9rem); margin: 16px 0 0; }
.hero h1 em { font-style: italic; color: var(--red-2); }
.hero p { color: var(--muted); font-size: 1.12rem; margin: 18px 0 28px; max-width: 480px; }
.hero-cta { display: flex; gap: 14px; flex-wrap: wrap; }
.trust-row { margin-top: 30px; display: flex; gap: 22px; flex-wrap: wrap; align-items: center; color: var(--muted); font-size: .88rem; }
.pill { background: var(--red-soft); color: var(--red-2); padding: 6px 13px; border-radius: 999px; font-weight: 600; font-size: .82rem; }
.hero-card { background: var(--surface); border: 1px solid var(--line); border-radius: 22px; box-shadow: var(--shadow); padding: 26px; }
.hero-card .pizza { font-size: 4.6rem; text-align: center; line-height: 1; margin: 4px 0 10px; }
.hero-card h3 { font-family: 'Fraunces', serif; font-size: 1.3rem; text-align: center; }
.hero-card .row { display: flex; justify-content: space-between; align-items: center; margin-top: 14px; font-size: .92rem; color: var(--muted); border-top: 1px solid var(--line); padding-top: 12px; }
.hero-card .row b { color: var(--ink); }
/* menu */
.sec-head { max-width: 640px; margin-bottom: 40px; }
.sec-head h2 { font-size: clamp(2rem, 4.6vw, 2.9rem); margin-top: 8px; }
.sec-head p { color: var(--muted); margin-top: 10px; }
.menu-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; }
.item { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 20px 22px; display: flex; flex-direction: column; transition: transform .18s, box-shadow .2s; }
.item:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.item .top { display: flex; align-items: flex-start; gap: 12px; }
.item .emoji { font-size: 1.8rem; line-height: 1; }
.item h3 { font-size: 1.18rem; margin-bottom: 2px; }
.item .desc { color: var(--muted); font-size: .86rem; }
.item .foot { display: flex; align-items: center; justify-content: space-between; margin-top: 16px; }
.item .price { font-weight: 700; font-size: 1.1rem; }
.add { background: var(--red-soft); color: var(--red-2); border: 1px solid #f4cfc8; font-weight: 700; padding: 8px 15px; border-radius: 999px; cursor: pointer; font-size: .88rem; transition: .15s; }
.add:hover { background: var(--red); color: #fff; border-color: var(--red); }
.tagv { display: inline-block; margin-top: 8px; font-size: .7rem; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; color: var(--green); }
/* order */
.order { background: var(--sand); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.order-grid { display: grid; grid-template-columns: 1fr .95fr; gap: 34px; align-items: start; }
.cart { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; position: sticky; top: 90px; }
.cart-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--line); font-weight: 700; }
.cart-items { padding: 8px 20px; max-height: 300px; overflow: auto; }
.cart-empty { color: var(--muted); font-size: .9rem; padding: 22px 0; text-align: center; }
.ci { display: flex; align-items: center; gap: 10px; padding: 12px 0; border-bottom: 1px solid var(--line); }
.ci:last-child { border-bottom: 0; }
.ci .name { flex: 1; font-size: .92rem; }
.ci .name small { display: block; color: var(--muted); font-size: .78rem; }
.qty { display: flex; align-items: center; gap: 8px; }
.qty button { width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--line); background: #fff; cursor: pointer; font-size: 1rem; line-height: 1; color: var(--ink); }
.qty button:hover { border-color: var(--red); color: var(--red); }
.qty span { min-width: 18px; text-align: center; font-weight: 700; font-size: .9rem; }
.ci .lp { width: 64px; text-align: right; font-weight: 600; font-size: .9rem; }
.cart-total { display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-top: 1px solid var(--line); font-size: 1.15rem; font-weight: 700; }
.cart-foot { padding: 0 20px 16px; color: var(--muted); font-size: .74rem; }
form.order-form { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 26px; box-shadow: var(--shadow); }
.field { margin-bottom: 15px; }
.field label { display: block; font-size: .85rem; color: var(--muted); margin-bottom: 6px; }
.field input, .field textarea, .field select { width: 100%; background: #fdfaf7; border: 1px solid var(--line); border-radius: 10px; color: var(--ink); padding: 12px 14px; font: inherit; font-size: .95rem; }
.field input:focus, .field textarea:focus, .field select:focus { outline: 2px solid var(--red); border-color: var(--red); }
.seg { display: flex; gap: 10px; }
.seg label { flex: 1; cursor: pointer; border: 1px solid var(--line); border-radius: 10px; padding: 11px; text-align: center; background: #fdfaf7; font-size: .9rem; font-weight: 600; color: var(--ink); margin: 0; transition: .15s; }
.seg input { display: none; }
.seg label:has(input:checked) { border-color: var(--red); background: var(--red-soft); color: var(--red-2); }
.two { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.form-note { font-size: .78rem; color: var(--muted); margin-top: 10px; }
.toast { display: none; margin-top: 14px; padding: 12px 14px; border-radius: 10px; background: var(--red-soft); border: 1px solid var(--red); color: var(--red-2); font-size: .9rem; }
.toast.err { background: #fdecec; border-color: #e0533a; color: #9b2226; }
/* footer */
footer { padding: 54px 0 40px; border-top: 1px solid var(--line); color: var(--muted); font-size: .9rem; background: var(--surface); }
.foot-grid { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 24px; }
@media (max-width: 820px) {
nav ul { display: none; }
.nav-toggle { display: block; }
nav.open ul { display: flex; position: absolute; inset: 72px 0 auto 0; flex-direction: column; background: var(--surface); padding: 18px 6vw; gap: 18px; border-bottom: 1px solid var(--line); }
.hero-grid, .order-grid { grid-template-columns: 1fr; }
.cart { position: static; }
section { padding: 60px 0; }
}
</style>
</head>
<body>
<div class="demo-bar">Demo · Pizzeria mit Online-Bestellung — Beispielseite für Gäste <button class="cb-open" type="button" onclick="cbOpen()">📞 Rückruf anfordern</button></div>
<header class="nav">
<div class="wrap nav-inner">
<a href="#top" class="brand"><span class="logo">🍕</span> Bella Napoli</a>
<nav id="nav">
<ul>
<li><a href="#speisekarte">Speisekarte</a></li>
<li><a href="#bestellen">Bestellen</a></li>
<li><a href="#kontakt">Kontakt</a></li>
<li><a href="#bestellen" class="cart-pill" onclick="scrollCart()">🛒 Warenkorb <span class="badge" id="navCount">0</span></a></li>
</ul>
</nav>
<button class="nav-toggle" aria-label="Menü" onclick="document.getElementById('nav').classList.toggle('open')"></button>
</div>
</header>
<main id="top">
<!-- HERO -->
<section class="hero">
<div class="wrap hero-grid">
<div>
<span class="eyebrow">Pizzeria · Holzofen · Nürnberg</span>
<h1>Echte Pizza.<br><em>Online bestellt.</em></h1>
<p>Neapolitanischer Teig, 48 Stunden gereift, aus dem Holzofen. Stell dir deine Bestellung zusammen — Abholung oder Lieferung, ganz ohne Telefon-Warteschleife.</p>
<div class="hero-cta">
<a href="#speisekarte" class="btn">Jetzt bestellen</a>
<a href="#kontakt" class="btn ghost">Öffnungszeiten</a>
</div>
<div class="trust-row">
<span class="pill">🔥 Aus dem Holzofen</span>
<span>🚴 Lieferung in 3040 Min</span>
<span>⭐ 4,8 / 5</span>
</div>
</div>
<aside class="hero-card">
<div class="pizza">🍕</div>
<h3>Pizza der Woche</h3>
<p style="text-align:center;color:var(--muted);font-size:.9rem;margin-top:6px;">Diavola — scharfe Salami, Chili, frischer Basilikum</p>
<div class="row"><span>Heute frisch gebacken</span><b>10,90 €</b></div>
<button class="btn" style="width:100%;margin-top:16px;" onclick="addToCart('diavola'); scrollCart();">+ In den Warenkorb</button>
</aside>
</div>
</section>
<!-- MENU -->
<section id="speisekarte">
<div class="wrap">
<div class="sec-head">
<span class="eyebrow">Speisekarte</span>
<h2>Unsere Pizzen</h2>
<p>Alle Pizzen 30 cm, auf hausgemachtem Sauerteig. Tippe auf „Hinzufügen“ — dein Warenkorb füllt sich unten.</p>
</div>
<div class="menu-grid" id="menuGrid"><!-- items injected by JS --></div>
</div>
</section>
<!-- ORDER -->
<section class="order" id="bestellen">
<div class="wrap">
<div class="sec-head">
<span class="eyebrow">Bestellen</span>
<h2>Deine Bestellung</h2>
<p>Prüf deinen Warenkorb, wähl Abholung oder Lieferung — wir bestätigen direkt.</p>
</div>
<div class="order-grid">
<!-- Demo: dieses Formular postet die Bestellung an einen n8n-Webhook -> CRM (Leads/Bestellungen) + Telegram. -->
<form class="order-form" onsubmit="return submitOrder(event, this)">
<div class="field two">
<div><label for="o-name">Name</label><input id="o-name" name="name" type="text" placeholder="Luca Rossi" required /></div>
<div><label for="o-phone">Telefon</label><input id="o-phone" name="phone" type="tel" inputmode="tel" placeholder="+49 …" required /></div>
</div>
<div class="field">
<label>Abholung oder Lieferung?</label>
<div class="seg">
<label><input type="radio" name="mode" value="Abholung" checked onchange="toggleAddr()" /> 🏠 Abholung</label>
<label><input type="radio" name="mode" value="Lieferung" onchange="toggleAddr()" /> 🚴 Lieferung</label>
</div>
</div>
<div class="field" id="addrField" style="display:none;">
<label for="o-addr">Lieferadresse</label>
<input id="o-addr" name="address" type="text" placeholder="Straße, Hausnr., PLZ Ort" />
</div>
<div class="field">
<label for="o-time">Wunschzeit <span style="opacity:.6">(optional)</span></label>
<input id="o-time" name="time" type="text" placeholder="z. B. so schnell wie möglich / 19:30" />
</div>
<div class="field">
<label for="o-note">Anmerkung <span style="opacity:.6">(optional)</span></label>
<textarea id="o-note" name="note" rows="2" placeholder="Klingel defekt, bitte anrufen · ohne Zwiebeln …"></textarea>
</div>
<button class="btn" type="submit" style="width:100%;">Bestellung abschicken</button>
<p class="form-note">Demo-Vorschau. In der Live-Version läuft die Bestellung direkt ins Bestell-Management der Pizzeria + sofortige Benachrichtigung.</p>
<div class="toast" id="orderToast"></div>
</form>
<aside class="cart" id="cartBox">
<div class="cart-head"><span>🛒 Warenkorb</span><span id="cartCount">0 Artikel</span></div>
<div class="cart-items" id="cartItems">
<div class="cart-empty" id="cartEmpty">Noch nichts ausgewählt — wähl oben deine Pizza.</div>
</div>
<div class="cart-total"><span>Summe</span><span id="cartTotal">0,00 €</span></div>
<div class="cart-foot">Lieferung ab 15 € · Mindestbestellwert nur bei Lieferung · Preise inkl. MwSt.</div>
</aside>
</div>
</div>
</section>
<!-- CONTACT -->
<section id="kontakt">
<div class="wrap">
<div class="sec-head">
<span class="eyebrow">Kontakt &amp; Öffnungszeiten</span>
<h2>Bella Napoli</h2>
<p>Frisch gebacken, DiSo. Montags Ruhetag.</p>
</div>
<div class="foot-grid" style="color:var(--muted)">
<div><p><b style="color:var(--ink)">Adresse</b></p><p>Weinmarkt 7 · 90403 Nürnberg</p></div>
<div><p><b style="color:var(--ink)">Öffnungszeiten</b></p><p>DiSo 11:3022:00 · Mo Ruhetag</p></div>
<div><p><b style="color:var(--ink)">Telefon</b></p><p>0911 000000 · ciao@bellanapoli-nbg.de</p></div>
</div>
</div>
</section>
</main>
<footer>
<div class="wrap foot-grid">
<div>
<div class="brand" style="margin-bottom:8px;"><span class="logo">🍕</span> Bella Napoli</div>
<p>Weinmarkt 7 · 90403 Nürnberg</p>
</div>
<div><p><b style="color:var(--ink)">Bestellen</b></p><p>Online · Abholung &amp; Lieferung</p></div>
<div><p><b style="color:var(--ink)">Folge uns</b></p><p>Instagram · Facebook</p></div>
</div>
<div class="wrap" style="margin-top:30px;opacity:.7;font-size:.8rem;">© 2026 Bella Napoli · Demo-Seite · Impressum · Datenschutz</div>
</footer>
<script>
// ===== Menü & Warenkorb =====
const MENU = [
{ id: 'margherita', emoji: '🍕', name: 'Margherita', desc: 'Tomate, Mozzarella, Basilikum', price: 8.50, veg: true },
{ id: 'salami', emoji: '🍕', name: 'Salami', desc: 'Tomate, Mozzarella, italienische Salami', price: 9.50 },
{ id: 'funghi', emoji: '🍄', name: 'Prosciutto e Funghi', desc: 'Schinken, frische Champignons, Mozzarella', price: 10.50 },
{ id: 'diavola', emoji: '🌶️', name: 'Diavola', desc: 'Scharfe Salami, Chili, Basilikum', price: 10.90 },
{ id: 'formaggi', emoji: '🧀', name: 'Quattro Formaggi', desc: 'Vier Käse: Mozzarella, Gorgonzola, Parmesan, Provolone', price: 11.50, veg: true },
{ id: 'vegetariale',emoji: '🥦', name: 'Vegetariale', desc: 'Grillgemüse, Rucola, Cherrytomaten', price: 10.50, veg: true },
{ id: 'tonno', emoji: '🐟', name: 'Tonno e Cipolla', desc: 'Thunfisch, rote Zwiebeln, Kapern', price: 11.00 },
{ id: 'calzone', emoji: '🥟', name: 'Calzone', desc: 'Gefüllte Teigtasche: Schinken, Pilze, Ricotta', price: 11.50 },
];
const byId = Object.fromEntries(MENU.map(m => [m.id, m]));
const cart = {}; // id -> qty
function eur(n) { return n.toFixed(2).replace('.', ',') + ' €'; }
function renderMenu() {
document.getElementById('menuGrid').innerHTML = MENU.map(m => `
<article class="item">
<div class="top">
<div class="emoji">${m.emoji}</div>
<div>
<h3>${m.name}</h3>
<div class="desc">${m.desc}</div>
${m.veg ? '<span class="tagv">🌱 Vegetarisch</span>' : ''}
</div>
</div>
<div class="foot">
<span class="price">${eur(m.price)}</span>
<button class="add" type="button" onclick="addToCart('${m.id}')">+ Hinzufügen</button>
</div>
</article>`).join('');
}
function addToCart(id) { cart[id] = (cart[id] || 0) + 1; renderCart(); }
function changeQty(id, d) {
cart[id] = (cart[id] || 0) + d;
if (cart[id] <= 0) delete cart[id];
renderCart();
}
function cartCount() { return Object.values(cart).reduce((a, b) => a + b, 0); }
function cartTotal() { return Object.entries(cart).reduce((s, [id, q]) => s + byId[id].price * q, 0); }
function renderCart() {
const n = cartCount();
document.getElementById('navCount').textContent = n;
document.getElementById('cartCount').textContent = n + (n === 1 ? ' Artikel' : ' Artikel');
document.getElementById('cartTotal').textContent = eur(cartTotal());
const box = document.getElementById('cartItems');
const ids = Object.keys(cart);
if (!ids.length) { box.innerHTML = '<div class="cart-empty">Noch nichts ausgewählt — wähl oben deine Pizza.</div>'; return; }
box.innerHTML = ids.map(id => {
const m = byId[id], q = cart[id];
return `<div class="ci">
<div class="name">${m.emoji} ${m.name}<small>${eur(m.price)} / Stück</small></div>
<div class="qty">
<button type="button" onclick="changeQty('${id}',-1)" aria-label="weniger"></button>
<span>${q}</span>
<button type="button" onclick="changeQty('${id}',1)" aria-label="mehr">+</button>
</div>
<div class="lp">${eur(m.price * q)}</div>
</div>`;
}).join('');
}
function scrollCart() { document.getElementById('bestellen').scrollIntoView({ behavior: 'smooth' }); }
function toggleAddr() {
const del = document.querySelector('input[name=mode]:checked').value === 'Lieferung';
const f = document.getElementById('addrField');
f.style.display = del ? 'block' : 'none';
document.getElementById('o-addr').required = del;
}
// ===== Bestellung absenden -> n8n Webhook (Leads/Bestellungen) =====
const ORDER_WEBHOOK = 'https://n8n.mivanchenko.de/webhook/lead-intake';
async function submitOrder(e, form) {
e.preventDefault();
const toast = document.getElementById('orderToast');
if (!cartCount()) {
toast.className = 'toast err'; toast.textContent = '⚠ Dein Warenkorb ist noch leer.'; toast.style.display = 'block';
return false;
}
const d = Object.fromEntries(new FormData(form).entries());
const lines = Object.entries(cart).map(([id, q]) => `${q}× ${byId[id].name} (${eur(byId[id].price * q)})`).join(', ');
const summary = `Bestellung (${d.mode}): ${lines}. Summe: ${eur(cartTotal())}.`
+ (d.address ? ` Lieferadresse: ${d.address}.` : '')
+ (d.time ? ` Wunschzeit: ${d.time}.` : '')
+ (d.note ? ` Anmerkung: ${d.note}.` : '');
const payload = {
client_id: 'DEMO-PIZZA', source: 'order-pizzeria',
name: d.name, phone: d.phone,
service_interest: 'Online-Bestellung · ' + d.mode,
message: summary
};
const btn = form.querySelector('button[type=submit]');
const old = btn.textContent; btn.disabled = true; btn.textContent = 'Wird gesendet…';
toast.style.display = 'none';
try {
const r = await fetch(ORDER_WEBHOOK, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
if (!r.ok) throw new Error(r.status);
toast.className = 'toast'; toast.textContent = '✓ Danke, ' + d.name + '! Deine Bestellung über ' + eur(cartTotal()) + ' ist eingegangen — wir bestätigen gleich.';
toast.style.display = 'block';
for (const k in cart) delete cart[k]; renderCart(); form.reset(); toggleAddr();
} catch (err) {
toast.className = 'toast err'; toast.textContent = '⚠ Senden fehlgeschlagen — bitte erneut versuchen oder anrufen.';
toast.style.display = 'block';
} finally { btn.disabled = false; btn.textContent = old; }
return false;
}
renderMenu();
renderCart();
</script>
<script>window.CB_CTX = { client_id: "DEMO-PIZZA", source: "callback-pizzeria" };</script>
<!-- ===== Rückruf-Widget (lead capture popup) ===== -->
<style>
.cb-fab { position: fixed; right: 18px; bottom: 18px; z-index: 900; background: #d6453a; color: #fff; border: 0; border-radius: 999px; padding: 13px 20px; font: 600 .95rem system-ui, -apple-system, Segoe UI, Roboto, sans-serif; box-shadow: 0 10px 26px rgba(0,0,0,.28); cursor: pointer; transition: transform .15s, background .2s; }
.cb-fab:hover { transform: translateY(-2px); background: #b5342b; }
.cb-overlay { position: fixed; inset: 0; z-index: 1000; background: rgba(30,12,8,.55); backdrop-filter: blur(3px); display: none; align-items: center; justify-content: center; padding: 18px; }
.cb-overlay.open { display: flex; }
.cb-modal { position: relative; width: min(440px, 100%); background: #fff; color: #2a1812; border-radius: 16px; box-shadow: 0 24px 60px rgba(0,0,0,.35); padding: 28px 26px 22px; font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; max-height: 92vh; overflow: auto; animation: cbIn .18s ease; }
@keyframes cbIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.cb-modal h3 { margin: 0 0 4px; font-size: 1.35rem; }
.cb-sub { margin: 0 0 18px; color: #8a6f63; font-size: .9rem; }
.cb-x { position: absolute; top: 10px; right: 13px; background: none; border: 0; font-size: 1.7rem; line-height: 1; color: #b79f95; cursor: pointer; }
.cb-x:hover { color: #2a1812; }
.cb-modal label { display: block; font-size: .82rem; font-weight: 600; margin-bottom: 13px; }
.cb-modal label .o { color: #b79f95; font-weight: 400; }
.cb-modal input, .cb-modal textarea { width: 100%; margin-top: 5px; font: inherit; font-size: .94rem; color: #2a1812; background: #fbf6f0; border: 1px solid #efe1d6; border-radius: 9px; padding: 10px 12px; resize: vertical; }
.cb-modal input:focus, .cb-modal textarea:focus { outline: none; border-color: #d6453a; background: #fff; }
.cb-submit { width: 100%; margin-top: 4px; background: #d6453a; color: #fff; border: 0; border-radius: 999px; padding: 13px; font: 600 1rem system-ui; cursor: pointer; transition: background .2s; }
.cb-submit:hover { background: #b5342b; }
.cb-submit:disabled { opacity: .6; cursor: default; }
.cb-toast { display: none; margin-top: 14px; padding: 11px 13px; border-radius: 9px; font-size: .88rem; font-weight: 500; }
.cb-toast.ok { background: #e3f6ec; color: #0c6b3c; border: 1px solid #b8e6cc; }
.cb-toast.err { background: #fdecec; color: #9b2226; border: 1px solid #f3c7c7; }
.cb-open { margin-left: 10px; background: rgba(255,255,255,.16); color: inherit; border: 1px solid rgba(255,255,255,.34); border-radius: 999px; padding: 3px 13px; font: 600 .74rem system-ui; cursor: pointer; letter-spacing: .4px; vertical-align: middle; }
.cb-open:hover { background: rgba(255,255,255,.30); }
@media (max-width: 560px) { .cb-fab { right: 12px; bottom: 12px; padding: 11px 17px; } }
</style>
<button class="cb-fab" type="button" onclick="cbOpen()" aria-label="Rückruf anfordern">📞 Rückruf</button>
<div class="cb-overlay" id="cbOverlay" onclick="cbBg(event)">
<div class="cb-modal" role="dialog" aria-modal="true" aria-labelledby="cbTitle">
<button class="cb-x" type="button" onclick="cbClose()" aria-label="Schließen">&times;</button>
<h3 id="cbTitle">Rückruf anfordern</h3>
<p class="cb-sub">Name &amp; Nummer genügen — wir melden uns bei Ihnen. Alles andere ist optional.</p>
<form onsubmit="return cbSubmit(event, this)">
<input type="hidden" name="client_id" />
<input type="hidden" name="source" />
<label>Name *
<input name="name" required autocomplete="name" placeholder="Vor- und Nachname" />
</label>
<label>Telefon *
<input name="phone" required autocomplete="tel" inputmode="tel" placeholder="+49 …" />
</label>
<label>Ihr Betrieb <span class="o">(optional)</span>
<input name="business" placeholder="z. B. Café Lichtblick" />
</label>
<label>Was sollen wir für Sie umsetzen? <span class="o">(optional)</span>
<input name="service" placeholder="Website, Online-Bestellung, Rückruf-Service …" />
</label>
<label>Nachricht <span class="o">(optional)</span>
<textarea name="message" rows="2" placeholder="Worum geht es?"></textarea>
</label>
<button class="cb-submit" type="submit">Rückruf anfordern</button>
<div class="cb-toast" id="cbToast"></div>
</form>
</div>
</div>
<script>
const CB_WEBHOOK = 'https://n8n.mivanchenko.de/webhook/lead-intake';
const cbCtx = window.CB_CTX || { client_id: 'DEMO', source: 'callback' };
function cbOpen() {
const o = document.getElementById('cbOverlay');
o.querySelector('[name=client_id]').value = cbCtx.client_id;
o.querySelector('[name=source]').value = cbCtx.source;
o.classList.add('open');
document.body.style.overflow = 'hidden';
setTimeout(() => o.querySelector('[name=name]').focus(), 60);
}
function cbClose() {
document.getElementById('cbOverlay').classList.remove('open');
document.body.style.overflow = '';
}
function cbBg(e) { if (e.target.id === 'cbOverlay') cbClose(); }
document.addEventListener('keydown', e => { if (e.key === 'Escape') cbClose(); });
async function cbSubmit(e, form) {
e.preventDefault();
const btn = form.querySelector('.cb-submit');
const toast = document.getElementById('cbToast');
const d = Object.fromEntries(new FormData(form).entries());
let msg = (d.message || '').trim();
if (d.business) msg = (msg ? msg + ' — ' : '') + 'Betrieb: ' + d.business;
const payload = {
client_id: d.client_id, source: d.source,
name: d.name, phone: d.phone,
service_interest: d.service || '', message: msg
};
const old = btn.textContent;
btn.disabled = true; btn.textContent = 'Senden…';
toast.style.display = 'none';
try {
const r = await fetch(CB_WEBHOOK, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
if (!r.ok) throw new Error(r.status);
toast.className = 'cb-toast ok';
toast.textContent = '✓ Danke! Wir rufen Sie zurück.';
toast.style.display = 'block';
form.reset();
setTimeout(cbClose, 2200);
} catch (err) {
toast.className = 'cb-toast err';
toast.textContent = '⚠ Senden fehlgeschlagen. Bitte erneut versuchen.';
toast.style.display = 'block';
} finally {
btn.disabled = false; btn.textContent = old;
}
return false;
}
</script>
<!-- ===== /Rückruf-Widget ===== -->
</body>
</html>