feat(ui): Startseite mit eigenstaendigen Abschnitten statt Schablonen-Zickzack

- Aktuelle Ernte: bei ein, zwei Produkten steht die Ueberschrift als
  eigene Spalte daneben, statt ueber einem halb leeren Dreierraster.
- Herkunft: Foto randlos ueber die halbe Seitenbreite, Text daneben,
  ohne Rahmen.
- Philosophie: typografische Reihe mit Trennlinien und grossen
  Serif-Titeln statt vier Icon-Karten.
- Region: breites Bild, der Text liegt als Tafel ueberlappend darueber.
- Geschaeftskunden: kompakte Tafel mit randlosem Bild statt eines dritten
  grossen Bild/Text-Wechsels.

Inhalte, Texte und Content-Schluessel bleiben unveraendert.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-26 01:45:06 +02:00
1 parent 1fa8ca246a
commit 7e467a9ddf
2 files changed
+85 -81

No files matched your search

+48 -44
View File
@@ -1,4 +1,4 @@
import { Container, SectionHeading, LinkButton, FramedMedia } from "@/components/ui"; import { Container, SectionHeading, LinkButton } from "@/components/ui";
import { HoneycombPattern, HexMark } from "@/components/honeycomb"; import { HoneycombPattern, HexMark } from "@/components/honeycomb";
import { ScrollReveal } from "@/components/scroll-reveal"; import { ScrollReveal } from "@/components/scroll-reveal";
import { ContentImage } from "@/components/content-image"; import { ContentImage } from "@/components/content-image";
@@ -17,16 +17,25 @@ export async function CurrentHarvest({
reviewSummaries?: Map<string, { average: number | null; count: number }>; reviewSummaries?: Map<string, { average: number | null; count: number }>;
}) { }) {
const t = getDictionary(await getLocale()); const t = getDictionary(await getLocale());
// Ausserhalb der Erntezeit sind es oft nur ein, zwei Glaeser. Dann steht die
// Ueberschrift als eigene Spalte daneben, statt ueber einem halb leeren
// Dreierraster.
const isFew = products.length <= 2;
return ( return (
<section className="py-24"> <section className="py-24">
<Container> <Container className={isFew ? "grid gap-12 lg:grid-cols-12 lg:gap-16" : ""}>
<ScrollReveal> <ScrollReveal className={isFew ? "lg:col-span-4" : ""}>
<SectionHeading <div className={isFew ? "lg:sticky lg:top-32" : ""}>
title={t.home.currentHarvest.title} <SectionHeading title={t.home.currentHarvest.title} subtitle={t.home.currentHarvest.subtitle} />
subtitle={t.home.currentHarvest.subtitle} </div>
/>
</ScrollReveal> </ScrollReveal>
<div className="mt-14 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-14"> <div
className={
isFew
? "grid grid-cols-1 gap-x-8 gap-y-14 sm:grid-cols-2 lg:col-span-8"
: "mt-14 grid grid-cols-1 gap-x-8 gap-y-14 sm:grid-cols-2 lg:grid-cols-3"
}
>
{products.map((product, index) => ( {products.map((product, index) => (
<ProductCard <ProductCard
key={product.id} key={product.id}
@@ -55,24 +64,23 @@ export async function OriginStory() {
); );
const facts = parseListSimple(content["home.originStory.facts"]); const facts = parseListSimple(content["home.originStory.facts"]);
return ( return (
<section className="py-28 bg-cream/60"> // Randlos geteilt: das Foto fuellt die linke Haelfte bis an den
<Container className="grid md:grid-cols-2 gap-16 lg:gap-24 items-center"> // Fensterrand, der Text steht daneben — kein Rahmen, keine Karte.
<ScrollReveal> <section className="grid bg-cream/60 md:grid-cols-2">
<FramedMedia> <ScrollReveal className="h-full">
<ContentImage <ContentImage
src={content["home.originStory.image"] || null} src={content["home.originStory.image"] || null}
alt="" alt=""
tone="forest" tone="forest"
className="aspect-[4/5] w-full" rounded={false}
label={t.home.originStory.eyebrow} className="h-full min-h-[24rem] w-full md:min-h-[40rem]"
sizes="(min-width: 768px) 50vw, 100vw" label={t.home.originStory.eyebrow}
/> sizes="(min-width: 768px) 50vw, 100vw"
</FramedMedia> />
</ScrollReveal> </ScrollReveal>
<ScrollReveal delay={0.1}> <div className="flex items-center px-6 py-20 sm:px-10 lg:px-20 xl:px-28">
<SectionHeading <ScrollReveal delay={0.1} className="max-w-xl">
title={t.home.originStory.title} <SectionHeading title={t.home.originStory.title} />
/>
<p className="mt-6 text-ink/75 leading-relaxed">{content["home.originStory.paragraph1"]}</p> <p className="mt-6 text-ink/75 leading-relaxed">{content["home.originStory.paragraph1"]}</p>
<p className="mt-4 text-ink/75 leading-relaxed">{content["home.originStory.paragraph2"]}</p> <p className="mt-4 text-ink/75 leading-relaxed">{content["home.originStory.paragraph2"]}</p>
<ul className="mt-8 grid gap-2.5 text-sm text-ink/75 sm:grid-cols-2"> <ul className="mt-8 grid gap-2.5 text-sm text-ink/75 sm:grid-cols-2">
@@ -83,11 +91,11 @@ export async function OriginStory() {
</li> </li>
))} ))}
</ul> </ul>
<LinkButton href="/herkunft" variant="secondary" className="mt-8"> <LinkButton href="/herkunft" variant="secondary" className="mt-10">
{t.home.originStory.cta} {t.home.originStory.cta}
</LinkButton> </LinkButton>
</ScrollReveal> </ScrollReveal>
</Container> </div>
</section> </section>
); );
} }
@@ -98,26 +106,22 @@ export async function Philosophy() {
const content = await getContentValues(["home.philosophy.items"], locale); const content = await getContentValues(["home.philosophy.items"], locale);
const items = parseListTitled(content["home.philosophy.items"]); const items = parseListTitled(content["home.philosophy.items"]);
return ( return (
<section className="py-24"> <section className="py-24 md:py-28">
<Container> <Container>
<ScrollReveal> <ScrollReveal>
<SectionHeading <SectionHeading title={t.home.philosophy.title} />
title={t.home.philosophy.title}
align="center"
className="mx-auto"
/>
</ScrollReveal> </ScrollReveal>
<div className="mt-16 grid sm:grid-cols-2 lg:grid-cols-4 gap-x-8 gap-y-14"> {/* Typografische Reihe statt Icon-Karten: vier Grundsaetze, getrennt
durch Linien, die Titel tragen das Gewicht. */}
<div className="mt-14 grid border-t border-ink/15 sm:grid-cols-2 lg:grid-cols-4">
{items.map((item, i) => ( {items.map((item, i) => (
<ScrollReveal key={item.title} delay={i * 0.08}> <ScrollReveal
<div className={`border-t border-ink/15 pt-6 ${i % 2 === 1 ? "lg:mt-10" : ""}`}> key={item.title}
{/* Wabe statt "01"-Nummer: die vier Punkte sind gleichrangig, keine Reihenfolge. */} delay={i * 0.06}
<HexMark filled={i % 2 === 0} className="h-5 w-5 text-honey" /> className="border-b border-ink/15 py-8 sm:pr-8 lg:border-b-0 lg:border-l lg:px-8 lg:first:border-l-0 lg:first:pl-0"
<h3 className="mt-4 text-2xl text-forest">{item.title}</h3> >
<p className="mt-3 text-sm text-ink/70 leading-relaxed"> <h3 className="font-serif text-3xl leading-tight text-forest text-balance">{item.title}</h3>
{item.text} <p className="mt-3 max-w-[34ch] text-sm leading-relaxed text-ink/70">{item.text}</p>
</p>
</div>
</ScrollReveal> </ScrollReveal>
))} ))}
</div> </div>
+37 -37
View File
@@ -1,4 +1,4 @@
import { Container, SectionHeading, LinkButton, FramedMedia } from "@/components/ui"; import { Container, SectionHeading, LinkButton } from "@/components/ui";
import { ProcessPath } from "@/components/process-path"; import { ProcessPath } from "@/components/process-path";
import { HoneycombPattern, HexMark } from "@/components/honeycomb"; import { HoneycombPattern, HexMark } from "@/components/honeycomb";
import { ScrollReveal } from "@/components/scroll-reveal"; import { ScrollReveal } from "@/components/scroll-reveal";
@@ -43,11 +43,24 @@ export async function RegionalSection() {
); );
return ( return (
<section className="py-24"> <section className="py-24">
<Container className="grid md:grid-cols-2 gap-16 lg:gap-24 items-center"> <Container>
<ScrollReveal className="order-2 md:order-1"> {/* Breites Bild, der Text legt sich als Tafel darueber — Tiefe statt
<SectionHeading eines weiteren Bild-neben-Text-Wechsels. */}
title={t.home.regionalSection.title} <ScrollReveal>
<ContentImage
src={content["home.regionalSection.image"] || null}
alt=""
tone="forest"
className="aspect-[4/3] w-full sm:aspect-[16/9]"
label={t.home.regionalSection.eyebrow}
sizes="(min-width: 1280px) 1200px, 100vw"
/> />
</ScrollReveal>
<ScrollReveal
delay={0.1}
className="relative mx-4 -mt-16 max-w-2xl bg-background p-8 shadow-[0_24px_50px_-34px_rgba(36,53,43,0.45)] sm:mx-10 sm:p-10 lg:-mt-40 lg:ml-16 lg:p-12"
>
<SectionHeading title={t.home.regionalSection.title} />
<p className="mt-6 text-ink/75 leading-relaxed">{content["home.regionalSection.paragraph1"]}</p> <p className="mt-6 text-ink/75 leading-relaxed">{content["home.regionalSection.paragraph1"]}</p>
<p className="mt-4 text-ink/75 leading-relaxed">{content["home.regionalSection.paragraph2"]}</p> <p className="mt-4 text-ink/75 leading-relaxed">{content["home.regionalSection.paragraph2"]}</p>
<div className="mt-8 flex flex-wrap gap-4"> <div className="mt-8 flex flex-wrap gap-4">
@@ -59,18 +72,6 @@ export async function RegionalSection() {
</LinkButton> </LinkButton>
</div> </div>
</ScrollReveal> </ScrollReveal>
<ScrollReveal className="order-1 md:order-2" delay={0.1}>
<FramedMedia side="left">
<ContentImage
src={content["home.regionalSection.image"] || null}
alt=""
tone="forest"
className="aspect-[4/5] w-full"
label={t.home.regionalSection.eyebrow}
sizes="(min-width: 768px) 50vw, 100vw"
/>
</FramedMedia>
</ScrollReveal>
</Container> </Container>
</section> </section>
); );
@@ -120,27 +121,26 @@ export async function BusinessTeaser() {
); );
return ( return (
<section className="py-24"> <section className="py-24">
<Container className="grid md:grid-cols-2 gap-16 lg:gap-24 items-center"> <Container>
<ScrollReveal> {/* Kompakte Tafel statt eines dritten grossen Bild/Text-Blocks: ein
<FramedMedia> Angebot fuer eine kleinere Zielgruppe, entsprechend zurueckhaltend. */}
<ContentImage <ScrollReveal className="grid overflow-hidden border border-ink/10 bg-background md:grid-cols-5">
src={content["home.businessTeaser.image"] || null} <div className="flex flex-col justify-center p-8 sm:p-12 md:col-span-3 lg:p-16">
alt="" <SectionHeading title={t.home.businessTeaser.title} />
tone="honey" <p className="mt-6 max-w-[55ch] text-ink/75 leading-relaxed">{content["home.businessTeaser.paragraph"]}</p>
className="aspect-[4/5] w-full" <LinkButton href="/unternehmen" className="mt-8 self-start">
label={t.home.businessTeaser.eyebrow} {t.home.businessTeaser.cta}
sizes="(min-width: 768px) 50vw, 100vw" </LinkButton>
/> </div>
</FramedMedia> <ContentImage
</ScrollReveal> src={content["home.businessTeaser.image"] || null}
<ScrollReveal delay={0.1}> alt=""
<SectionHeading tone="honey"
title={t.home.businessTeaser.title} rounded={false}
className="aspect-[4/3] w-full md:col-span-2 md:aspect-auto md:h-full md:min-h-[22rem]"
label={t.home.businessTeaser.eyebrow}
sizes="(min-width: 768px) 40vw, 100vw"
/> />
<p className="mt-6 text-ink/75 leading-relaxed">{content["home.businessTeaser.paragraph"]}</p>
<LinkButton href="/unternehmen" className="mt-8">
{t.home.businessTeaser.cta}
</LinkButton>
</ScrollReveal> </ScrollReveal>
</Container> </Container>
</section> </section>