Initial commit: Lageplan v1.0 - Next.js 15.5, React 19

This commit is contained in:
Pepe Ziberi
2026-02-21 11:57:44 +01:00
commit adf3dc8c1d
167 changed files with 34265 additions and 0 deletions

673
src/app/page.tsx Normal file
View File

@@ -0,0 +1,673 @@
'use client'
import Link from 'next/link'
import { Button } from '@/components/ui/button'
import { Logo } from '@/components/ui/logo'
import { useAuth } from '@/components/providers/auth-provider'
import { useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
import {
Flame, Map, Shield, Users, Smartphone, FileText, Ruler, Clock,
Check, ArrowRight, Lock, ChevronRight, MessageSquare, Loader2, Send,
Heart, Coffee, Rocket, Sparkles, Lightbulb, HelpCircle,
MousePointer2, Minus, Pentagon, Square, Circle, Pencil, MoveRight, Type, Eraser,
} from 'lucide-react'
export default function LandingPage() {
const { user, loading } = useAuth()
const router = useRouter()
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center bg-background">
<div className="animate-pulse text-muted-foreground">Laden...</div>
</div>
)
}
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: 'Lageplan',
applicationCategory: 'BusinessApplication',
operatingSystem: 'Web Browser',
description:
'Die moderne Krokier-App für Schweizer Feuerwehren. Einsatzpläne digital erstellen, Journal führen, Rapporte generieren direkt im Browser.',
url: 'https://lageplan.ch',
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'CHF',
description: 'Kostenlos für Schweizer Feuerwehren',
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: '4.8',
ratingCount: '12',
},
author: {
'@type': 'Organization',
name: 'Lageplan.ch',
url: 'https://lageplan.ch',
},
featureList: [
'Digitale Einsatzkrokierung',
'Einsatzjournal mit Wörtervorschlägen',
'Automatische Rapport-Generierung',
'Echtzeit-Zusammenarbeit',
'SOMA-Checkliste',
'PDF-Export',
'Schlauchberechnung',
'Offline-fähig',
],
}
return (
<div className="min-h-screen bg-white">
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<main>
{/* Navigation */}
<nav className="fixed top-0 w-full z-50 bg-white/80 backdrop-blur-md border-b border-gray-100">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<div className="flex items-center gap-2">
<Logo size={36} />
<span className="font-bold text-xl text-gray-900">Lageplan</span>
</div>
<div className="hidden md:flex items-center gap-8 text-sm text-gray-600">
<a href="#story" className="hover:text-gray-900 transition">Die Geschichte</a>
<a href="#features" className="hover:text-gray-900 transition">Funktionen</a>
<a href="#support" className="hover:text-gray-900 transition">Unterstützen</a>
<a href="#roadmap" className="hover:text-gray-900 transition">Roadmap</a>
</div>
<div className="flex items-center gap-3">
{user ? (
<Link href="/app">
<Button size="sm" className="bg-red-600 hover:bg-red-700">
Zur App
</Button>
</Link>
) : (
<>
<Link href="/login">
<Button variant="ghost" size="sm">Anmelden</Button>
</Link>
<Link href="/register">
<Button size="sm" className="bg-red-600 hover:bg-red-700">
Kostenlos starten
</Button>
</Link>
</>
)}
</div>
</div>
</div>
</nav>
{/* Hero */}
<section className="pt-32 pb-20 px-4">
<div className="max-w-4xl mx-auto text-center">
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-red-50 text-red-700 text-sm font-medium mb-6">
<Heart className="w-3.5 h-3.5" />
Ein Herzensprojekt Swiss Made
</div>
<h1 className="text-5xl sm:text-6xl font-extrabold text-gray-900 leading-tight tracking-tight">
Digitale Lagepläne
<br />
<span className="text-red-600">für die Schweizer Feuerwehr</span>
</h1>
<p className="mt-6 text-xl text-gray-500 max-w-2xl mx-auto leading-relaxed">
Die moderne Krokier-App für Einsatzdokumentation. Lagepläne erstellen,
Journal führen und Einsätze dokumentieren direkt im Browser, auf jedem Gerät.
<span className="block mt-2 text-gray-500">Kostenlos. Von einem Feuerwehrmann für Feuerwehrleute.</span>
</p>
<div className="mt-10 flex flex-col sm:flex-row gap-4 justify-center">
<Link href="/register">
<Button size="lg" className="bg-red-600 hover:bg-red-700 text-base px-8 h-12">
Jetzt kostenlos starten
<ArrowRight className="w-4 h-4 ml-2" />
</Button>
</Link>
<a href="#story">
<Button size="lg" variant="outline" className="text-base px-8 h-12">
Die Geschichte dahinter
</Button>
</a>
</div>
</div>
{/* Hero visual — App Preview Mockup */}
<div className="max-w-5xl mx-auto mt-16">
<div className="rounded-2xl border border-gray-200 shadow-2xl overflow-hidden bg-gradient-to-br from-gray-50 to-gray-100 p-1">
<div className="rounded-xl bg-white overflow-hidden">
<div className="bg-gray-800 h-8 flex items-center px-4 gap-1.5">
<div className="w-2.5 h-2.5 rounded-full bg-red-400" />
<div className="w-2.5 h-2.5 rounded-full bg-yellow-400" />
<div className="w-2.5 h-2.5 rounded-full bg-green-400" />
<span className="ml-3 text-xs text-gray-400 font-mono">lageplan.ch</span>
</div>
<div className="relative bg-gradient-to-br from-emerald-50 via-blue-50 to-sky-100 h-[400px] sm:h-[480px] flex items-center justify-center overflow-hidden">
{/* Stylized map background */}
<div className="absolute inset-0 opacity-20">
<div className="absolute top-[15%] left-[10%] w-32 h-20 bg-orange-200 rounded-sm rotate-6" />
<div className="absolute top-[25%] left-[30%] w-24 h-16 bg-orange-200 rounded-sm -rotate-3" />
<div className="absolute top-[10%] right-[20%] w-28 h-18 bg-orange-200 rounded-sm rotate-2" />
<div className="absolute bottom-[30%] left-[20%] w-20 h-14 bg-orange-200 rounded-sm" />
<div className="absolute bottom-[20%] right-[25%] w-36 h-22 bg-orange-200 rounded-sm rotate-1" />
<div className="absolute top-[5%] left-[5%] right-[5%] h-[2px] bg-gray-300" />
<div className="absolute top-[40%] left-0 right-0 h-[3px] bg-white" />
<div className="absolute top-0 bottom-0 left-[50%] w-[3px] bg-white" />
</div>
{/* Toolbar mockup */}
<div className="absolute left-4 top-4 bg-white/90 backdrop-blur rounded-xl shadow-lg p-2 flex flex-col gap-1.5">
{[MousePointer2, Minus, Pentagon, Square, Circle, Pencil, MoveRight, Type, Eraser, Ruler].map((Icon, i) => (
<div key={i} className={`w-8 h-8 rounded-lg flex items-center justify-center ${i === 1 ? 'bg-red-100 text-red-600' : 'text-gray-500 hover:bg-gray-100'}`}>
<Icon className="w-4 h-4" />
</div>
))}
</div>
{/* Center content */}
<div className="relative z-10 text-center px-8">
<div className="inline-flex items-center justify-center w-20 h-20 rounded-2xl bg-white shadow-xl mb-6">
<Map className="w-10 h-10 text-red-600" />
</div>
<p className="text-2xl sm:text-3xl font-bold text-gray-800">Krokieren. Digital.</p>
<p className="text-gray-500 mt-2 text-sm sm:text-base max-w-md mx-auto">
Interaktive Karte · FKS-Signaturen · Einsatzjournal · Echtzeit-Zusammenarbeit
</p>
<div className="flex flex-wrap gap-2 justify-center mt-6">
{['Zeichnen', 'Messen', 'Symbole', 'Journal', 'Export'].map(label => (
<span key={label} className="bg-white/80 backdrop-blur text-gray-700 text-xs font-medium px-3 py-1.5 rounded-full shadow-sm border border-gray-100">
{label}
</span>
))}
</div>
</div>
{/* Bottom CTA overlay */}
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-white via-white/80 to-transparent h-20 flex items-end justify-center pb-4">
<Link href="/register">
<Button size="sm" className="bg-red-600 hover:bg-red-700 shadow-lg">
Kostenlos registrieren & loslegen
<ArrowRight className="w-4 h-4 ml-1" />
</Button>
</Link>
</div>
</div>
</div>
</div>
</div>
</section>
{/* Stats / Trust Badges */}
<section className="py-12 px-4 border-b border-gray-100">
<div className="max-w-5xl mx-auto">
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 md:gap-8">
{[
{ value: '117', label: 'FKS-Signaturen', sub: 'Offizielle taktische Zeichen' },
{ value: '100%', label: 'Swiss Hosted', sub: 'Server in der Schweiz' },
{ value: '0.—', label: 'Kostenlos', sub: 'Für alle Feuerwehren' },
{ value: '24/7', label: 'Verfügbar', sub: 'Browser-basiert, jedes Gerät' },
].map((stat, i) => (
<div key={i} className="text-center">
<p className="text-3xl sm:text-4xl font-extrabold text-red-600">{stat.value}</p>
<p className="text-sm font-semibold text-gray-900 mt-1">{stat.label}</p>
<p className="text-xs text-gray-500 mt-0.5">{stat.sub}</p>
</div>
))}
</div>
</div>
</section>
{/* Personal Story */}
<section id="story" className="py-20 px-4 bg-gradient-to-b from-white to-gray-50">
<div className="max-w-5xl mx-auto">
<h2 className="text-3xl font-bold text-gray-900 mb-12 text-center">Warum ich das hier baue</h2>
<div className="grid md:grid-cols-3 gap-12 items-start">
{/* Video column */}
<div className="md:col-span-1 flex flex-col items-center">
<div className="w-full max-w-[280px] rounded-2xl overflow-hidden shadow-xl border border-gray-200">
<video
src="/Pepe_Avatar.mp4"
autoPlay
loop
muted
playsInline
className="w-full h-auto"
/>
</div>
<div className="mt-4 text-center">
<h3 className="text-lg font-bold text-gray-900">Pepe Ziberi</h3>
<p className="text-sm text-red-600 font-medium">Offizier & Chef Drohnenkorps</p>
<p className="text-xs text-gray-500 mt-1">IT · Dad · Feuerwehr</p>
<div className="flex gap-2 mt-3 justify-center">
<span className="text-xs bg-red-50 text-red-700 px-2 py-1 rounded-full">Feuerwehr</span>
<span className="text-xs bg-blue-50 text-blue-700 px-2 py-1 rounded-full">IT</span>
<span className="text-xs bg-green-50 text-green-700 px-2 py-1 rounded-full">Drohnen</span>
</div>
</div>
</div>
{/* Text column */}
<div className="md:col-span-2 space-y-4 text-gray-600 leading-relaxed text-[15px]">
<p>
Ich bin Pepe arbeite in der IT, bin Offizier in der Chemiewehr und Chef des Drohnenkorps, Vater und laut meiner Frau «anstrengend auf einem Level, das die Wissenschaft noch nicht erforscht hat». Nicht weil ich was Schlimmes mache. Sondern weil ich um Mitternacht im Bett liege und flüstere: «Schatz, ich hab eine Idee für eine App.» Ich höre förmlich wie sie neben mir die Augen verdreht. Im Dunkeln. Ohne ein Wort. Nur der tiefste Seufzer den ein Mensch erzeugen kann.
</p>
<p>
Mein Alltag besteht zu 40% aus IT-Tickets, 30% aus Kindergeschrei, 20% aus Drohnenflügen und 10% aus dem Versuch, meiner Frau zu erklären, warum ich schon wieder einen neuen Server brauche. «Aber Schatz, der alte hat nur 16GB RAM!» «Dein Kind braucht Winterschuhe.» «...der Server auch.»
</p>
<p>
Ich überwache meine Katzen per Smart-Home-Dashboard. Ja, wirklich. Ich weiss wann sie fressen, wann sie aufs Klo gehen und wie viel sie wiegen. Meine Frau sagt, unsere Katzen haben mehr Monitoring als mein Arbeitgeber. Sie hat leider recht. Leo und Mimi haben eine bessere Dateninfrastruktur als so manches Rechenzentrum. Das ist kein Witz. Das ist ein Hilferuf.
</p>
<p>
In der Chemiewehr bin ich der, der bei jeder Übung in der Ecke steht und murmelt: «Das könnte man automatisieren.» Meine Kameraden ignorieren mich mittlerweile professionell. Beim Krokieren im Einsatz wurde mir dann endgültig klar es gibt kein vernünftiges digitales Tool für die Schweizer Feuerwehr. Keins. Null. Nada. Niente. Nichts. Nüt.
</p>
<p>Also habe ich <strong>Lageplan</strong> gebaut.</p>
<p>
Und jetzt kommt der Plot-Twist: Ich kann eigentlich gar nicht richtig programmieren. Ich bin ein IT-Dad der nachts um Mitternacht Apps baut mit viel Kaffee, noch mehr Ehrgeiz und einer KI als Co-Pilot.
</p>
<p>
Aber halt es bleibt ja nicht beim Code. Nein nein. Ich bin gleichzeitig auch der Systemadministrator, der Netzwerktechniker, der Datenbankflüsterer, der DNS-Jongleur, der SSL-Zertifikats-Erneuerer-um-3-Uhr-morgens-weil-alles-abgelaufen-ist, der Backup-Verantwortliche, der Security-Beauftragte, der Designer, der Tester, der einzige User der um 2 Uhr nachts einen Bug meldet an sich selbst UND der Typ der dann in den App Store geht und schreibt «Funktioniert einwandfrei, 5 Sterne».
</p>
<p>
Die IT-Abteilung bin ich. Der Support bin ich. Der Kunde der sich beschwert bin ich auch. Das Marketing-Team? Auch ich. Die Finanzabteilung? Mein Bankkonto weint leise. HR? Ich hab mir letztens selbst eine Verwarnung gegeben wegen zu vieler Überstunden. Hab sie ignoriert.
</p>
<p>
Ein-Mann-Startup ohne das Start und ohne das Up. Einfach ein Mann mit WLAN, Energy Drinks und einer bedenklichen Anzahl offener Browser-Tabs. Letzter Stand: 347. Nein, ich werde keinen davon schliessen.
</p>
<p>
Dieses Projekt entsteht in meiner Freizeit. Also in der Zeit, in der normale Menschen schlafen, Netflix schauen oder Hobbys haben, die ihre Partnerin nicht in den Wahnsinn treiben. Meine Frau hat letztens gefragt: «Wann ist das Projekt fertig?» Ich habe gelacht. Sie hat nicht gelacht. Dann hat sie den Router ausgesteckt. Härteste Verhandlungstaktik die ich je erlebt habe.
</p>
<p>
Ich stelle <strong>Lageplan</strong> allen Feuerwehren in der Schweiz <strong>kostenlos</strong> zur Verfügung weil gute Werkzeuge allen gehören sollten. Und weil ich offenbar nicht genug Chaos in meinem Leben habe.
</p>
<p className="text-gray-500 italic text-sm">
Falls du mich suchst: Ich bin der mit den Augenringen, dem dritten Monitor und dem Gesichtsausdruck eines Mannes, der gerade realisiert hat, dass er vergessen hat die Datenbank zu sichern.
</p>
</div>
</div>
</div>
</section>
{/* Why it matters */}
<section className="py-16 px-4">
<div className="max-w-4xl mx-auto">
<div className="bg-gradient-to-r from-red-50 to-orange-50 rounded-2xl p-8 md:p-12 border border-red-100">
<div className="flex items-start gap-4">
<Lightbulb className="w-8 h-8 text-red-500 shrink-0 mt-1" />
<div>
<h2 className="text-xl font-bold text-gray-900 mb-3">Entwicklung kostet Zeit und Geld</h2>
<p className="text-gray-600 leading-relaxed">
Hinter jeder Funktion stecken Stunden an Entwicklung, Testing und Feinschliff.
Server-Hosting, Domain, SSL-Zertifikate das alles kostet. Ich finanziere das aktuell
komplett aus eigener Tasche, weil mir das Projekt am Herzen liegt.
Wenn du Lageplan nützlich findest, kannst du die Weiterentwicklung mit einer freiwilligen
Spende unterstützen. Jeder Beitrag hilft, neue Features zu bauen und die Server am Laufen zu halten.
</p>
</div>
</div>
</div>
</div>
</section>
{/* Features */}
<section id="features" className="py-20 bg-gray-50 px-4">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-16">
<h2 className="text-3xl font-bold text-gray-900">Was Lageplan kann</h2>
<p className="mt-3 text-lg text-gray-500">Professionelle Einsatzdokumentation einfach und schnell</p>
</div>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{[
{ icon: Map, title: 'Interaktive Karte', desc: 'Lagepläne auf OSM-Karten zeichnen mit Linien, Polygonen, Pfeilen und Freihandzeichnung.' },
{ icon: Shield, title: 'FKS-Signaturen', desc: 'Alle offiziellen FKS/BABS-Signaturen per Drag & Drop platzieren. Eigene Symbole hochladen.' },
{ icon: Ruler, title: 'Messwerkzeug', desc: 'Distanzen messen mit Druckverlustberechnung und Pumpenanzahl für Schlauchleitungen.' },
{ icon: FileText, title: 'Einsatzjournal', desc: 'Journal mit Zeitstempel, SOMA-Checkliste und Pendenzenliste — alles an einem Ort.' },
{ icon: Smartphone, title: 'Installierbare App (PWA)', desc: 'Auf dem Homescreen installierbar — funktioniert offline wie eine native App. Desktop, Tablet & Smartphone.' },
{ icon: Users, title: 'Echtzeit-Zusammenarbeit', desc: 'Mehrere Benutzer können gleichzeitig am selben Lageplan arbeiten.' },
{ icon: Clock, title: 'Audit Trail', desc: 'Lückenlose Protokollierung aller Aktionen mit Zeitstempel für Nachvollziehbarkeit.' },
{ icon: Lock, title: 'Sicher & Schweiz', desc: 'Rollenbasierte Zugriffskontrolle. Gehostet in der Schweiz — DSG/DSGVO-konform.' },
{ icon: FileText, title: 'PDF-Export', desc: 'Lagepläne als PDF exportieren mit Metadaten, Datum und Einsatzinformationen.' },
].map((f, i) => (
<div key={i} className="bg-white rounded-xl p-6 border border-gray-100 hover:shadow-lg transition-shadow">
<div className="w-10 h-10 rounded-lg bg-red-50 flex items-center justify-center mb-4">
<f.icon className="w-5 h-5 text-red-600" />
</div>
<h3 className="font-semibold text-gray-900 mb-2" role="heading" aria-level={3}>{f.title}</h3>
<p className="text-sm text-gray-500 leading-relaxed">{f.desc}</p>
</div>
))}
</div>
</div>
</section>
{/* Support / Buy me a coffee */}
<SupportSection />
{/* Roadmap */}
<section id="roadmap" className="py-20 bg-gray-50 px-4">
<div className="max-w-4xl mx-auto">
<div className="text-center mb-16">
<Rocket className="w-10 h-10 text-red-600 mx-auto mb-4" />
<h2 className="text-3xl font-bold text-gray-900">Was noch kommt</h2>
<p className="mt-3 text-lg text-gray-500">
Lageplan wird ständig weiterentwickelt. Hier ein Ausblick auf geplante Features.
</p>
</div>
<div className="space-y-4">
{[
{ status: 'done', title: 'Interaktive Karte mit Zeichenwerkzeugen', desc: 'Linien, Polygone, Pfeile, Freihand, Punkte' },
{ status: 'done', title: 'FKS-Signaturen (117 offizielle Symbole)', desc: 'Alle taktischen Zeichen nach Schweizer Standard (Bildquelle: Feuerwehr Koordination Schweiz FKS)' },
{ status: 'done', title: 'Einsatzjournal mit SOMA-Checkliste', desc: 'Zeitstempel, Pendenzen, Protokollierung' },
{ status: 'done', title: 'Messwerkzeug mit Druckverlustberechnung', desc: 'Distanzen, Höhenmeter, Pumpenanzahl' },
{ status: 'done', title: 'Echtzeit-Zusammenarbeit (WebSocket)', desc: 'Mehrere Benutzer gleichzeitig am Lageplan' },
{ status: 'done', title: 'PDF-Export & Nachtmodus', desc: 'Professioneller Export und augenschonendes Arbeiten' },
{ status: 'done', title: 'Mobile App (PWA)', desc: 'Installierbar auf dem Homescreen — offline-fähig, wie eine native App' },
{ status: 'planned', title: 'Vorlagen & Einsatzpläne', desc: 'Wiederverwendbare Vorlagen für häufige Szenarien' },
].map((item, i) => (
<div key={i} className="flex items-start gap-4 bg-white rounded-xl p-5 border border-gray-100">
<div className={`w-8 h-8 rounded-full flex items-center justify-center shrink-0 mt-0.5 ${
item.status === 'done' ? 'bg-green-100' : 'bg-amber-100'
}`}>
{item.status === 'done' ? (
<Check className="w-4 h-4 text-green-600" />
) : (
<Sparkles className="w-4 h-4 text-amber-600" />
)}
</div>
<div>
<h3 className="font-semibold text-gray-900">{item.title}</h3>
<p className="text-sm text-gray-500 mt-0.5">{item.desc}</p>
</div>
<span className={`ml-auto text-xs font-medium px-2.5 py-1 rounded-full shrink-0 ${
item.status === 'done' ? 'bg-green-50 text-green-700' : 'bg-amber-50 text-amber-700'
}`}>
{item.status === 'done' ? 'Fertig' : 'Geplant'}
</span>
</div>
))}
</div>
<p className="text-center text-sm text-gray-500 mt-8">
Hast du eine Idee für ein neues Feature? <a href="#contact" className="text-red-600 hover:text-red-500 underline">Schreib mir!</a>
</p>
</div>
</section>
{/* Contact */}
<ContactSection />
{/* FAQ */}
<section id="faq" className="py-20 px-4">
<div className="max-w-3xl mx-auto">
<div className="text-center mb-12">
<HelpCircle className="w-10 h-10 text-red-600 mx-auto mb-4" />
<h2 className="text-3xl font-bold text-gray-900">Häufige Fragen</h2>
</div>
<div className="space-y-4">
{[
{
q: 'Was kostet Lageplan?',
a: 'Nichts. Lageplan ist kostenlos für alle Feuerwehren in der Schweiz. Die Entwicklung wird durch freiwillige Spenden finanziert.',
},
{
q: 'Brauche ich eine Installation?',
a: 'Nein. Lageplan läuft komplett im Browser — auf Desktop, Tablet und Smartphone. Einfach registrieren und loslegen.',
},
{
q: 'Funktioniert es auf dem Tablet im Einsatz?',
a: 'Ja. Die App ist für Touch-Bedienung optimiert und funktioniert auf allen modernen Tablets und Smartphones.',
},
{
q: 'Können mehrere Personen gleichzeitig arbeiten?',
a: 'Ja. Über Echtzeit-Synchronisation (WebSocket) können mehrere Benutzer gleichzeitig am selben Lageplan zeichnen und das Journal führen.',
},
{
q: 'Wo werden meine Daten gespeichert?',
a: 'Alle Daten werden auf Servern in der Schweiz gespeichert. Die Applikation ist DSG- und DSGVO-konform.',
},
{
q: 'Welche Symbole sind verfügbar?',
a: 'Alle 117 offiziellen FKS/BABS-Signaturen sind integriert. Zusätzlich können eigene Symbole hochgeladen werden.',
},
{
q: 'Kann ich Lagepläne exportieren?',
a: 'Ja. Lagepläne können als PNG oder PDF exportiert werden — inklusive Metadaten, Datum und Einsatzinformationen.',
},
].map((faq, i) => (
<details key={i} className="group bg-white rounded-xl border border-gray-200 overflow-hidden">
<summary className="flex items-center justify-between cursor-pointer px-6 py-4 text-left font-semibold text-gray-900 hover:bg-gray-50 transition">
{faq.q}
<ChevronRight className="w-4 h-4 text-gray-400 transition-transform group-open:rotate-90 shrink-0 ml-4" />
</summary>
<div className="px-6 pb-4 text-sm text-gray-600 leading-relaxed">
{faq.a}
</div>
</details>
))}
</div>
</div>
</section>
{/* CTA */}
<section className="py-20 px-4 bg-gradient-to-b from-white to-gray-50">
<div className="max-w-3xl mx-auto text-center">
<h2 className="text-3xl font-bold text-gray-900">Bereit loszulegen?</h2>
<p className="mt-4 text-lg text-gray-500">
Registriere dich kostenlos und starte sofort mit dem digitalen Krokieren.
Keine Kreditkarte, keine versteckten Kosten.
</p>
<Link href="/register" className="inline-block mt-8">
<Button size="lg" className="bg-red-600 hover:bg-red-700 text-base px-8 h-12">
Jetzt kostenlos registrieren
<ChevronRight className="w-4 h-4 ml-1" />
</Button>
</Link>
</div>
</section>
{/* Footer */}
<footer className="border-t border-gray-100 py-12 px-4">
<div className="max-w-6xl mx-auto">
<div className="flex flex-col sm:flex-row justify-between items-start gap-8">
<div>
<div className="flex items-center gap-2 mb-2">
<Logo size={28} />
<span className="font-semibold text-gray-900">Lageplan</span>
</div>
<p className="text-sm text-gray-500">Digitale Lagepläne für die Schweizer Feuerwehr</p>
<p className="text-sm text-gray-500 mt-1">Ein Projekt von Pepe Ziberi</p>
<p className="text-sm text-gray-500">Gehostet in der Schweiz Swiss Made 🇨🇭</p>
</div>
<div className="text-sm text-gray-500">
<p className="font-medium text-gray-700 mb-2">Kontakt</p>
<p>app@lageplan.ch</p>
<a href="#contact" className="text-red-600 hover:text-red-500 underline">Kontaktformular</a>
</div>
<div className="text-sm text-gray-500">
<p className="font-medium text-gray-700 mb-2">Produkt</p>
<a href="#features" className="block hover:text-gray-700">Funktionen</a>
<a href="#roadmap" className="block hover:text-gray-700 mt-1">Roadmap</a>
<a href="#support" className="block hover:text-gray-700 mt-1">Unterstützen</a>
<Link href="/register" className="block text-red-600 hover:text-red-500 mt-1">Registrieren</Link>
</div>
<div className="text-sm text-gray-500">
<p className="font-medium text-gray-700 mb-2">Rechtliches</p>
<Link href="/impressum" className="block hover:text-gray-700">Impressum</Link>
<Link href="/datenschutz" className="block hover:text-gray-700 mt-1">Datenschutz</Link>
</div>
</div>
<div className="border-t border-gray-100 mt-8 pt-6 text-center space-y-1">
<p className="text-sm text-gray-500">&copy; {new Date().getFullYear()} Lageplan Purepixel. Alle Rechte vorbehalten.</p>
<p className="text-xs text-gray-500">Taktische Symbole: Bildquelle Feuerwehr Koordination Schweiz FKS</p>
</div>
</div>
</footer>
</main>
</div>
)
}
function SupportSection() {
return (
<section id="support" className="py-20 px-4">
<div className="max-w-3xl mx-auto">
<div className="text-center mb-12">
<Coffee className="w-10 h-10 text-red-600 mx-auto mb-4" />
<h2 className="text-3xl font-bold text-gray-900">Projekt unterstützen</h2>
<p className="mt-3 text-lg text-gray-500 max-w-xl mx-auto">
Lageplan ist und bleibt kostenlos. Wenn du die Weiterentwicklung unterstützen möchtest,
freue ich mich über einen freiwilligen Beitrag.
</p>
</div>
<div className="bg-white rounded-2xl border border-gray-200 shadow-lg p-8 text-center">
{/* Tier preview */}
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3 mb-8">
{[
{ emoji: '\u2615', label: 'Kaffee', amount: 'CHF 5' },
{ emoji: '\uD83C\uDF55', label: 'Pizza', amount: 'CHF 10' },
{ emoji: '\uD83D\uDDA5\uFE0F', label: 'Server', amount: 'CHF 25' },
{ emoji: '\uD83D\uDE80', label: 'Feature', amount: 'CHF 50' },
].map(tier => (
<div key={tier.label} className="rounded-xl p-4 border border-gray-100 bg-gray-50">
<span className="text-2xl block mb-1">{tier.emoji}</span>
<span className="text-sm font-bold text-gray-900">{tier.amount}</span>
<span className="text-xs text-gray-500 block">{tier.label}</span>
</div>
))}
</div>
<p className="text-gray-500 mb-6">
Wähle einen Betrag auf unserer Spendenseite Zahlung sicher via Stripe (Kreditkarte, Twint).
</p>
<Link href="/spenden">
<Button className="bg-red-600 hover:bg-red-700 h-12 text-base px-8">
<Heart className="w-4 h-4 mr-2" />
Zur Spendenseite
</Button>
</Link>
</div>
{/* Trust note */}
<div className="mt-8 text-center">
<p className="text-sm text-gray-500">
100% deiner Spende fliesst direkt in Serverkosten und Weiterentwicklung.
<br />Kein Unternehmen, keine Investoren nur ein Feuerwehrmann mit einer Idee.
</p>
</div>
</div>
</section>
)
}
function ContactSection() {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [message, setMessage] = useState('')
const [sending, setSending] = useState(false)
const [sent, setSent] = useState(false)
const [error, setError] = useState('')
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setSending(true)
setError('')
try {
const res = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, email, message }),
})
if (res.ok) {
setSent(true)
setName('')
setEmail('')
setMessage('')
} else {
const data = await res.json()
setError(data.error || 'Senden fehlgeschlagen')
}
} catch {
setError('Verbindung fehlgeschlagen')
} finally {
setSending(false)
}
}
return (
<section id="contact" className="py-20 px-4">
<div className="max-w-2xl mx-auto">
<div className="text-center mb-10">
<MessageSquare className="w-10 h-10 text-red-600 mx-auto mb-4" />
<h2 className="text-3xl font-bold text-gray-900">Kontakt</h2>
<p className="mt-3 text-lg text-gray-500">
Fragen, Feature-Wünsche oder Feedback? Schreib mir ich freue mich über jede Nachricht.
</p>
</div>
{sent ? (
<div className="text-center bg-green-50 border border-green-200 rounded-xl p-8">
<Check className="w-10 h-10 text-green-600 mx-auto mb-3" />
<h3 className="font-semibold text-green-900 text-lg">Nachricht gesendet!</h3>
<p className="text-green-700 mt-2">Vielen Dank! Ich melde mich so schnell wie möglich.</p>
<Button variant="outline" className="mt-4" onClick={() => setSent(false)}>
Weitere Nachricht senden
</Button>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-4">
<div className="grid sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Name</label>
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
required
placeholder="Dein Name"
className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">E-Mail</label>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
required
placeholder="name@feuerwehr.ch"
className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Nachricht</label>
<textarea
value={message}
onChange={e => setMessage(e.target.value)}
required
rows={5}
placeholder="Deine Nachricht..."
className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-red-500 focus:border-transparent resize-none"
/>
</div>
{error && <p className="text-sm text-red-600">{error}</p>}
<Button
type="submit"
className="bg-red-600 hover:bg-red-700"
disabled={sending || !name || !email || !message}
>
{sending ? (
<><Loader2 className="w-4 h-4 mr-2 animate-spin" /> Wird gesendet...</>
) : (
<><Send className="w-4 h-4 mr-2" /> Nachricht senden</>
)}
</Button>
</form>
)}
</div>
</section>
)
}