diff --git a/app/(frontend)/aktuelles/page.tsx b/app/(frontend)/aktuelles/page.tsx index 638a5fb..f8dcce7 100644 --- a/app/(frontend)/aktuelles/page.tsx +++ b/app/(frontend)/aktuelles/page.tsx @@ -55,9 +55,10 @@ export default async function AktuellesPage() {
{posts.length === 0 ? ( -

- Hier erscheinen bald die ersten Neuigkeiten — schau gerne wieder vorbei. -

+
+

Hier erscheinen bald die ersten Neuigkeiten

+

Schau gerne wieder vorbei.

+
) : (
{posts.map((post, i) => ( diff --git a/app/(frontend)/angebote/page.tsx b/app/(frontend)/angebote/page.tsx index 5b28a14..2e7a73e 100644 --- a/app/(frontend)/angebote/page.tsx +++ b/app/(frontend)/angebote/page.tsx @@ -19,7 +19,7 @@ import { siteConfig } from "@/lib/site"; export const dynamic = "force-dynamic"; const FALLBACK_DESCRIPTION = - "Prozessbegleitung, Doula-Begleitung, Kindergruppen und Singkreise — die Angebote von Anouma im Überblick."; + "Prozessbegleitung, Doula-Begleitung, Kindergruppen und Singkreise – die Angebote von Anouma im Überblick."; export async function generateMetadata(): Promise { const [intro, seoSettings] = await Promise.all([getAngeboteIntroGlobal(), getSEOSettingsGlobal()]); @@ -170,7 +170,7 @@ export default async function AngebotePage() { ); diff --git a/app/(frontend)/angebote/singkreise/page.tsx b/app/(frontend)/angebote/singkreise/page.tsx index 94c9e5b..f9399ca 100644 --- a/app/(frontend)/angebote/singkreise/page.tsx +++ b/app/(frontend)/angebote/singkreise/page.tsx @@ -53,7 +53,7 @@ export default async function SingkreisePage() { ); diff --git a/app/(frontend)/auth/verify-email/[token]/page.tsx b/app/(frontend)/auth/verify-email/[token]/page.tsx index 363631f..8a7b052 100644 --- a/app/(frontend)/auth/verify-email/[token]/page.tsx +++ b/app/(frontend)/auth/verify-email/[token]/page.tsx @@ -21,6 +21,7 @@ export default async function VerifyEmailPage({ params }: Args) { eyebrow="Konto" title={verified ? "E-Mail-Adresse bestätigt" : "Bestätigung fehlgeschlagen"} crumbs={[{ title: "E-Mail bestätigen" }]} + compact />
diff --git a/app/(frontend)/datenschutz/page.tsx b/app/(frontend)/datenschutz/page.tsx index 79499fc..0395a68 100644 --- a/app/(frontend)/datenschutz/page.tsx +++ b/app/(frontend)/datenschutz/page.tsx @@ -21,7 +21,7 @@ export default async function DatenschutzPage() { const contact = await getContactGlobal(); return ( <> - +
@@ -33,9 +33,9 @@ export default async function DatenschutzPage() {

Verantwortliche Stelle

- [Platzhalter — Vor- und Nachname] + [Platzhalter – Vor- und Nachname]
- [Platzhalter — Anschrift] + [Platzhalter – Anschrift]
E-Mail: {contact.email}

@@ -48,7 +48,7 @@ export default async function DatenschutzPage() {

Beim Besuch dieser Website werden aus technischen Gründen automatisch Informationen erfasst, die dein Browser übermittelt (z. B. IP-Adresse, Datum und Uhrzeit des - Zugriffs). [Platzhalter — Details zum verwendeten Hosting-Anbieter ergänzen.] + Zugriffs). [Platzhalter – Details zum verwendeten Hosting-Anbieter ergänzen.]

diff --git a/app/(frontend)/impressionen/page.tsx b/app/(frontend)/impressionen/page.tsx index 78619e5..db12bf8 100644 --- a/app/(frontend)/impressionen/page.tsx +++ b/app/(frontend)/impressionen/page.tsx @@ -10,7 +10,7 @@ import { JsonLd, webPageJsonLd } from "@/lib/seo/jsonld"; export const dynamic = "force-dynamic"; -const DESCRIPTION = "Bildeindrücke aus der Arbeit von Anouma — Natur, Gemeinschaft und gemeinsame Räume."; +const DESCRIPTION = "Bildeindrücke aus der Arbeit von Anouma – Natur, Gemeinschaft und gemeinsame Räume."; export async function generateMetadata(): Promise { return buildMetadata({ title: "Impressionen", description: DESCRIPTION, path: "/impressionen" }); @@ -36,11 +36,11 @@ export default async function ImpressionenPage() {
-
+
{gallery.map((item, i) => ( diff --git a/app/(frontend)/impressum/page.tsx b/app/(frontend)/impressum/page.tsx index 202f994..23b8459 100644 --- a/app/(frontend)/impressum/page.tsx +++ b/app/(frontend)/impressum/page.tsx @@ -23,7 +23,7 @@ export default async function ImpressumPage() { const contact = await getContactGlobal(); return ( <> - +
@@ -35,11 +35,11 @@ export default async function ImpressumPage() {

Angaben gemäß § 5 TMG

- [Platzhalter — Vor- und Nachname] + [Platzhalter – Vor- und Nachname]
- [Platzhalter — Straße und Hausnummer] + [Platzhalter – Straße und Hausnummer]
- [Platzhalter — PLZ und Ort] + [Platzhalter – PLZ und Ort]

@@ -61,7 +61,7 @@ export default async function ImpressumPage() { Umsatzsteuer-Identifikationsnummer

- [Platzhalter — sofern vorhanden, gemäß § 27 a Umsatzsteuergesetz] + [Platzhalter – sofern vorhanden, gemäß § 27 a Umsatzsteuergesetz]

@@ -70,7 +70,7 @@ export default async function ImpressumPage() { Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV

- [Platzhalter — Vor- und Nachname, Anschrift wie oben] + [Platzhalter – Vor- und Nachname, Anschrift wie oben]

diff --git a/app/(frontend)/kontakt/page.tsx b/app/(frontend)/kontakt/page.tsx index 8455b95..21758eb 100644 --- a/app/(frontend)/kontakt/page.tsx +++ b/app/(frontend)/kontakt/page.tsx @@ -50,6 +50,9 @@ export default async function KontaktPage() { title={contact.title || "Ich freue mich, von dir zu hören"} lead={contact.lead} crumbs={[{ title: "Kontakt" }]} + blobTone="sage" + blobPosition="top-left" + compact />
diff --git a/app/(frontend)/konto/layout.tsx b/app/(frontend)/konto/layout.tsx index 0056fc4..f3838a1 100644 --- a/app/(frontend)/konto/layout.tsx +++ b/app/(frontend)/konto/layout.tsx @@ -44,12 +44,12 @@ export default async function KontoLayout({ children }: { children: ReactNode }) {item.title} ))} - +
{children}
diff --git a/app/(frontend)/konto/page.tsx b/app/(frontend)/konto/page.tsx index ae6ccd6..ffed82e 100644 --- a/app/(frontend)/konto/page.tsx +++ b/app/(frontend)/konto/page.tsx @@ -1,8 +1,8 @@ import type { Metadata } from "next"; -import Link from "next/link"; import { getCurrentCustomer } from "@/lib/auth/customer"; import { getCustomerBookings } from "@/lib/booking/queries"; import { BookingCard } from "@/components/booking/BookingCard"; +import { ArrowLink } from "@/components/ArrowLink"; export const metadata: Metadata = { title: "Mein Konto" }; @@ -61,9 +61,7 @@ export default async function KontoOverviewPage() { {past.length > 0 && (
- - Alle Termine (inkl. vergangene) ansehen → - + Alle Termine (inkl. vergangene) ansehen
)}
diff --git a/app/(frontend)/konto/profil/page.tsx b/app/(frontend)/konto/profil/page.tsx index 7df4662..cbbec28 100644 --- a/app/(frontend)/konto/profil/page.tsx +++ b/app/(frontend)/konto/profil/page.tsx @@ -14,7 +14,7 @@ export default async function KontoProfilPage() {

- E-Mail: {customer.email} — für eine Änderung der E-Mail-Adresse melde dich bitte direkt bei Anna. + E-Mail: {customer.email} – für eine Änderung der E-Mail-Adresse melde dich bitte direkt bei Anna.

diff --git a/app/(frontend)/login/page.tsx b/app/(frontend)/login/page.tsx index a9c3566..a2fae54 100644 --- a/app/(frontend)/login/page.tsx +++ b/app/(frontend)/login/page.tsx @@ -20,7 +20,7 @@ export default async function LoginPage() { return ( <> - +
diff --git a/app/(frontend)/page.tsx b/app/(frontend)/page.tsx index 9020654..0dedc80 100644 --- a/app/(frontend)/page.tsx +++ b/app/(frontend)/page.tsx @@ -2,9 +2,11 @@ import type { Metadata } from "next"; import Link from "next/link"; import { Hero } from "@/components/Hero"; import { Button } from "@/components/Button"; +import { ArrowLink } from "@/components/ArrowLink"; import { Section, SectionHeading } from "@/components/Section"; import { AngebotCard } from "@/components/AngebotCard"; import { ImagePlaceholder } from "@/components/ImagePlaceholder"; +import { OrganicBlob } from "@/components/OrganicBlob"; import { RichText } from "@/components/RichText"; import { CTA } from "@/components/CTA"; import { Reveal } from "@/components/Reveal"; @@ -29,7 +31,7 @@ export async function generateMetadata(): Promise { fallbackDescription: () => home.heroSupporting || seoSettings.defaultDescription || - "Prozessbegleitung, Doula-Begleitung, Kindergruppen und Singkreise — Räume für Verbindung mit dir selbst, miteinander und mit der Natur.", + "Prozessbegleitung, Doula-Begleitung, Kindergruppen und Singkreise – Räume für Verbindung mit dir selbst, miteinander und mit der Natur.", fallbackImage: home.heroImage, defaultOgImage: seoSettings.defaultOgImage, }); @@ -53,6 +55,8 @@ export default async function Home() { getUpcomingEvents(3), ]); const groups = groupOffersByCategory(offers); + const featuredGroup = groups.find((g) => g.category === "prozessbegleitung"); + const remainingGroups = featuredGroup ? groups.filter((g) => g !== featuredGroup) : groups; const siteUrl = await getSiteUrl(); return ( @@ -73,9 +77,9 @@ export default async function Home() { actions={ <> - + } > @@ -89,24 +93,39 @@ export default async function Home() { -
- {groups.map((group, i) => { - const isSingle = group.offers.length === 1; - const first = group.offers[0]; - return ( - - - - ); - })} +
+ {/* Prozessbegleitung ist das Kernangebot – bekommt deshalb den größeren, ersten Platz statt eines gleichwertigen 4er-Rasters. */} + {featuredGroup && ( + + + + )} +
+ {remainingGroups.map((group, i) => { + const isSingle = group.offers.length === 1; + const first = group.offers[0]; + return ( + + + + ); + })} +
@@ -141,9 +160,7 @@ export default async function Home() {
)}
- + Mehr über mich lesen
@@ -151,24 +168,27 @@ export default async function Home() { )} {home.values && home.values.length > 0 && ( -
- -
- {home.values.map((wert, i) => ( - -

- {wert.title} -

-

- „{wert.quote}“ -

-
- ))} +
+ +
+ +
+ {home.values.map((wert, i) => ( + +

+ {wert.title} +

+

+ „{wert.quote}“ +

+
+ ))} +
)} @@ -177,9 +197,7 @@ export default async function Home() {
- + Alle Termine
{events.map((event, i) => ( @@ -195,23 +213,31 @@ export default async function Home() {
- + Alle Neuigkeiten
{posts.map((post, i) => ( - -

+ +

+ +
+

{new Date(post.publishDate ?? post.createdAt).toLocaleDateString("de-DE", { day: "2-digit", month: "long", year: "numeric", })}

-

{post.title}

-

{post.teaser}

+

{post.title}

+

{post.teaser}

))} @@ -228,13 +254,8 @@ export default async function Home() { ))}
-
- - Alle Impressionen ansehen - +
+ Alle Impressionen ansehen
@@ -242,7 +263,7 @@ export default async function Home() { title={home.ctaTitle || "Kennenlernen & Termine vereinbaren"} lead={ home.ctaLead || - "Ich freue mich, von dir zu hören — schreib mir oder buche direkt ein unverbindliches Kennenlerngespräch." + "Ich freue mich, von dir zu hören – schreib mir oder buche direkt ein unverbindliches Kennenlerngespräch." } /> diff --git a/app/(frontend)/registrieren/page.tsx b/app/(frontend)/registrieren/page.tsx index 3c09ef2..e92909a 100644 --- a/app/(frontend)/registrieren/page.tsx +++ b/app/(frontend)/registrieren/page.tsx @@ -24,6 +24,7 @@ export default async function RegisterPage() { title="Konto erstellen" lead="Mit einem Konto kannst du Termine anfragen und behältst alle deine Buchungen im Blick." crumbs={[{ title: "Konto erstellen" }]} + compact />
diff --git a/app/(frontend)/termin-buchen/page.tsx b/app/(frontend)/termin-buchen/page.tsx index a8e4f14..750f5ee 100644 --- a/app/(frontend)/termin-buchen/page.tsx +++ b/app/(frontend)/termin-buchen/page.tsx @@ -98,7 +98,7 @@ export default async function TerminBuchenPage() { title="Allgemeine Anfrage" lead={ bookableOffers.length > 0 - ? "Für einen konkreten Termin wähle oben ein Angebot — hier kannst du auch einfach eine allgemeine Nachricht schreiben." + ? "Für einen konkreten Termin wähle oben ein Angebot – hier kannst du auch einfach eine allgemeine Nachricht schreiben." : undefined } align="left" diff --git a/app/(frontend)/termine/[slug]/page.tsx b/app/(frontend)/termine/[slug]/page.tsx index 02bcadf..1f61041 100644 --- a/app/(frontend)/termine/[slug]/page.tsx +++ b/app/(frontend)/termine/[slug]/page.tsx @@ -126,6 +126,8 @@ export default async function EventDetailPage({ params }: Args) { eyebrow={categoryLabel} title={event.title} crumbs={[{ title: "Termine", href: "/termine" }, { title: event.title }]} + blobTone="mauve" + blobPosition="top-left" />
diff --git a/app/(frontend)/termine/page.tsx b/app/(frontend)/termine/page.tsx index f94edd8..6f86114 100644 --- a/app/(frontend)/termine/page.tsx +++ b/app/(frontend)/termine/page.tsx @@ -3,6 +3,7 @@ import { PageHeader } from "@/components/PageHeader"; import { Section } from "@/components/Section"; import { Reveal } from "@/components/Reveal"; import { EventTeaserCard } from "@/components/EventTeaserCard"; +import { ArrowLink } from "@/components/ArrowLink"; import { getAllEvents } from "@/lib/payload/content"; import { buildMetadata } from "@/lib/seo/metadata"; import { getSiteUrl } from "@/lib/seo/config"; @@ -32,14 +33,21 @@ export default async function TerminePage() { title="Aktuelle Termine" lead="Ein Überblick über anstehende Kreise, Begleitungen und Veranstaltungen." crumbs={[{ title: "Termine" }]} + blobTone="mauve" + blobPosition="top-left" />
{upcoming.length === 0 ? ( -

- Aktuell sind keine Termine geplant — schau gerne bald wieder vorbei oder melde dich - direkt über die Kontaktseite. -

+
+

Aktuell sind keine Termine geplant

+

+ Schau gerne bald wieder vorbei, oder melde dich direkt – ich freue mich von dir zu hören. +

+
+ Zur Kontaktseite +
+
) : (
{upcoming.map((event, i) => ( diff --git a/components/AngebotCard.tsx b/components/AngebotCard.tsx index 6e0c59b..a8de20d 100644 --- a/components/AngebotCard.tsx +++ b/components/AngebotCard.tsx @@ -1,5 +1,6 @@ import Link from "next/link"; import { ImagePlaceholder } from "@/components/ImagePlaceholder"; +import { ArrowIcon } from "@/components/ArrowIcon"; import type { ImageMood } from "@/lib/angebote"; type AngebotCardProps = { @@ -42,9 +43,7 @@ export function AngebotCard({

{tagline}

Mehr erfahren - +
diff --git a/components/ArrowIcon.tsx b/components/ArrowIcon.tsx new file mode 100644 index 0000000..e9cabbe --- /dev/null +++ b/components/ArrowIcon.tsx @@ -0,0 +1,15 @@ +/** The small forward-nudging arrow shared by ArrowLink and the "Mehr erfahren" affordance inside AngebotCard/EventTeaserCard (which can't use ArrowLink directly since those are already whole-tile links, and nesting an anchor inside an anchor is invalid). Purely visual — animates on the parent's `group-hover`. */ +export function ArrowIcon({ className = "" }: { className?: string }) { + return ( + + ); +} diff --git a/components/ArrowLink.tsx b/components/ArrowLink.tsx new file mode 100644 index 0000000..020169a --- /dev/null +++ b/components/ArrowLink.tsx @@ -0,0 +1,43 @@ +import Link from "next/link"; +import type { ReactNode } from "react"; +import { ArrowIcon } from "@/components/ArrowIcon"; + +type Tone = "mauve" | "plum" | "white"; +type Size = "sm" | "base"; + +const tones: Record = { + mauve: "text-anouma-mauve-dark", + plum: "text-anouma-plum", + white: "text-white", +}; + +const sizes: Record = { + sm: "gap-1.5 text-sm", + base: "gap-2 text-base", +}; + +type ArrowLinkProps = { + href: string; + children: ReactNode; + tone?: Tone; + size?: Size; + className?: string; +}; + +/** + * The site's one secondary-action pattern: a text link with a small arrow + * that nudges forward on hover. Used instead of a pill button wherever the + * action isn't the single primary one on screen — keeps most CTAs feeling + * like an invitation to read on rather than another button to click. + */ +export function ArrowLink({ href, children, tone = "mauve", size = "sm", className = "" }: ArrowLinkProps) { + return ( + + {children} + + + ); +} diff --git a/components/ContactForm.tsx b/components/ContactForm.tsx index 8eee741..d990bdc 100644 --- a/components/ContactForm.tsx +++ b/components/ContactForm.tsx @@ -1,9 +1,8 @@ "use client"; import { useState, type FormEvent } from "react"; - -const fieldClass = - "w-full rounded-2xl border border-anouma-taupe/30 bg-background px-5 py-3.5 text-base text-anouma-plum placeholder:text-anouma-plum/50 focus:border-anouma-mauve-dark focus:outline-none"; +import { track } from "@/lib/analytics/client"; +import { TextField, TextAreaField } from "@/components/FormField"; type ContactFormProps = { toEmail: string; @@ -31,31 +30,19 @@ export function ContactForm({ const message = String(data.get("message") ?? ""); const subject = encodeURIComponent(`${subjectPrefix} von ${name}`); - const body = encodeURIComponent(`${message}\n\n— ${name} (${email})`); + const body = encodeURIComponent(`${message}\n\n– ${name} (${email})`); window.location.href = `mailto:${toEmail}?subject=${subject}&body=${body}`; + // Event only — never the form's name/email/message content, see + // lib/analytics/events.ts and the data-minimization requirement. + track("contact_clicked"); setSent(true); } return (
-
- - -
-
- - -
-
- -