feat(ui): Markenseiten Herkunft, Imkerei, Unternehmen, Kontakt
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m24s
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m24s
- Herkunft: atmendes Titelbild mit Wabenraster, Ueberschrift als Karte, die ins Bild ragt; Aspekte als Liste mit Waben - Imkerei: Bild mit Honigrahmen, erster Absatz als Einstieg - Fuer Unternehmen: Pakete als Karten mit Waben-Wasserzeichen, das beim Hover einrueckt; Anfrage mit mitlaufender Ueberschrift neben dem Formular - Kontakt: E-Mail gross auf Wald-Karte mit Wabenraster, Abholung daneben Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
dfda94ddfc
commit
76be1292ed
4 files changed
+153
-78
No files matched your search
+48
-25
@@ -1,5 +1,6 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Container, SectionHeading } from "@/components/ui";
|
import { Container, SectionHeading } from "@/components/ui";
|
||||||
|
import { HoneycombPattern, HexMark } from "@/components/honeycomb";
|
||||||
import { ContentImage } from "@/components/content-image";
|
import { ContentImage } from "@/components/content-image";
|
||||||
import { ScrollReveal } from "@/components/scroll-reveal";
|
import { ScrollReveal } from "@/components/scroll-reveal";
|
||||||
import { getLocale } from "@/i18n/locale";
|
import { getLocale } from "@/i18n/locale";
|
||||||
@@ -20,33 +21,55 @@ export default async function HerkunftPage() {
|
|||||||
const t = getDictionary(locale);
|
const t = getDictionary(locale);
|
||||||
const image = await getContentValue("herkunft.image", locale);
|
const image = await getContentValue("herkunft.image", locale);
|
||||||
return (
|
return (
|
||||||
<div className="py-20">
|
<div className="pb-20">
|
||||||
<ContentImage
|
{/* Landschaft atmet langsam, Wabenraster liegt darueber — die
|
||||||
src={image || null}
|
Ueberschrift ragt als Karte ins Bild hinein. */}
|
||||||
alt={t.herkunftPage.eyebrow}
|
<div className="relative h-[60vh] min-h-[380px] w-full overflow-hidden">
|
||||||
tone="forest"
|
<div className="motion-drift absolute inset-0 animate-[wh-drift_26s_ease-in-out_infinite_alternate]">
|
||||||
className="h-[50vh] min-h-[360px] w-full mb-20"
|
<ContentImage
|
||||||
label={t.herkunftPage.eyebrow}
|
src={image || null}
|
||||||
priority
|
alt={t.herkunftPage.eyebrow}
|
||||||
/>
|
tone="forest"
|
||||||
<Container>
|
className="h-full w-full"
|
||||||
<ScrollReveal>
|
label={t.herkunftPage.eyebrow}
|
||||||
<SectionHeading
|
rounded={false}
|
||||||
level="h1"
|
priority
|
||||||
eyebrow={t.herkunftPage.eyebrow}
|
|
||||||
title={t.herkunftPage.title}
|
|
||||||
subtitle={t.herkunftPage.subtitle}
|
|
||||||
/>
|
/>
|
||||||
</ScrollReveal>
|
|
||||||
<div className="mt-16 grid sm:grid-cols-2 gap-10">
|
|
||||||
{t.herkunftPage.aspects.map((a, i) => (
|
|
||||||
<ScrollReveal key={a.title} delay={i * 0.08}>
|
|
||||||
<h3 className="text-lg text-forest">{a.title}</h3>
|
|
||||||
<p className="mt-2 text-sm text-ink/65 leading-relaxed">{a.text}</p>
|
|
||||||
</ScrollReveal>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-16 text-xs text-ink/65 max-w-md">
|
<div className="absolute inset-0 bg-gradient-to-t from-forest/50 to-transparent" />
|
||||||
|
<HoneycombPattern className="text-honey-pale/[0.12]" size={32} fade="right" />
|
||||||
|
</div>
|
||||||
|
<Container>
|
||||||
|
<ScrollReveal className="relative -mt-28 md:-mt-36">
|
||||||
|
<div className="max-w-3xl bg-background px-8 pt-10 pb-2 md:px-12 md:pt-12 shadow-[0_-20px_40px_-30px_rgba(36,53,43,0.35)]">
|
||||||
|
<SectionHeading
|
||||||
|
level="h1"
|
||||||
|
eyebrow={t.herkunftPage.eyebrow}
|
||||||
|
title={t.herkunftPage.title}
|
||||||
|
subtitle={t.herkunftPage.subtitle}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
<ul className="mt-16 grid gap-x-16 border-t border-ink/10 sm:grid-cols-2">
|
||||||
|
{t.herkunftPage.aspects.map((a, i) => (
|
||||||
|
<li key={a.title} className="border-b border-ink/10">
|
||||||
|
<ScrollReveal delay={(i % 2) * 0.08}>
|
||||||
|
<div className="group grid grid-cols-[auto_1fr] gap-x-5 py-8">
|
||||||
|
<HexMark
|
||||||
|
filled={i % 2 === 0}
|
||||||
|
className="mt-2 h-3.5 w-3.5 text-honey transition-transform duration-500 ease-[var(--ease-arrive)] group-hover:rotate-90"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-2xl text-forest">{a.title}</h3>
|
||||||
|
<p className="mt-2 text-ink/70 leading-relaxed">{a.text}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<p className="mt-12 flex max-w-lg items-start gap-3 text-xs text-ink/65 leading-relaxed">
|
||||||
|
<HexMark filled={false} className="mt-0.5 h-3 w-3 flex-shrink-0 text-honey-dark" />
|
||||||
{t.herkunftPage.privacyNote}
|
{t.herkunftPage.privacyNote}
|
||||||
</p>
|
</p>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
+19
-14
@@ -1,5 +1,5 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Container, SectionHeading } from "@/components/ui";
|
import { Container, SectionHeading, FramedMedia } from "@/components/ui";
|
||||||
import { ContentImage } from "@/components/content-image";
|
import { ContentImage } from "@/components/content-image";
|
||||||
import { ScrollReveal } from "@/components/scroll-reveal";
|
import { ScrollReveal } from "@/components/scroll-reveal";
|
||||||
import { getLocale } from "@/i18n/locale";
|
import { getLocale } from "@/i18n/locale";
|
||||||
@@ -21,16 +21,19 @@ export default async function ImkereiPage() {
|
|||||||
const image = await getContentValue("imkerei.image", locale);
|
const image = await getContentValue("imkerei.image", locale);
|
||||||
return (
|
return (
|
||||||
<div className="py-20">
|
<div className="py-20">
|
||||||
<Container className="grid md:grid-cols-2 gap-14 items-center">
|
<Container className="grid md:grid-cols-2 gap-16 lg:gap-24 items-center">
|
||||||
<ScrollReveal>
|
<ScrollReveal>
|
||||||
<SectionHeading
|
<SectionHeading
|
||||||
level="h1"
|
level="h1"
|
||||||
eyebrow={t.imkereiPage.eyebrow}
|
eyebrow={t.imkereiPage.eyebrow}
|
||||||
title={t.imkereiPage.title}
|
title={t.imkereiPage.title}
|
||||||
/>
|
/>
|
||||||
<div className="mt-8 space-y-5 text-ink/75 leading-relaxed">
|
<div className="mt-8 max-w-[62ch] space-y-5 text-ink/75 leading-relaxed">
|
||||||
{t.imkereiPage.paragraphs.map((paragraph) => (
|
{t.imkereiPage.paragraphs.map((paragraph, i) => (
|
||||||
<p key={paragraph}>{paragraph}</p>
|
// Erster Absatz als Einstieg: groesser, in Serifenschrift.
|
||||||
|
<p key={paragraph} className={i === 0 ? "font-serif text-2xl leading-snug text-forest" : undefined}>
|
||||||
|
{paragraph}
|
||||||
|
</p>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</ScrollReveal>
|
</ScrollReveal>
|
||||||
@@ -41,15 +44,17 @@ export default async function ImkereiPage() {
|
|||||||
dem sonst greifenden Lazy-Loading erschien das Bild damit
|
dem sonst greifenden Lazy-Loading erschien das Bild damit
|
||||||
deutlich spaeter als noetig. Die Textspalte daneben behaelt ihre
|
deutlich spaeter als noetig. Die Textspalte daneben behaelt ihre
|
||||||
Einblendung, die kostet nichts. */}
|
Einblendung, die kostet nichts. */}
|
||||||
<ContentImage
|
<FramedMedia>
|
||||||
src={image || null}
|
<ContentImage
|
||||||
alt={t.imkereiPage.imageLabel}
|
src={image || null}
|
||||||
tone="honey"
|
alt={t.imkereiPage.imageLabel}
|
||||||
className="aspect-[4/5] w-full"
|
tone="honey"
|
||||||
label={t.imkereiPage.imageLabel}
|
className="aspect-[4/5] w-full"
|
||||||
sizes="(min-width: 768px) 50vw, 100vw"
|
label={t.imkereiPage.imageLabel}
|
||||||
priority
|
sizes="(min-width: 768px) 50vw, 100vw"
|
||||||
/>
|
priority
|
||||||
|
/>
|
||||||
|
</FramedMedia>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+46
-23
@@ -1,5 +1,6 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Container, SectionHeading } from "@/components/ui";
|
import { Container, SectionHeading } from "@/components/ui";
|
||||||
|
import { HoneycombPattern, HexMark } from "@/components/honeycomb";
|
||||||
import { ScrollReveal } from "@/components/scroll-reveal";
|
import { ScrollReveal } from "@/components/scroll-reveal";
|
||||||
import { getCompanyInfo } from "@/lib/company-info";
|
import { getCompanyInfo } from "@/lib/company-info";
|
||||||
import { getLocale } from "@/i18n/locale";
|
import { getLocale } from "@/i18n/locale";
|
||||||
@@ -19,34 +20,56 @@ export default async function KontaktPage() {
|
|||||||
const company = await getCompanyInfo();
|
const company = await getCompanyInfo();
|
||||||
return (
|
return (
|
||||||
<div className="py-20">
|
<div className="py-20">
|
||||||
<Container className="max-w-2xl">
|
<Container className="max-w-5xl">
|
||||||
<ScrollReveal>
|
<ScrollReveal>
|
||||||
<SectionHeading level="h1" eyebrow={t.kontaktPage.eyebrow} title={t.kontaktPage.title} />
|
<SectionHeading level="h1" eyebrow={t.kontaktPage.eyebrow} title={t.kontaktPage.title} />
|
||||||
</ScrollReveal>
|
</ScrollReveal>
|
||||||
|
|
||||||
<ScrollReveal delay={0.1} className="mt-10 space-y-2 text-ink/75">
|
<div className="mt-12 grid gap-6 md:grid-cols-5">
|
||||||
<p>{company.name}</p>
|
{/* Kontaktkarte auf Wabenraster: die E-Mail ist der Hauptweg und
|
||||||
<p>{company.address.street}</p>
|
steht deshalb gross in Serifenschrift. */}
|
||||||
<p>
|
<ScrollReveal delay={0.1} className="md:col-span-3">
|
||||||
{company.address.zip} {company.address.city}
|
<div className="relative h-full overflow-hidden bg-forest px-8 py-10 text-cream md:px-10">
|
||||||
</p>
|
<HoneycombPattern className="text-honey-light/[0.1]" size={24} fade="right" />
|
||||||
<p className="pt-4">
|
<div className="relative">
|
||||||
<a href={`mailto:${company.email}`} className="text-forest underline">
|
<a
|
||||||
{company.email}
|
href={`mailto:${company.email}`}
|
||||||
</a>
|
className="group inline-flex items-center gap-3 font-serif text-3xl md:text-4xl text-cream"
|
||||||
</p>
|
>
|
||||||
{/* Ohne gepflegte Nummer entfaellt die Zeile ganz. Eine
|
<span className="link-draw pb-1">{company.email}</span>
|
||||||
Platzhalternummer waere schlimmer als keine: sie sieht nach
|
<svg
|
||||||
einem Kontaktweg aus, an dessen Ende niemand abnimmt. */}
|
aria-hidden="true"
|
||||||
{company.phone ? <p>{company.phone}</p> : null}
|
viewBox="0 0 24 24"
|
||||||
</ScrollReveal>
|
className="h-5 w-5 text-honey-light transition-transform duration-300 ease-[var(--ease-arrive)] group-hover:translate-x-1"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
>
|
||||||
|
<path d="M4 12h15M13 6l6 6-6 6" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
{/* Ohne gepflegte Nummer entfaellt die Zeile ganz. Eine
|
||||||
|
Platzhalternummer waere schlimmer als keine: sie sieht nach
|
||||||
|
einem Kontaktweg aus, an dessen Ende niemand abnimmt. */}
|
||||||
|
{company.phone ? <p className="mt-4 text-lg text-cream/85 tabular-nums">{company.phone}</p> : null}
|
||||||
|
<address className="mt-10 not-italic text-cream/75 leading-relaxed">
|
||||||
|
<span className="block text-cream">{company.name}</span>
|
||||||
|
{company.address.street}
|
||||||
|
<br />
|
||||||
|
{company.address.zip} {company.address.city}
|
||||||
|
</address>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
<ScrollReveal delay={0.18} className="mt-12 border-t border-ink/10 pt-8">
|
<ScrollReveal delay={0.18} className="md:col-span-2">
|
||||||
<h2 className="text-lg text-forest">{t.kontaktPage.pickupHeading}</h2>
|
<div className="h-full border border-honey/30 bg-honey/[0.06] px-8 py-10">
|
||||||
<p className="mt-2 text-sm text-ink/65 leading-relaxed">
|
<HexMark className="h-4 w-4 text-honey-dark" />
|
||||||
{t.kontaktPage.pickupText}
|
<h2 className="mt-5 text-2xl text-forest">{t.kontaktPage.pickupHeading}</h2>
|
||||||
</p>
|
<p className="mt-3 text-ink/70 leading-relaxed">{t.kontaktPage.pickupText}</p>
|
||||||
</ScrollReveal>
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Container, SectionHeading } from "@/components/ui";
|
import { Container, SectionHeading } from "@/components/ui";
|
||||||
|
import { HoneycombPattern } from "@/components/honeycomb";
|
||||||
import { ContentImage } from "@/components/content-image";
|
import { ContentImage } from "@/components/content-image";
|
||||||
import { BusinessInquiryForm } from "@/components/business-inquiry-form";
|
import { BusinessInquiryForm } from "@/components/business-inquiry-form";
|
||||||
import { ScrollReveal } from "@/components/scroll-reveal";
|
import { ScrollReveal } from "@/components/scroll-reveal";
|
||||||
@@ -22,14 +23,20 @@ export default async function UnternehmenPage() {
|
|||||||
const image = await getContentValue("unternehmen.image", locale);
|
const image = await getContentValue("unternehmen.image", locale);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ContentImage
|
<div className="relative h-[50vh] min-h-[340px] w-full overflow-hidden">
|
||||||
src={image || null}
|
<div className="motion-drift absolute inset-0 animate-[wh-drift_26s_ease-in-out_infinite_alternate]">
|
||||||
alt={t.unternehmenPage.eyebrow}
|
<ContentImage
|
||||||
tone="honey"
|
src={image || null}
|
||||||
className="h-[45vh] min-h-[320px] w-full"
|
alt={t.unternehmenPage.eyebrow}
|
||||||
label={t.unternehmenPage.eyebrow}
|
tone="honey"
|
||||||
priority
|
className="h-full w-full"
|
||||||
/>
|
label={t.unternehmenPage.eyebrow}
|
||||||
|
rounded={false}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<HoneycombPattern className="text-cream/[0.14]" size={30} fade="right" />
|
||||||
|
</div>
|
||||||
<Container className="py-20">
|
<Container className="py-20">
|
||||||
<ScrollReveal>
|
<ScrollReveal>
|
||||||
<SectionHeading
|
<SectionHeading
|
||||||
@@ -40,19 +47,36 @@ export default async function UnternehmenPage() {
|
|||||||
/>
|
/>
|
||||||
</ScrollReveal>
|
</ScrollReveal>
|
||||||
|
|
||||||
<div className="mt-14 grid sm:grid-cols-3 gap-8">
|
<div className="mt-14 grid sm:grid-cols-3 gap-6">
|
||||||
{t.unternehmenPage.packages.map((pkg, i) => (
|
{t.unternehmenPage.packages.map((pkg, i) => (
|
||||||
<ScrollReveal key={pkg.key} delay={i * 0.08} className="border border-ink/10 p-8">
|
<ScrollReveal key={pkg.key} delay={i * 0.08}>
|
||||||
<h3 className="text-lg text-forest">{pkg.title}</h3>
|
<div className="group relative h-full overflow-hidden border border-ink/10 bg-background p-8 transition-[transform,border-color,box-shadow] duration-500 ease-[var(--ease-arrive)] hover:-translate-y-1 hover:border-honey/60 hover:shadow-[0_22px_40px_-24px_rgba(184,138,68,0.45)]">
|
||||||
<p className="mt-2 text-sm text-ink/65">{pkg.text}</p>
|
{/* Grosses Sechseck als Wasserzeichen, das beim Hover einrueckt. */}
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 26"
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute -right-6 -top-6 h-28 w-28 text-honey/15 transition-transform duration-700 ease-[var(--ease-arrive)] group-hover:-translate-x-2 group-hover:translate-y-2 group-hover:rotate-12"
|
||||||
|
>
|
||||||
|
<polygon points="12,1 22.4,7 22.4,19 12,25 1.6,19 1.6,7" fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
<h3 className="relative text-2xl text-forest">{pkg.title}</h3>
|
||||||
|
<p className="relative mt-3 text-sm text-ink/70 leading-relaxed">{pkg.text}</p>
|
||||||
|
</div>
|
||||||
</ScrollReveal>
|
</ScrollReveal>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ScrollReveal className="mt-20 max-w-2xl">
|
{/* Geteilt: die Ueberschrift laeuft neben dem langen Formular mit. */}
|
||||||
<h2 className="text-2xl text-forest mb-8">{t.unternehmenPage.inquiryHeading}</h2>
|
<div className="mt-24 grid gap-10 lg:grid-cols-12 lg:gap-16">
|
||||||
<BusinessInquiryForm turnstileSiteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY} />
|
<ScrollReveal className="lg:col-span-4">
|
||||||
</ScrollReveal>
|
<h2 className="text-4xl leading-tight text-forest lg:sticky lg:top-32">
|
||||||
|
{t.unternehmenPage.inquiryHeading}
|
||||||
|
</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
<ScrollReveal className="lg:col-span-8">
|
||||||
|
<BusinessInquiryForm turnstileSiteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY} />
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user