Initial commit: spreewaldzeit + Dockerfile for Coolify (Next.js + Prisma/SQLite)
This commit is contained in:
53
components/home/About.tsx
Normal file
53
components/home/About.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
export function About() {
|
||||
return (
|
||||
<section className="py-20 md:py-28 border-t border-ink/10">
|
||||
<div className="container">
|
||||
{/* Stats row */}
|
||||
<div className="grid grid-cols-3 gap-4 md:gap-0 md:divide-x divide-ink/10 border border-ink/10 rounded-sm mb-16 md:mb-20">
|
||||
{[
|
||||
{ value: "2", label: "Ferienwohnungen" },
|
||||
{ value: "400 km", label: "Radwege ringsum" },
|
||||
{ value: "90 min", label: "ab Berlin" },
|
||||
].map((s) => (
|
||||
<div key={s.label} className="px-4 py-5 md:px-10 md:py-7 text-center">
|
||||
<div className="font-display text-2xl md:text-4xl leading-none text-moss-700 mb-1">{s.value}</div>
|
||||
<div className="text-[10px] md:text-xs text-ink/50 uppercase tracking-[0.12em] md:tracking-[0.15em]">{s.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Editorial text block */}
|
||||
<div className="grid md:grid-cols-12 gap-10 md:gap-16">
|
||||
<div className="md:col-span-4">
|
||||
<div className="eyebrow mb-4">Über uns</div>
|
||||
<h2 className="font-display text-display-md">
|
||||
Zwei Häuser,<br />
|
||||
<span className="italic text-moss-600">eine Haltung.</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-7 md:col-start-6 space-y-6 text-ink/80 leading-relaxed">
|
||||
<p className="text-lg">
|
||||
Wir vermieten keine Hotelzimmer und führen keine Rezeption. Was wir anbieten,
|
||||
sind zwei Wohnungen, die wir selbst bewohnen würden — und manchmal tun.
|
||||
</p>
|
||||
<p>
|
||||
Kahnblick liegt am Hafen von Lübbenau, Erlenhof steht unter den alten Bäumen
|
||||
in Burg. Beide sind verschieden, beide sind still. Beide sind gedacht für
|
||||
Menschen, die lieber einen Tag länger bleiben als einen schneller weiter.
|
||||
</p>
|
||||
<figure className="pt-6 border-t border-ink/10 mt-10">
|
||||
<blockquote className="font-display text-2xl md:text-3xl leading-tight italic text-moss-700">
|
||||
„Wir glauben, dass ein gutes Frühstück länger dauern darf als eine
|
||||
Vorstandssitzung."
|
||||
</blockquote>
|
||||
<figcaption className="mt-4 text-sm text-ink/55">
|
||||
— Familie Musterfrau, Gastgeber
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
138
components/home/ApartmentPreview.tsx
Normal file
138
components/home/ApartmentPreview.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import type { Apartment } from "@/types";
|
||||
import { formatPrice } from "@/lib/utils";
|
||||
|
||||
export function ApartmentPreview({
|
||||
apartments,
|
||||
}: {
|
||||
apartments: Apartment[];
|
||||
}) {
|
||||
return (
|
||||
<section id="wohnungen" className="py-20 md:py-28 border-t border-ink/10 scroll-mt-24">
|
||||
<div className="container">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-6 mb-14 md:mb-20">
|
||||
<div>
|
||||
<div className="eyebrow mb-4">Unsere Wohnungen</div>
|
||||
<h2 className="font-display text-display-lg max-w-2xl leading-[1.02]">
|
||||
Zwei Orte, an die man<br className="hidden md:inline" />{" "}
|
||||
<span className="italic text-moss-600">gerne zurückkehrt</span>.
|
||||
</h2>
|
||||
</div>
|
||||
<p className="text-ink/65 max-w-sm text-sm md:text-base">
|
||||
Jedes Haus hat seine eigene Geschichte — wählen Sie, was für Ihre
|
||||
nächste Auszeit passt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-20 md:space-y-28">
|
||||
{apartments.map((apt, i) => {
|
||||
const isEven = i % 2 === 0;
|
||||
const hasPlatforms = apt.airbnbUrl || apt.bookingUrl;
|
||||
return (
|
||||
<article
|
||||
key={apt.id}
|
||||
className="grid md:grid-cols-12 gap-8 md:gap-14 items-center"
|
||||
>
|
||||
<Link
|
||||
href={`/wohnungen/${apt.slug}`}
|
||||
className={`md:col-span-7 relative aspect-[3/2] md:aspect-[4/3] overflow-hidden rounded-sm group ${
|
||||
isEven ? "" : "md:col-start-6"
|
||||
}`}
|
||||
>
|
||||
<Image
|
||||
src={apt.images[0] ?? ""}
|
||||
alt={apt.name}
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, 58vw"
|
||||
className="object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-[1.03]"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-ink/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
|
||||
<div className="absolute top-5 left-5 bg-ink/60 backdrop-blur-sm text-parchment px-3 py-1 text-xs font-medium rounded-full tracking-widest">
|
||||
0{i + 1}
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<div
|
||||
className={`md:col-span-5 ${
|
||||
isEven ? "md:col-start-8" : "md:col-start-1 md:row-start-1"
|
||||
}`}
|
||||
>
|
||||
<div className="eyebrow mb-3">{apt.tagline}</div>
|
||||
<h3 className="font-display text-4xl md:text-5xl mb-5 leading-none">
|
||||
{apt.name}
|
||||
</h3>
|
||||
<p className="text-ink/75 leading-relaxed mb-6">
|
||||
{apt.shortDescription}
|
||||
</p>
|
||||
|
||||
<dl className="grid grid-cols-3 gap-4 py-5 border-y border-ink/10 text-sm">
|
||||
<div>
|
||||
<dt className="eyebrow mb-1.5">Gäste</dt>
|
||||
<dd className="font-display text-2xl leading-none">
|
||||
bis {apt.maxGuests}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="eyebrow mb-1.5">Größe</dt>
|
||||
<dd className="font-display text-2xl leading-none">{apt.sizeSqm} m²</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="eyebrow mb-1.5">ab / Nacht</dt>
|
||||
<dd className="font-display text-2xl leading-none text-moss-700">
|
||||
{formatPrice(apt.priceFrom)}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<div className="mt-7 flex flex-wrap gap-3">
|
||||
<Link
|
||||
href={`/wohnungen/${apt.slug}`}
|
||||
className="inline-flex items-center gap-2 bg-ink text-parchment px-6 py-3 rounded-full text-sm hover:bg-moss-700 transition-colors"
|
||||
>
|
||||
Wohnung ansehen
|
||||
<span aria-hidden>→</span>
|
||||
</Link>
|
||||
<Link
|
||||
href={`/anfrage?wohnung=${apt.slug}`}
|
||||
className="inline-flex items-center gap-2 px-6 py-3 rounded-full text-sm border border-ink/15 hover:border-ink/30 hover:bg-ink/5 transition"
|
||||
>
|
||||
Direkt anfragen
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{hasPlatforms && (
|
||||
<div className="mt-5 flex items-center gap-3 text-xs text-ink/45">
|
||||
<span>Auch auf</span>
|
||||
{apt.airbnbUrl && (
|
||||
<a
|
||||
href={apt.airbnbUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-ink/60 hover:text-ink transition underline underline-offset-2"
|
||||
>
|
||||
Airbnb
|
||||
</a>
|
||||
)}
|
||||
{apt.airbnbUrl && apt.bookingUrl && <span className="text-ink/25">·</span>}
|
||||
{apt.bookingUrl && (
|
||||
<a
|
||||
href={apt.bookingUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-ink/60 hover:text-ink transition underline underline-offset-2"
|
||||
>
|
||||
Booking.com
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
78
components/home/Hero.tsx
Normal file
78
components/home/Hero.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<section className="-mt-[72px] md:-mt-[80px] relative min-h-[100vh] flex flex-col overflow-hidden">
|
||||
{/* Full-bleed background image */}
|
||||
<div className="absolute inset-0 z-0">
|
||||
<Image
|
||||
src="/images/b6ca2c_a39e632a73b944dbbd6887cdb627223d~mv2.avif"
|
||||
alt="Nebel über dem Spreewald"
|
||||
fill
|
||||
priority
|
||||
sizes="100vw"
|
||||
className="object-cover"
|
||||
/>
|
||||
{/* Base dark veil */}
|
||||
<div className="absolute inset-0 bg-ink/40" />
|
||||
{/* Strong bottom-to-top gradient for text area */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-ink/85 via-ink/50 to-ink/15" />
|
||||
{/* Left-side reinforcement for text column */}
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-ink/50 via-ink/20 to-transparent" />
|
||||
</div>
|
||||
|
||||
{/* Content — pinned to bottom */}
|
||||
<div className="relative z-10 mt-auto container pb-16 md:pb-24 pt-32 md:pt-40">
|
||||
<div className="max-w-4xl">
|
||||
<div className="eyebrow !text-parchment/60 mb-6">Ferienwohnungen · Spreewald</div>
|
||||
<h1 className="font-display text-display-xl text-parchment leading-[0.96]" style={{ textShadow: "0 2px 24px rgba(28,38,32,0.5)" }}>
|
||||
Hier hat der<br />
|
||||
Morgen noch{" "}
|
||||
<span className="italic text-moss-300">Nebel</span>,<br />
|
||||
und die Nacht<br />
|
||||
noch{" "}
|
||||
<span className="italic text-moss-300">Sterne</span>.
|
||||
</h1>
|
||||
|
||||
<div className="mt-10 flex flex-col sm:flex-row gap-3">
|
||||
<Link
|
||||
href="#wohnungen"
|
||||
className="inline-flex items-center justify-center gap-2 bg-parchment text-ink px-7 py-3.5 rounded-full text-sm font-medium hover:bg-cream transition-colors"
|
||||
>
|
||||
Die Wohnungen ansehen
|
||||
<span aria-hidden>↓</span>
|
||||
</Link>
|
||||
<Link
|
||||
href="/anfrage"
|
||||
className="inline-flex items-center justify-center gap-2 border border-parchment/30 text-parchment px-7 py-3.5 rounded-full text-sm hover:border-parchment/60 hover:bg-parchment/10 transition"
|
||||
>
|
||||
Anfrage senden
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Trust signals */}
|
||||
<div className="mt-10 pt-8 border-t border-parchment/15 flex flex-wrap gap-6 text-xs text-parchment/50">
|
||||
<span className="flex items-center gap-2">
|
||||
<span className="h-1 w-1 rounded-full bg-moss-400 inline-block" aria-hidden />
|
||||
Keine Provision
|
||||
</span>
|
||||
<span className="flex items-center gap-2">
|
||||
<span className="h-1 w-1 rounded-full bg-moss-400 inline-block" aria-hidden />
|
||||
Direkt beim Gastgeber
|
||||
</span>
|
||||
<span className="flex items-center gap-2">
|
||||
<span className="h-1 w-1 rounded-full bg-moss-400 inline-block" aria-hidden />
|
||||
Antwort in 24 Stunden
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom scroll hint */}
|
||||
<div className="relative z-10 container pb-8 flex items-center justify-end">
|
||||
<span className="text-xs text-parchment/35 tracking-[0.2em] uppercase">Scrollen</span>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
136
components/home/Location.tsx
Normal file
136
components/home/Location.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { SpreeMap } from "./SpreeMap";
|
||||
|
||||
const highlights = [
|
||||
{
|
||||
title: "Die Kähne",
|
||||
text: "Vom Hafen Lübbenau starten täglich die traditionellen Spreewaldkähne. Drei Stunden durch das UNESCO-Biosphärenreservat — ohne Motor, mit Schilf und Stille.",
|
||||
image: "https://images.unsplash.com/photo-1528181304800-259b08848526?auto=format&fit=crop&w=800&q=75",
|
||||
},
|
||||
{
|
||||
title: "Die Wege",
|
||||
text: "Über 400 km Radwege führen durch die Region. Unsere Fahrräder stehen für Sie bereit, Tourentipps liegen in jeder Wohnung aus.",
|
||||
image: "https://images.unsplash.com/photo-1517649763962-0c623066013b?auto=format&fit=crop&w=800&q=75",
|
||||
},
|
||||
{
|
||||
title: "Der Tisch",
|
||||
text: "Gurken, Leinöl, frischer Fisch, sorbische Hochzeitssuppe — die Spreewaldküche ist bodenständig und überraschend. Wir haben unsere Lieblingsadressen notiert.",
|
||||
image: "https://images.unsplash.com/photo-1476224203421-9ac39bcb3327?auto=format&fit=crop&w=800&q=75",
|
||||
},
|
||||
];
|
||||
|
||||
export function Location() {
|
||||
return (
|
||||
<section id="umgebung" className="py-20 md:py-28 border-t border-ink/10 scroll-mt-24">
|
||||
<div className="container">
|
||||
<div className="max-w-3xl mb-14 md:mb-20">
|
||||
<div className="eyebrow mb-4">Die Umgebung</div>
|
||||
<h2 className="font-display text-display-lg leading-[1.02]">
|
||||
Zwischen Wasser,<br />
|
||||
<span className="italic text-moss-600">Wald und Wegen.</span>
|
||||
</h2>
|
||||
<p className="mt-6 text-ink/70 max-w-xl leading-relaxed">
|
||||
Der Spreewald ist ein UNESCO-Biosphärenreservat. Weniger als 90 Minuten
|
||||
von Berlin entfernt — und doch eine andere Welt.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Highlight cards */}
|
||||
<div className="grid md:grid-cols-3 gap-6 md:gap-8">
|
||||
{highlights.map((h) => (
|
||||
<article
|
||||
key={h.title}
|
||||
className="group bg-cream/60 rounded-sm overflow-hidden"
|
||||
>
|
||||
<div className="relative aspect-[5/4] overflow-hidden">
|
||||
<Image
|
||||
src={h.image}
|
||||
alt={h.title}
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, 33vw"
|
||||
className="object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-[1.04]"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-6 md:p-7">
|
||||
<h3 className="font-display text-2xl mb-3">{h.title}</h3>
|
||||
<p className="text-sm text-ink/75 leading-relaxed">{h.text}</p>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Map */}
|
||||
<div className="mt-16 md:mt-20">
|
||||
<div className="eyebrow mb-4">Wo wir sind</div>
|
||||
<h3 className="font-display text-2xl md:text-3xl mb-3 leading-tight">
|
||||
Vetschau/Spreewald —{" "}
|
||||
<span className="italic text-moss-600">mitten im Biosphärenreservat</span>.
|
||||
</h3>
|
||||
<p className="text-ink/65 text-sm mb-6 max-w-xl">
|
||||
Kraftwerkstraße 10 · 03226 Vetschau. Klicken Sie auf den Marker für
|
||||
die Routenplanung.
|
||||
</p>
|
||||
|
||||
<div className="relative overflow-hidden rounded-sm border border-ink/10 shadow-[0_1px_3px_rgba(28,38,32,0.04),0_4px_16px_rgba(28,38,32,0.05)]">
|
||||
<SpreeMap />
|
||||
<div className="absolute inset-0 pointer-events-none ring-1 ring-inset ring-ink/8 rounded-sm" />
|
||||
</div>
|
||||
|
||||
{/* Address cards */}
|
||||
<div className="mt-5 grid sm:grid-cols-2 gap-4">
|
||||
<div className="flex items-start justify-between gap-4 bg-cream/60 border border-ink/10 rounded-sm px-5 py-4">
|
||||
<div>
|
||||
<div className="eyebrow mb-1">Spreewaldzeit</div>
|
||||
<div className="font-display text-lg leading-tight">Kraftwerkstraße 10</div>
|
||||
<div className="text-sm text-ink/65 mt-1">03226 Vetschau/Spreewald</div>
|
||||
</div>
|
||||
<a
|
||||
href="https://www.google.com/maps/dir/?api=1&destination=51.7753,14.0966"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="shrink-0 text-xs text-moss-700 border border-moss-300 px-3 py-1.5 rounded-full hover:bg-moss-50 transition-colors mt-1"
|
||||
>
|
||||
Route →
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex items-start justify-between gap-4 bg-cream/60 border border-ink/10 rounded-sm px-5 py-4">
|
||||
<div>
|
||||
<div className="eyebrow mb-1">Anfahrt</div>
|
||||
<div className="font-display text-lg leading-tight">Berlin → Vetschau</div>
|
||||
<div className="text-sm text-ink/65 mt-1">ca. 90 Min · A13 Richtung Cottbus</div>
|
||||
</div>
|
||||
<a
|
||||
href="https://www.google.com/maps/dir/?api=1&destination=51.7753,14.0966"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="shrink-0 text-xs text-moss-700 border border-moss-300 px-3 py-1.5 rounded-full hover:bg-moss-50 transition-colors mt-1"
|
||||
>
|
||||
Route →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* CTA */}
|
||||
<div className="mt-16 md:mt-20 flex flex-col md:flex-row items-start md:items-center justify-between gap-6 p-8 md:p-10 bg-moss-800 text-parchment rounded-sm">
|
||||
<div>
|
||||
<div className="eyebrow !text-parchment/60 mb-2">Bereit?</div>
|
||||
<h3 className="font-display text-2xl md:text-3xl leading-tight">
|
||||
Fragen Sie unverbindlich an —<br className="hidden md:block" />
|
||||
wir melden uns innerhalb von 24 Stunden.
|
||||
</h3>
|
||||
</div>
|
||||
<Link
|
||||
href="/anfrage"
|
||||
className="inline-flex items-center gap-2 bg-parchment text-ink px-7 py-3.5 rounded-full text-sm hover:bg-cream transition-colors shrink-0"
|
||||
>
|
||||
Zur Anfrage
|
||||
<span aria-hidden>→</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
228
components/home/PlacesToVisit.tsx
Normal file
228
components/home/PlacesToVisit.tsx
Normal file
@@ -0,0 +1,228 @@
|
||||
"use client";
|
||||
|
||||
import { useRef } from "react";
|
||||
|
||||
type Category = "Natur" | "Museum" | "Wellness" | "Erlebnis" | "Restaurant" | "Ausflug";
|
||||
|
||||
interface Place {
|
||||
name: string;
|
||||
category: Category;
|
||||
distance: string;
|
||||
description: string;
|
||||
mapsQuery: string;
|
||||
rating?: string;
|
||||
}
|
||||
|
||||
const PLACES: Place[] = [
|
||||
{
|
||||
name: "Spreewaldfahrt Familie Goertz",
|
||||
category: "Ausflug",
|
||||
distance: "12 km",
|
||||
description:
|
||||
"Traditionelle Kahnfahrt durch das UNESCO-Biosphärenreservat. Stille Fließe, Schilf und die Geschichte des Spreewalds — erzählt vom Kahnführer.",
|
||||
mapsQuery: "Spreewaldfahrt+Familie+Goertz+Lübbenau",
|
||||
rating: "4.7",
|
||||
},
|
||||
{
|
||||
name: "Spreewald Therme",
|
||||
category: "Wellness",
|
||||
distance: "15 km",
|
||||
description:
|
||||
"Entspannung nach einem Wandertag: große Saunalandschaft, Außenbecken und Ruhebereich mitten im Biosphärenreservat. Burg (Spreewald).",
|
||||
mapsQuery: "Spreewald+Therme+Burg",
|
||||
rating: "4.2",
|
||||
},
|
||||
{
|
||||
name: "Slawischer Burgwall Raddusch",
|
||||
category: "Museum",
|
||||
distance: "5 km",
|
||||
description:
|
||||
"Rekonstruierter Ringwall aus dem 9. Jahrhundert — das älteste sichtbare Baudenkmal der Region. Mit Aussichtsplattform über die Teiche.",
|
||||
mapsQuery: "Slawischer+Burgwall+Raddusch",
|
||||
rating: "4.4",
|
||||
},
|
||||
{
|
||||
name: "Freilandmuseum Lehde",
|
||||
category: "Museum",
|
||||
distance: "20 km",
|
||||
description:
|
||||
"Vier original erhaltene Spreewaldgehöfte aus dem 19. Jahrhundert — Einblick in das Leben der sorbischen Bevölkerung. Im Dorf Lehde, nur per Kahn oder Rad erreichbar.",
|
||||
mapsQuery: "Freilandmuseum+Lehde+Spreewald",
|
||||
rating: "4.5",
|
||||
},
|
||||
{
|
||||
name: "Gurkenradweg",
|
||||
category: "Natur",
|
||||
distance: "direkt",
|
||||
description:
|
||||
"250 km Radwegenetz durch Gurkenfelder, Wasserwege und Dörfer — das Herzstück des Spreewalds. Flach, gut beschildert, für jedes Tempo geeignet.",
|
||||
mapsQuery: "Gurkenradweg+Spreewald",
|
||||
},
|
||||
{
|
||||
name: "Tropical Islands",
|
||||
category: "Erlebnis",
|
||||
distance: "35 km",
|
||||
description:
|
||||
"Die größte Indoortropenwelt der Welt in einer umgebauten Luftschiffhalle. Strand, Wasserrutschen, Regenwald und Sauna unter einem Dach.",
|
||||
mapsQuery: "Tropical+Islands+Brand",
|
||||
rating: "4.3",
|
||||
},
|
||||
{
|
||||
name: "Schloss & Park Branitz",
|
||||
category: "Natur",
|
||||
distance: "28 km",
|
||||
description:
|
||||
"Das Lebenswerk des exzentrischen Fürsten Pückler-Muskau: ein englischer Landschaftspark mit einzigartigen Erdpyramiden und barockem Schloss in Cottbus.",
|
||||
mapsQuery: "Schloss+Branitz+Cottbus",
|
||||
rating: "4.6",
|
||||
},
|
||||
{
|
||||
name: "Spreewood Distillers",
|
||||
category: "Erlebnis",
|
||||
distance: "18 km",
|
||||
description:
|
||||
"Whisky, Gin und Liköre aus dem Biosphärenreservat. Führungen durch die Destillerie, Tastings und ein kleiner Shop direkt vor Ort in Schlepzig.",
|
||||
mapsQuery: "Spreewood+Distillers+Schlepzig",
|
||||
rating: "4.2",
|
||||
},
|
||||
{
|
||||
name: "Biberhof & Aquarium",
|
||||
category: "Natur",
|
||||
distance: "15 km",
|
||||
description:
|
||||
"Biber, Fischotter, Fischadler — die typischen Bewohner des Spreewalds zum Anfassen nah. Beliebtes Ausflugsziel für Familien in Raddusch.",
|
||||
mapsQuery: "Biberhof+Raddusch+Spreewald",
|
||||
rating: "4.2",
|
||||
},
|
||||
{
|
||||
name: "Bismarckturm Burg",
|
||||
category: "Ausflug",
|
||||
distance: "17 km",
|
||||
description:
|
||||
"Historischer Aussichtsturm auf einem der wenigen Hügel des Spreewalds. Bei klarem Wetter Panoramablick über das gesamte Biosphärenreservat.",
|
||||
mapsQuery: "Bismarckturm+Burg+Spreewald",
|
||||
rating: "3.6",
|
||||
},
|
||||
];
|
||||
|
||||
const categoryStyles: Record<Category, { bg: string; text: string; dot: string }> = {
|
||||
Natur: { bg: "bg-moss-50", text: "text-moss-700", dot: "bg-moss-500" },
|
||||
Museum: { bg: "bg-sand-50", text: "text-sand-700", dot: "bg-sand-500" },
|
||||
Wellness: { bg: "bg-blue-50", text: "text-blue-700", dot: "bg-blue-400" },
|
||||
Erlebnis: { bg: "bg-rose-50", text: "text-rose-700", dot: "bg-rose-400" },
|
||||
Restaurant:{ bg: "bg-orange-50", text: "text-orange-700", dot: "bg-orange-400" },
|
||||
Ausflug: { bg: "bg-teal-50", text: "text-teal-700", dot: "bg-teal-500" },
|
||||
};
|
||||
|
||||
export function PlacesToVisit() {
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const scroll = (dir: "left" | "right") => {
|
||||
const el = scrollRef.current;
|
||||
if (!el) return;
|
||||
el.scrollBy({ left: dir === "right" ? 320 : -320, behavior: "smooth" });
|
||||
};
|
||||
|
||||
return (
|
||||
<section id="ausflugsziele" className="py-20 md:py-28 border-t border-ink/10 scroll-mt-24">
|
||||
<div className="container">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-6 mb-10 md:mb-14">
|
||||
<div>
|
||||
<div className="eyebrow mb-4">Die Region</div>
|
||||
<h2 className="font-display text-display-lg max-w-2xl leading-[1.02]">
|
||||
Was Sie nicht verpassen{" "}
|
||||
<span className="italic text-moss-600">sollten.</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<p className="text-ink/60 max-w-xs text-sm md:text-base shrink-0 hidden md:block">
|
||||
Ausgewählte Highlights im Umkreis von 50 km.
|
||||
</p>
|
||||
{/* Arrow buttons */}
|
||||
<div className="flex gap-2 shrink-0">
|
||||
<button
|
||||
onClick={() => scroll("left")}
|
||||
aria-label="Zurück"
|
||||
className="h-9 w-9 rounded-full border border-ink/15 bg-cream flex items-center justify-center text-ink/50 hover:border-ink/30 hover:text-ink/80 transition-colors"
|
||||
>
|
||||
←
|
||||
</button>
|
||||
<button
|
||||
onClick={() => scroll("right")}
|
||||
aria-label="Weiter"
|
||||
className="h-9 w-9 rounded-full border border-ink/15 bg-cream flex items-center justify-center text-ink/50 hover:border-ink/30 hover:text-ink/80 transition-colors"
|
||||
>
|
||||
→
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Horizontal scroll container */}
|
||||
<div className="relative">
|
||||
{/* Fade edges */}
|
||||
<div className="pointer-events-none absolute left-0 top-0 bottom-4 w-8 bg-gradient-to-r from-parchment to-transparent z-10" />
|
||||
<div className="pointer-events-none absolute right-0 top-0 bottom-4 w-16 bg-gradient-to-l from-parchment to-transparent z-10" />
|
||||
|
||||
<div
|
||||
ref={scrollRef}
|
||||
className="flex gap-4 overflow-x-auto pb-4 scroll-smooth snap-x snap-mandatory"
|
||||
style={{ scrollbarWidth: "none", msOverflowStyle: "none" }}
|
||||
>
|
||||
{PLACES.map((place) => {
|
||||
const style = categoryStyles[place.category];
|
||||
const mapsUrl = `https://www.google.com/maps/search/?api=1&query=${place.mapsQuery}`;
|
||||
return (
|
||||
<a
|
||||
key={place.name}
|
||||
href={mapsUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group snap-start shrink-0 w-[280px] md:w-[300px] bg-cream border border-ink/10 rounded-sm p-5 md:p-6 flex flex-col hover:border-ink/25 hover:shadow-card transition-all duration-200"
|
||||
>
|
||||
{/* Top row: category + distance */}
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span className={`inline-flex items-center gap-1.5 text-[11px] font-medium uppercase tracking-[0.15em] px-2.5 py-1 rounded-full ${style.bg} ${style.text}`}>
|
||||
<span className={`h-1.5 w-1.5 rounded-full ${style.dot}`} aria-hidden />
|
||||
{place.category}
|
||||
</span>
|
||||
<span className="text-xs text-ink/45 tabular-nums">{place.distance}</span>
|
||||
</div>
|
||||
|
||||
{/* Name */}
|
||||
<h3 className="font-display text-xl leading-tight mb-3 group-hover:text-moss-700 transition-colors">
|
||||
{place.name}
|
||||
</h3>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-sm text-ink/70 leading-relaxed flex-1">
|
||||
{place.description}
|
||||
</p>
|
||||
|
||||
{/* Footer: rating + link hint */}
|
||||
<div className="mt-4 pt-4 border-t border-ink/8 flex items-center justify-between">
|
||||
{place.rating ? (
|
||||
<span className="text-xs text-ink/50 flex items-center gap-1">
|
||||
<span className="text-sand-500">★</span>
|
||||
{place.rating} auf TripAdvisor
|
||||
</span>
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
<span className="text-xs text-ink/40 group-hover:text-moss-600 transition-colors">
|
||||
Maps ↗
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="mt-3 text-xs text-ink/40">
|
||||
Alle Entfernungen ab Vetschau/Spreewald. Bewertungen via TripAdvisor.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
15
components/home/SpreeMap.tsx
Normal file
15
components/home/SpreeMap.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
export function SpreeMap() {
|
||||
return (
|
||||
<iframe
|
||||
src="https://maps.google.com/maps?q=Kraftwerkstra%C3%9Fe+10%2C+03226+Vetschau%2FSpreewald&z=15&output=embed"
|
||||
width="100%"
|
||||
height="300"
|
||||
className="md:!h-[440px]"
|
||||
style={{ border: 0, display: "block" }}
|
||||
allowFullScreen
|
||||
loading="lazy"
|
||||
referrerPolicy="no-referrer-when-downgrade"
|
||||
title="Spreewaldzeit – Kraftwerkstraße 10, Vetschau/Spreewald"
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user