Design-Verbesserungen: Copy, Layout, Karte & Social-Proof

- Hero: Sub-Headline und CTA-Text niedrigschwelliger formuliert
- Services: Konkretere, ergebnisorientierte Beschreibungen
- Neue About-Section mit persönlichem Pull-Quote
- Neue Process-Section als 4-Spalten-Grid (statt Services-Klon)
- Packages: Feature-Listen, Profi-Paket hervorgehoben, neue Texte
- Contact: Infos links, interaktive mapcn-Karte rechts (Crimmitschau)
- Karte rot eingefärbt via sepia/hue-rotate CSS-Filter
- Nav um "Ablauf"-Link ergänzt, fehlende IDs gesetzt
- Kleinere Copy-Fixes (Opacity, leerer Span, Region konkretisiert)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Matthias
2026-05-18 18:12:25 +02:00
parent 8a4ec60655
commit cee5f470ad
14 changed files with 2294 additions and 58 deletions

View File

@@ -62,6 +62,7 @@ const LandingHeroSection = () => {
</a>
<nav className="flex flex-wrap gap-5">
<a href="#leistungen">Leistungen</a>
<a href="#ablauf">Ablauf</a>
<a href="#pakete">Pakete</a>
<a href="#kontakt">Kontakt</a>
</nav>
@@ -76,9 +77,9 @@ const LandingHeroSection = () => {
</h1>
<div className="mt-8 grid gap-7 border-t border-border pt-7 lg:grid-cols-[0.72fr_1fr]">
<p className="text-sm uppercase tracking-[0.24em] text-muted-foreground">
Strategie trifft Umsetzung
Ihr Betrieb im Netz ohne Stress
</p>
<p className="max-w-2xl text-lg leading-8 text-foreground/78">
<p className="max-w-2xl text-lg leading-8 text-foreground/80">
Ich baue Websites für Handwerk, Praxen, Salons und
Dienstleister aus der Region klar genug, dass Besucher
anrufen statt weiterklicken.
@@ -118,7 +119,7 @@ const LandingHeroSection = () => {
<div className="relative z-10 flex min-h-[520px] flex-1 flex-col lg:min-h-0">
<div className="flex shrink-0 items-start justify-between border-b border-primary-foreground/30 pb-5 text-xs uppercase tracking-[0.28em]">
<span></span>
<span className="text-primary-foreground/60">Webdesign</span>
<span>&copy;2026</span>
</div>
@@ -177,7 +178,7 @@ const LandingHeroSection = () => {
href="#kontakt"
className="group relative z-10 inline-flex w-fit shrink-0 items-center gap-3 border border-primary-foreground px-5 py-4 text-sm font-semibold uppercase tracking-[0.18em] transition hover:bg-primary-foreground hover:text-primary"
>
Projekt anfragen
Unverbindlich anfragen
<ArrowUpRight className="size-5 transition group-hover:-translate-y-0.5 group-hover:translate-x-0.5" />
</a>
</div>

View File

@@ -1,15 +1,15 @@
import { ContactSection } from "@/components/landing/contact-section";
import { DeliverablesSection } from "@/components/landing/deliverables-section";
import { AboutSection } from "@/components/landing/about-section";
import { PackagesSection } from "@/components/landing/packages-section";
import { ProcessSection } from "@/components/landing/process-section";
import { ServicesSection } from "@/components/landing/services-section";
const LandingPageSections = () => {
return (
<>
<ServicesSection />
<DeliverablesSection />
<AboutSection />
<ProcessSection />
<PackagesSection />
<ContactSection />
</>
);
};

View File

@@ -0,0 +1,36 @@
const AboutSection = () => {
return (
<section
id="ueber"
className="grid border-b border-border lg:grid-cols-[0.36fr_0.64fr]"
>
<div className="border-b border-border bg-foreground px-5 py-12 text-background sm:px-8 lg:border-b-0 lg:border-r lg:px-12 lg:py-20">
<p className="text-sm uppercase tracking-[0.3em] text-primary">
Über mich (03)
</p>
<h2 className="mt-6 max-w-[12ch] text-5xl font-black uppercase leading-[0.86] sm:text-7xl">
Ein Mensch. Kein Ticketsystem.
</h2>
</div>
<div className="flex flex-col justify-center gap-8 px-5 py-12 sm:px-8 lg:px-12 lg:py-20">
<p className="max-w-xl text-[clamp(1.5rem,3vw,2rem)] font-black uppercase leading-[0.95] text-foreground">
Ich bin Matthias Webdesigner aus Sachsen.
</p>
<div className="flex flex-col gap-5">
<p className="max-w-2xl text-lg leading-8 text-muted-foreground">
Ich baue Websites für Betriebe, die ihre Energie lieber in Kunden
stecken als in Technik. Kein Großraumbüro, kein Agentur-Overhead
ein Ansprechpartner von der ersten Idee bis zum Go-live.
</p>
<p className="max-w-2xl text-lg leading-8 text-muted-foreground">
Meine Kunden sind Handwerker, Praxen, Salons und Dienstleister aus
der Region. Menschen, die eine Website wollen, die funktioniert
und sich dann wieder um ihren Betrieb kümmern möchten.
</p>
</div>
</div>
</section>
);
};
export { AboutSection };

View File

@@ -1,43 +1,65 @@
"use client";
import { CornerDownRight, Mail, MapPin, Phone } from "lucide-react";
import { Map, MapControls, MapMarker, MarkerContent } from "@/components/ui/map";
/** Karl-Marx-Str. 22, 08451 Crimmitschau (OpenStreetMap) */
const OFFICE: [number, number] = [12.3829769, 50.8131218];
const ContactSection = () => {
return (
<section
id="kontakt"
className="grid min-h-[620px] lg:grid-cols-[0.72fr_0.28fr]"
>
<div className="px-5 py-14 sm:px-8 lg:px-12 lg:py-24">
<p className="text-sm uppercase tracking-[0.3em] text-primary">
Kontakt (05)
</p>
<h2 className="mt-8 max-w-[12ch] text-5xl font-black uppercase leading-[0.86] sm:text-7xl lg:text-8xl">
Erzählen Sie mir kurz von Ihrem Betrieb
</h2>
<p className="mt-8 max-w-2xl text-xl leading-8 text-muted-foreground">
Ein paar Sätze reichen: Was bieten Sie an, was soll die Website für
Sie tun, und wann soll sie online sein?
</p>
<a
href="mailto:support@matthias-meister-webdesign.de"
className="mt-10 inline-flex items-center gap-3 bg-primary px-6 py-5 text-sm font-black uppercase tracking-[0.18em] text-primary-foreground transition hover:bg-foreground hover:text-background"
>
<CornerDownRight className="size-5" />
Anfrage per Mail senden
</a>
<section id="kontakt" className="grid min-h-[620px] lg:grid-cols-2">
<div className="flex flex-col justify-between px-5 py-14 sm:px-8 lg:px-12 lg:py-24">
<div>
<p className="text-sm uppercase tracking-[0.3em] text-primary">
Kontakt (06)
</p>
<h2 className="mt-8 max-w-[12ch] text-5xl font-black uppercase leading-[0.86] sm:text-7xl lg:text-8xl">
Erzählen Sie mir kurz von Ihrem Betrieb
</h2>
<p className="mt-8 max-w-2xl text-xl leading-8 text-muted-foreground">
Ein paar Sätze reichen: Was bieten Sie an, was soll die Website für
Sie tun, und wann soll sie online sein?
</p>
<a
href="mailto:support@matthias-meister-webdesign.de"
className="mt-10 inline-flex items-center gap-3 bg-primary px-6 py-5 text-sm font-black uppercase tracking-[0.18em] text-primary-foreground transition hover:bg-foreground hover:text-background"
>
<CornerDownRight className="size-5" />
Kurze Nachricht schreiben
</a>
</div>
<div className="mt-12 flex flex-col gap-5 border-t border-border pt-8 text-sm text-muted-foreground">
<div className="flex gap-3">
<Mail className="size-4 shrink-0 text-primary" />
<span>support@matthias-meister-webdesign.de</span>
</div>
<div className="flex gap-3">
<Phone className="size-4 shrink-0 text-primary" />
<span>Rückmeldung innerhalb von 24 Stunden</span>
</div>
<div className="flex gap-3">
<MapPin className="size-4 shrink-0 text-primary" />
<span>Karl-Marx-Str. 22, 08451 Crimmitschau</span>
</div>
</div>
</div>
<div className="flex flex-col justify-end gap-6 bg-primary px-5 py-10 text-primary-foreground sm:px-8 lg:px-10">
<div className="flex gap-3">
<Mail className="size-5" />
<span>support@matthias-meister-webdesign.de</span>
</div>
<div className="flex gap-3">
<Phone className="size-5" />
<span>Rückmeldung innerhalb von 24 Stunden</span>
</div>
<div className="flex gap-3">
<MapPin className="size-5" />
<span>Betriebe aus der Region</span>
</div>
<div className="contact-map relative min-h-[400px] bg-primary lg:min-h-0">
<Map
center={OFFICE}
zoom={15}
theme="dark"
className="h-full w-full"
>
<MapControls position="bottom-right" showZoom />
<MapMarker longitude={OFFICE[0]} latitude={OFFICE[1]}>
<MarkerContent>
<div className="size-5 rounded-full border-[3px] border-primary-foreground bg-primary shadow-lg ring-4 ring-primary-foreground/50" />
</MarkerContent>
</MapMarker>
</Map>
</div>
</section>
);

View File

@@ -1,18 +1,51 @@
import { Check } from "lucide-react";
const packages = [
{
name: "Basis",
price: "799 EUR",
detail: "Eine starke Seite für ein klares Angebot.",
highlighted: false,
features: [
"One-Page-Website",
"Mobil optimiert",
"Kontaktformular",
"DSGVO & Impressum",
"Hosting für 1 Jahr",
],
},
{
name: "Profi",
price: "1.499 EUR",
detail: "Mehrere Seiten für Betriebe mit mehr zu zeigen.",
highlighted: true,
features: [
"Alles aus Basis",
"Bis zu 5 Unterseiten",
"Individuelles Design",
"SEO-Grundoptimierung",
"Google Maps Einbindung",
"Kontaktformular",
"DSGVO & Impressum",
"Hosting für 1 Jahr",
],
},
{
name: "Maßarbeit",
price: "2.499 EUR+",
detail: "Eigene Struktur, selbst pflegbar, für besondere Anforderungen.",
detail: "Ihr Betrieb, Ihre Regeln. Inhalte selbst ändern, Seiten ergänzen, wachsen.",
highlighted: false,
features: [
"Alles aus Profi",
"Unbegrenzte Seiten",
"Inhalte selbst pflegbar (CMS)",
"Individuelles Design & Struktur",
"SEO-Optimierung",
"Blog oder News-Bereich",
"Erweiterte Funktionen",
"DSGVO & Impressum",
"Hosting für 1 Jahr",
],
},
];
@@ -25,29 +58,47 @@ const PackagesSection = () => {
<div className="grid gap-8 lg:grid-cols-[0.45fr_0.55fr]">
<div>
<p className="text-sm uppercase tracking-[0.3em] text-primary">
Pakete (04)
Pakete (05)
</p>
<h2 className="mt-8 max-w-[9ch] text-5xl font-black uppercase leading-[0.86] sm:text-7xl">
Kosten ohne Nebel
Festpreis. Punkt.
</h2>
</div>
<div className="grid gap-4">
{packages.map((item) => (
<article
key={item.name}
className="grid gap-4 border border-border p-5 sm:grid-cols-[0.5fr_0.5fr] sm:p-6"
className={`grid gap-6 border p-5 sm:grid-cols-[0.5fr_0.5fr] sm:p-6 ${item.highlighted ? "border-primary bg-primary/6" : "border-border"}`}
>
<div>
<p className="text-sm uppercase tracking-[0.24em] text-primary">
{item.name}
</p>
<div className="flex items-center gap-3">
<p className="text-sm uppercase tracking-[0.24em] text-primary">
{item.name}
</p>
{item.highlighted ? (
<span className="bg-primary px-2 py-0.5 text-[10px] font-bold uppercase tracking-[0.2em] text-primary-foreground">
Beliebteste Wahl
</span>
) : null}
</div>
<p className="mt-4 text-4xl font-black uppercase">
{item.price}
</p>
<p className="mt-3 text-lg leading-7 text-muted-foreground">
{item.detail}
</p>
</div>
<p className="self-end text-lg leading-7 text-muted-foreground">
{item.detail}
</p>
<ul className="flex flex-col gap-2.5 self-center">
{item.features.map((feature) => (
<li
key={feature}
className="flex items-center gap-3 text-sm text-foreground/85"
>
<Check className="size-4 shrink-0 text-primary" />
<span>{feature}</span>
</li>
))}
</ul>
</article>
))}
</div>

View File

@@ -0,0 +1,61 @@
const steps = [
{
number: "01",
title: "Gespräch",
text: "15 Minuten telefonieren. Sie erzählen, ich höre zu.",
},
{
number: "02",
title: "Konzept",
text: "Seitenstruktur und Design-Vorschlag innerhalb einer Woche.",
},
{
number: "03",
title: "Umsetzung",
text: "Fertige Website in 24 Wochen. Feedback, Anpassung, fertig.",
},
{
number: "04",
title: "Online",
text: "Ich schalte live, richte Hosting ein, kümmere mich um den Rest.",
},
];
const ProcessSection = () => {
return (
<section
id="ablauf"
className="border-b border-border px-5 py-14 sm:px-8 lg:px-12 lg:py-24"
>
<div className="mb-12">
<p className="text-sm uppercase tracking-[0.3em] text-primary">
Ablauf (04)
</p>
<h2 className="mt-6 max-w-[9ch] text-5xl font-black uppercase leading-[0.86] sm:text-7xl">
Vier Schritte. Fertig.
</h2>
</div>
<div className="grid gap-0 sm:grid-cols-2 lg:grid-cols-4">
{steps.map((step, i) => (
<article
key={step.number}
className={`relative flex flex-col gap-4 border-t border-border py-8 pr-6 lg:border-t-0 lg:border-l lg:py-0 lg:pl-8 lg:pr-10 ${i === 0 ? "lg:border-l-0 lg:pl-0" : ""}`}
>
<span className="text-6xl font-black leading-none text-primary/25">
{step.number}
</span>
<h3 className="text-xl font-black uppercase leading-none">
{step.title}
</h3>
<p className="max-w-xs text-base leading-7 text-muted-foreground">
{step.text}
</p>
</article>
))}
</div>
</section>
);
};
export { ProcessSection };

View File

@@ -2,17 +2,17 @@ const services = [
{
number: "01",
title: "Website",
text: "Eine klare Startseite oder ein kompletter Auftritt, der sofort zeigt, warum man Ihnen vertrauen kann.",
text: "Vom Elektriker bis zur Physiotherapie — eine Seite, die in drei Sekunden zeigt, was Sie machen und wie man Sie erreicht.",
},
{
number: "02",
title: "Struktur",
text: "Angebot, Referenzen, Ablauf und Kontakt werden so sortiert, dass Besucher nicht suchen müssen.",
text: "Angebot, Leistungen, Ablauf und Kontakt — alles dort, wo Besucher es erwarten. Damit aus Klicks Anrufe werden.",
},
{
number: "03",
title: "Technik",
text: "Schnell, mobil sauber, DSGVO-sauber und so gebaut, dass spätere Änderungen nicht zum Projekt werden.",
text: "Lädt in unter zwei Sekunden, sieht auf jedem Handy gut aus und ist rechtssicher. Änderungen später? Ein Anruf genügt.",
},
];

1844
src/components/ui/map.tsx Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
---
import { LandingHeroSection } from "@/components/landing-hero-section";
import { LandingPageSections } from "@/components/landing-page-sections";
import { ContactSection } from "@/components/landing/contact-section";
import { Footer27 } from "@/components/footer27";
import "@/styles/global.css";
---
@@ -22,6 +23,7 @@ import "@/styles/global.css";
<main class="min-h-screen overflow-hidden bg-background text-foreground">
<LandingHeroSection client:media="(min-width: 1024px)" />
<LandingPageSections />
<ContactSection client:visible />
<Footer27 />
</main>
<script>

View File

@@ -129,4 +129,31 @@
@apply font-sans;
scroll-behavior: smooth;
}
}
.maplibregl-popup-content {
@apply bg-transparent! shadow-none! p-0! rounded-none!;
}
.maplibregl-popup-tip {
@apply hidden!;
}
.contact-map .maplibregl-canvas {
/* Dunkle Carto-Tiles: Sepia kippt alles in Braun, hue-rotate(325deg)
schiebt Braun (~38°) auf Rot (~3°). Helle Straßen/Labels werden
zu hellem Rot, der dunkle Hintergrund zu dunklem Rot. */
filter: sepia(1) hue-rotate(325deg) saturate(4.5) brightness(0.82);
}
.contact-map .maplibregl-ctrl-attrib {
background: oklch(0.115 0.012 22 / 70%) !important;
}
.contact-map .maplibregl-ctrl-attrib a {
color: oklch(0.985 0.01 76 / 80%) !important;
}
.contact-map .maplibregl-ctrl-group {
background: transparent !important;
box-shadow: none !important;
}
.contact-map .maplibregl-ctrl-group button {
background: oklch(0.115 0.012 22 / 80%) !important;
color: oklch(0.985 0.01 76) !important;
border-color: oklch(0.985 0.01 76 / 20%) !important;
}
}