feat: Onepage nach Filmcamp-Gliederung umgebaut

- Figtree als einzige Schrift, Logo mittig in fester Kopfzeile
- Headerbild ueber volle Breite und Hoehe, ohne Preis, mit Terminleiste
- "Was dich erwartet" stellt die Villa mit animierter Bilderfolge vor,
  darunter Tagesablauf als Karussell (Programm folgt als PDF)
- Preis: regulaerer Preis durchgestrichen, kein Verkaufsstartdatum,
  Ueberschrift "Jetzt Platz sichern mit Early Bird"
- Team als "Mit dabei", danach drei Reels im Hochformat
- Logo, Headerbild, Portraets und Reels ueber feste Dateinamen im
  Medienverzeichnis (docs/MEDIEN.md), ohne Neustart austauschbar
- Widerrufsbelehrung im Admin pflegbar, Seite /widerruf, Live-Verkauf
  ohne Freigabe gesperrt
- Anreise: Abholung am Bahnhof Firenze Santa Maria Novella
- Booking-Link entfernt, Gedankenstriche aus Kundentexten entfernt

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-01 23:53:18 +02:00
1 parent 3cba3e2341
commit 7203cd1e6e
37 files changed
+962 -517

No files matched your search

@@ -0,0 +1,65 @@
import { site } from "@/content/site";
import type { PublicCampFacts } from "@/lib/camp";
import { addDays, formatIsoWeekdayDate } from "@/lib/time";
import { ScheduleCarousel, type ScheduleCard } from "../ScheduleCarousel";
import { Slideshow } from "../Slideshow";
/** Stellt die Villa vor: Text links, laufende Bilderfolge rechts. */
export function ExpectSection() {
const { expect } = site;
return (
<section id="erwartet" aria-labelledby="erwartet-title" className="py-24 md:py-32">
<div className="container-edge grid items-center gap-12 md:grid-cols-12 md:gap-10">
<div className="md:col-span-5">
<h2 id="erwartet-title" className="section-title">{expect.title}</h2>
<p className="mt-6 text-xl font-semibold leading-snug md:text-2xl">{expect.lead}</p>
<div className="prose-body mt-6 text-ink/85">
{expect.paragraphs.map((p) => (
<p key={p}>{p}</p>
))}
</div>
<ul className="mt-8 space-y-3">
{expect.highlights.map((item) => (
<li key={item} className="flex items-center gap-3 font-medium">
<span aria-hidden className="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-rosso text-paper">
<svg viewBox="0 0 24 24" className="h-4 w-4">
<path d="M5 12.5l4.5 4.5L19 7.5" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</span>
{item}
</li>
))}
</ul>
</div>
<Slideshow images={expect.slides} className="md:col-span-7 md:col-start-6 lg:col-span-6 lg:col-start-7" />
</div>
</section>
);
}
function scheduleCards(facts: PublicCampFacts): ScheduleCard[] {
return site.schedule.days.map((day, index) => ({
label: `Tag ${index + 1}`,
date: facts.startDate ? formatIsoWeekdayDate(addDays(facts.startDate, index)) : null,
title: day.title,
items: day.items,
}));
}
/** Tagesablauf als Karten pro Tag, wie das Programm bei der Referenz. */
export function ScheduleSection({ facts }: { facts: PublicCampFacts }) {
const { schedule } = site;
return (
<section id="ablauf" aria-labelledby="ablauf-title" className="bg-sand/35 py-24 md:py-32">
<div className="container-edge">
<div className="flex flex-wrap items-end justify-between gap-6">
<h2 id="ablauf-title" className="section-title">{schedule.title}</h2>
<p className="max-w-md text-ink/80">{schedule.note}</p>
</div>
<div className="mt-12">
<ScheduleCarousel days={scheduleCards(facts)} emptyText="Programm folgt." />
</div>
</div>
</section>
);
}