Files
Greenlens/greenlns-landing/components/seo/SeoSections.tsx
2026-08-05 19:39:22 +02:00

336 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from 'next/link'
import SeoIcon, { type SeoIconName } from '@/components/seo/SeoIcon'
import type { SeoPageProfile } from '@/lib/seoPages'
import type { SeoTemplateCopy } from '@/lib/seoTemplateCopy'
interface SectionProps {
profile: SeoPageProfile
copy: SeoTemplateCopy
}
export function SeoHero({
profile,
copy,
secondaryHref = '#methodology',
}: SectionProps & { secondaryHref?: string }) {
return (
<section className="max-w-[1200px] mx-auto px-6 py-16 grid grid-cols-1 lg:grid-cols-12 gap-12 items-start mt-8">
<div className="lg:col-span-6 z-10 flex flex-col justify-center pt-8">
<div className="font-body text-xs font-semibold tracking-[0.1em] uppercase text-primary mb-6 seo-fade">
{copy.hero.eyebrow}
</div>
<h1 className="font-display font-bold text-[40px] leading-[1.2] tracking-[-0.01em] md:text-[56px] md:leading-[1.1] md:tracking-[-0.02em] text-primary mb-6 seo-fade seo-fade-1">
{profile.h1}
</h1>
<p className="font-body text-lg text-on-surface-variant mb-4 leading-relaxed seo-fade seo-fade-2">{profile.tagline}</p>
<p className="font-body text-base text-on-surface-variant mb-10 leading-relaxed seo-fade seo-fade-2">{profile.directAnswer}</p>
<div className="flex flex-col sm:flex-row gap-4 seo-fade seo-fade-3">
<a
className="inline-flex justify-center items-center px-8 py-3 bg-ink text-white rounded-full font-bold shadow-lg hover:bg-ink-soft transition-colors duration-300"
href="#download"
>
{copy.hero.ctaPrimary}
</a>
<a
className="inline-flex justify-center items-center px-8 py-3 bg-transparent border-2 border-ink text-ink rounded-full font-bold hover:bg-ink hover:text-white transition-colors duration-300"
href={secondaryHref}
>
{copy.hero.ctaSecondary}
</a>
</div>
<ul className="mt-6 flex flex-wrap gap-x-5 gap-y-2 seo-fade seo-fade-3">
{profile.timeCue && (
<li className="flex items-center gap-2 text-sm text-on-surface-variant">
<SeoIcon name="check_circle" className="w-4 h-4 shrink-0 text-primary" />
<span>{profile.timeCue}</span>
</li>
)}
{copy.hero.trustChips.map((chip) => (
<li key={chip} className="flex items-center gap-2 text-sm text-on-surface-variant">
<SeoIcon name="check_circle" className="w-4 h-4 shrink-0 text-primary" />
<span>{chip}</span>
</li>
))}
</ul>
<div className="mt-12 grid grid-cols-2 gap-6 pt-8 border-t border-surface-container-high seo-fade seo-fade-3">
<div>
<div className="font-display text-2xl font-semibold text-primary mb-1">{copy.hero.stat1Title}</div>
<div className="text-xs font-semibold tracking-[0.1em] uppercase text-on-surface-variant">{copy.hero.stat1Label}</div>
</div>
<div>
<div className="font-display text-2xl font-semibold text-primary mb-1">{copy.hero.stat2Title}</div>
<div className="text-xs font-semibold tracking-[0.1em] uppercase text-on-surface-variant">{copy.hero.stat2Label}</div>
</div>
</div>
</div>
<div className="lg:col-span-6 flex justify-center items-center seo-fade seo-fade-2">
<div className="relative w-full max-w-[320px] md:max-w-[380px] h-[550px] md:h-[650px] rounded-3xl overflow-hidden shadow-2xl border-[8px] border-surface-container">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
alt={profile.heroImageAlt ?? copy.hero.imageAlt}
className="w-full h-full object-cover"
src={profile.heroImage ?? '/hero.jpg'}
/>
<div className="absolute bottom-6 left-1/2 -translate-x-1/2 w-[90%] bg-surface/95 backdrop-blur p-4 rounded-xl border border-surface-container-highest flex items-center gap-3 shadow-xl">
<div className="w-1.5 h-8 bg-primary rounded-full"></div>
<div>
<div className="font-display font-semibold text-sm text-primary mb-0.5">{profile.heroImageBadgeTitle ?? copy.hero.floatTitle}</div>
<div className="text-[10px] font-semibold tracking-[0.1em] uppercase text-on-surface-variant">{profile.heroImageBadgeLabel ?? copy.hero.floatLabel}</div>
</div>
</div>
</div>
</div>
</section>
)
}
export function SeoAdvantage({
profile,
copy,
showInsights = false,
}: SectionProps & { showInsights?: boolean }) {
// Echtes Prüfdatum aus dem Profil. Kein globales Datum — eine Seite, die seit
// April unverändert ist, darf kein aktuelleres Datum behaupten.
const reviewedAt = profile.lastUpdated
return (
<section className={`max-w-[1200px] mx-auto px-6 py-16 grid grid-cols-1 ${showInsights ? 'lg:grid-cols-3' : ''} gap-12 border-t border-surface-container-high`} id="advantage">
<div className={showInsights ? 'lg:col-span-2' : ''}>
<div className="mb-8">
<span className="text-xs font-semibold tracking-[0.1em] uppercase text-primary">{copy.advantage.eyebrow}</span>
</div>
<h2 className="font-display font-semibold text-[32px] leading-[1.3] text-primary mb-6">{copy.advantage.heading}</h2>
<p className="font-body text-lg text-on-surface-variant mb-6">{profile.definitionBlock}</p>
<div className="text-xs font-semibold tracking-[0.1em] uppercase text-on-surface-variant">
{copy.advantage.updated}: {reviewedAt}
</div>
</div>
{showInsights && <div className="bg-surface-container rounded-xl p-8 seo-card">
<div className="flex items-center gap-3 mb-6">
<SeoIcon name="psychology" className="w-8 h-8 text-primary" />
<h3 className="font-display font-semibold text-2xl text-primary">{copy.didYouKnow.title}</h3>
</div>
<ul className="space-y-6">
{copy.didYouKnow.items.map((item, index) => (
<li key={item.title} className={index < copy.didYouKnow.items.length - 1 ? 'border-b border-outline-variant/30 pb-4' : ''}>
<strong className="block font-body text-primary mb-1">{item.title}</strong>
<span className="text-sm text-on-surface-variant">{item.body}</span>
</li>
))}
</ul>
</div>}
</section>
)
}
export function SeoMethodology({
copy,
intent = 'identification',
}: {
copy: SeoTemplateCopy
intent?: 'identification' | 'diagnosis'
}) {
const methodology = intent === 'diagnosis' ? copy.diagnosisMethodology : copy.methodology
return (
<section className="bg-surface-container-low py-16 md:py-[120px]" id="methodology">
<div className="max-w-[1200px] mx-auto px-6">
<div className="text-center mb-16">
<span className="text-xs font-semibold tracking-[0.1em] uppercase text-primary block mb-4">{methodology.eyebrow}</span>
<h2 className="font-display font-semibold text-[32px] md:text-[40px] leading-[1.2] text-primary">{methodology.title}</h2>
<p className="font-body text-on-surface-variant max-w-2xl mx-auto mt-4">{methodology.lead}</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{methodology.steps.map((step, index) => (
<div key={step.title} className="bg-surface p-8 rounded-xl shadow-sm border border-surface-container-high seo-card">
<div className="w-12 h-12 bg-primary-container text-on-primary-container rounded-full flex items-center justify-center font-display font-semibold text-xl mb-6">
{index + 1}
</div>
<h3 className="font-display font-semibold text-lg text-primary mb-3">{step.title}</h3>
<p className="font-body text-on-surface-variant text-sm">{step.body}</p>
</div>
))}
</div>
</div>
</section>
)
}
const comparisonIcons: SeoIconName[] = ['check_circle', 'calendar', 'healing', 'sun']
export function SeoComparison({
profile,
copy,
intent = 'identification',
}: SectionProps & { intent?: 'identification' | 'diagnosis' | 'care' | 'watering' }) {
const tipLink = profile.relatedLinks[0]
return (
<section className="py-16 md:py-24" id="comparison">
<div className="max-w-[1200px] mx-auto px-6 grid grid-cols-1 lg:grid-cols-12 gap-12">
<div className="lg:col-span-4">
<div className="lg:sticky lg:top-32">
<span className="text-xs font-semibold tracking-[0.1em] uppercase text-primary block mb-4">{copy.comparison.eyebrow}</span>
<h2 className="font-display font-semibold text-[32px] leading-[1.3] text-primary mb-6">{profile.featureTable.title}</h2>
<p className="font-body text-on-surface-variant mb-8">{copy.comparison.intentLead[intent]}</p>
{tipLink && (
<div className="bg-primary-container/20 p-6 rounded-lg border border-primary-container/30">
<h4 className="font-display font-semibold text-base text-primary mb-2">{copy.comparison.tipTitle}</h4>
<p className="text-sm text-on-surface-variant mb-3">{copy.comparison.tipBody}</p>
<Link className="text-primary text-xs font-semibold tracking-[0.1em] uppercase hover:underline" href={tipLink.href}>
{tipLink.label}
</Link>
</div>
)}
</div>
</div>
<div className="lg:col-span-8 flex flex-col border-t border-surface-container-highest">
{profile.featureTable.rows.map((row, index) => (
<div key={row.feature} className="grid grid-cols-1 md:grid-cols-2 gap-8 py-8 border-b border-surface-container-highest items-start">
<div>
<div className="flex items-center gap-2 mb-2">
<SeoIcon name={comparisonIcons[index % comparisonIcons.length]} className="w-5 h-5 text-primary" />
<h3 className="font-display font-semibold text-lg text-primary">{row.feature}</h3>
</div>
<div className="text-xs font-semibold tracking-[0.1em] uppercase text-primary mb-2">{copy.comparison.greenlens}</div>
<p className="font-body text-on-surface-variant text-sm">{row.greenlens}</p>
</div>
<div className="md:pt-9">
<div className="text-xs font-semibold tracking-[0.1em] uppercase text-on-surface-variant/60 mb-2">
{profile.featureTable.alternativeLabel}
</div>
<p className="font-body text-on-surface-variant/80 text-sm">{row.alternative}</p>
</div>
</div>
))}
</div>
</div>
</section>
)
}
export function SeoContentSections({ profile }: { profile: SeoPageProfile }) {
if (!profile.contentSections || profile.contentSections.length === 0) return null
return (
<section className="bg-surface-container-low py-16">
<div className="max-w-[1200px] mx-auto px-6 grid grid-cols-1 md:grid-cols-2 gap-8 items-start">
{profile.contentSections.map((section) => (
<article key={section.title} className="bg-surface p-8 rounded-xl shadow-sm border border-surface-container-high seo-card">
<span className="text-xs font-semibold tracking-[0.1em] uppercase text-primary block mb-4">{section.eyebrow}</span>
<h2 className="font-display font-semibold text-2xl text-primary mb-4">{section.title}</h2>
<p className="font-body text-on-surface-variant text-sm mb-4">{section.body}</p>
{section.bullets && (
<ul className="space-y-3">
{section.bullets.map((item) => (
<li key={item} className="flex items-start gap-2 text-sm text-on-surface-variant">
<SeoIcon name="check_circle" className="w-4 h-4 mt-0.5 shrink-0 text-primary" />
<span>{item}</span>
</li>
))}
</ul>
)}
</article>
))}
</div>
</section>
)
}
export function SeoOfferBlock({ profile }: { profile: SeoPageProfile }) {
if (!profile.offer) return null
const { eyebrow, title, body, terms, ctaLabel } = profile.offer
return (
<section className="max-w-[1200px] mx-auto px-6 py-16">
<div className="bg-surface rounded-2xl p-8 md:p-12 border border-surface-container-high shadow-sm">
<span className="text-xs font-semibold tracking-[0.1em] uppercase text-primary block mb-4">{eyebrow}</span>
<h2 className="font-display font-semibold text-2xl md:text-3xl text-primary mb-4 max-w-[26ch]">{title}</h2>
<p className="font-body text-on-surface-variant leading-relaxed mb-6 max-w-[60ch]">{body}</p>
<a
className="inline-flex justify-center items-center px-8 py-3 bg-ink text-white rounded-full font-bold shadow-lg hover:bg-ink-soft transition-colors duration-300"
href="#download"
>
{ctaLabel}
</a>
<p className="font-body text-sm text-on-surface-variant mt-5 max-w-[60ch]">{terms}</p>
</div>
</section>
)
}
export function SeoBestFit({ profile, copy }: SectionProps) {
return (
<section className="max-w-[1200px] mx-auto px-6 py-16 grid grid-cols-1 md:grid-cols-2 gap-8">
<article className="bg-secondary-container/40 rounded-xl p-8 border border-secondary-container">
<span className="text-xs font-semibold tracking-[0.1em] uppercase text-primary block mb-3">{copy.bestFit.chooseTag}</span>
<h2 className="font-display font-semibold text-2xl text-primary mb-6">{copy.bestFit.chooseTitle}</h2>
<ul className="space-y-4">
{profile.greenLensIf.map((item) => (
<li key={item} className="flex items-start gap-3 text-sm text-on-surface-variant">
<SeoIcon name="check_circle" className="w-5 h-5 mt-0.5 shrink-0 text-primary" />
<span>{item}</span>
</li>
))}
</ul>
</article>
<article className="bg-surface-container rounded-xl p-8 border border-surface-container-high">
<span className="text-xs font-semibold tracking-[0.1em] uppercase text-on-surface-variant block mb-3">{copy.bestFit.notTag}</span>
<h2 className="font-display font-semibold text-2xl text-primary mb-6">{copy.bestFit.notTitle}</h2>
<ul className="space-y-4">
{profile.notBestIf.map((item) => (
<li key={item} className="flex items-start gap-3 text-sm text-on-surface-variant">
<span className="w-5 h-5 mt-0.5 shrink-0 rounded-full border border-outline-variant flex items-center justify-center text-[10px] text-on-surface-variant">
</span>
<span>{item}</span>
</li>
))}
</ul>
</article>
</section>
)
}
export function SeoFaq({ profile, copy }: SectionProps) {
return (
<section className="bg-surface-container py-16 md:py-24" id="faq">
<div className="max-w-[900px] mx-auto px-6">
<div className="text-center mb-16">
<h2 className="font-display font-semibold text-[32px] leading-[1.3] text-primary">{copy.faq.title}</h2>
</div>
<div className="space-y-6">
{profile.faqs.map((item) => (
<div key={item.question} className="bg-surface rounded-lg p-6 shadow-sm">
<h3 className="font-display font-semibold text-base text-primary mb-2">{item.question}</h3>
<p className="font-body text-on-surface-variant text-sm">{item.answer}</p>
</div>
))}
</div>
</div>
</section>
)
}
export function SeoRelated({ profile, copy }: SectionProps) {
if (profile.relatedLinks.length === 0) return null
return (
<section className="max-w-[1200px] mx-auto px-6 py-16">
<div className="mb-10">
<span className="text-xs font-semibold tracking-[0.1em] uppercase text-primary block mb-3">{copy.related.tag}</span>
<h2 className="font-display font-semibold text-[32px] leading-[1.3] text-primary">{copy.related.title}</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{profile.relatedLinks.map((link) => (
<Link key={link.href} href={link.href} className="bg-surface p-6 rounded-xl border border-surface-container-high seo-card block">
<h3 className="font-display font-semibold text-lg text-primary mb-2">{link.label}</h3>
<p className="text-sm text-on-surface-variant">{link.description}</p>
</Link>
))}
</div>
</section>
)
}