import React, { useState } from 'react'; import { Link } from '@/spa/router'; import { ChevronRight, ArrowLeft, Users, Star, Eye, Heart } from 'lucide-react'; import MunichSkylineBg from '@/spa/components/ui/munich-skyline-bg'; import { useIsMobile } from '@/spa/hooks/useIsMobile'; import MembershipWizard from '@/spa/components/membership/MembershipWizard'; import { useCmsRoute } from '@/spa/cmsRoute'; import { mediaAlt, mediaUrl } from '@/spa/cmsMediaField'; import { mitgliedWerdenContent } from '@/spa/mitgliedWerdenContent'; import Image from '@/spa/components/ui/UnoptimizedImage' const NAVY = '#111D55'; const GOLD = '#EFBF04'; const CREAM = '#EFE5E3'; const FF = '"IBM Plex Sans", sans-serif'; const FB = '"Inter", sans-serif'; const BLUE = '#4A8FC9'; // ── Benefit card with hover accent line ────────────────────────────────────── function BenefitCard({ icon, title, body, isLast, isMobile = false, }: { icon: React.ReactNode; title: string; body: string; isLast: boolean; isMobile?: boolean; }) { const [hovered, setHovered] = useState(false); return (
setHovered(true)} onMouseLeave={() => setHovered(false)} >
{icon}
{title}
{body}
); } // ── Beitragsrechner ─────────────────────────────────────────────────────────── type MitgliedWerdenCms = Partial & { hero?: Partial & { image?: unknown } formIntro?: Partial & { image?: unknown } applicationOptions?: Partial & { items?: Array & { file?: unknown }> } } type PricingContent = typeof mitgliedWerdenContent.pricing type ApplicationOption = (typeof mitgliedWerdenContent.applicationOptions.items)[number] & { file?: unknown } type FaqItem = (typeof mitgliedWerdenContent.faq.items)[number] const fallbackText = (value: unknown, fallback: string) => typeof value === 'string' && value.length > 0 ? value : fallback; const fallbackNumber = (value: unknown, fallback: number) => typeof value === 'number' && Number.isFinite(value) ? value : fallback; const fallbackArray = (value: unknown, fallback: T[]) => Array.isArray(value) && value.length ? value as T[] : fallback; function formatEurLocal(n: number) { return n.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' }); } function TextLines({ text }: { text: string }) { return ( <> {text.split('\n').map((line, i) => ( {i > 0 &&
} {line}
))} ) } function BeitragsrechnerSection({ isMobile, content }: { isMobile: boolean; content: Partial }) { const [selected, setSelected] = useState(''); const [tableOpen, setTableOpen] = useState(false); const fallback = mitgliedWerdenContent.pricing; const tiers = fallbackArray(content.tiers, fallback.tiers); const vatRate = fallbackNumber(content.vatRate, fallback.vatRate); const admissionFeeNet = fallbackNumber(content.admissionFeeNet, fallback.admissionFeeNet); const tier = tiers.find(t => t.label === selected); const annualNet = tier?.annual ?? 0; const annualGross = annualNet * (1 + vatRate); const admissionGross = admissionFeeNet * (1 + vatRate); const today = new Date(); const monthsLeft = 12 - today.getMonth(); const anteilig = annualGross / 12 * monthsLeft + admissionGross; return (
{fallbackText(content.eyebrow, fallback.eyebrow)}

{fallbackText(content.heading, fallback.heading)}

{/* Left: dropdown + result */}
{/* Result card */}
{fallbackText(content.resultEyebrow, fallback.resultEyebrow)}
{[ [fallbackText(content.annualNetLabel, fallback.annualNetLabel), formatEurLocal(annualNet)], [fallbackText(content.annualGrossLabel, fallback.annualGrossLabel), formatEurLocal(annualGross)], [fallbackText(content.admissionGrossLabel, fallback.admissionGrossLabel), formatEurLocal(admissionGross)], ].map(([l, v]) => (
{l} {v}
))}
{fallbackText(content.totalYearOneLabel, fallback.totalYearOneLabel)} {formatEurLocal(anteilig)}
{fallbackText(content.footnote, fallback.footnote)}
{ (e.currentTarget as HTMLElement).style.background = '#FFD130'; (e.currentTarget as HTMLElement).style.boxShadow = '0 0 18px rgba(239,191,4,0.6)'; }} onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = GOLD; (e.currentTarget as HTMLElement).style.boxShadow = 'none'; }} > {fallbackText(content.ctaLabel, fallback.ctaLabel)}
{/* Right: Staffeltabelle */}
{tableOpen && (
{tiers.map((t, i) => ( ))}
{fallbackText(content.tableEmployeeHeader, fallback.tableEmployeeHeader)} {fallbackText(content.tableNetHeader, fallback.tableNetHeader)} {fallbackText(content.tableGrossHeader, fallback.tableGrossHeader)}
{t.label} {formatEurLocal(t.annual)} {formatEurLocal(t.annual * (1 + vatRate))}
{fallbackText(content.tableNotePrefix, fallback.tableNotePrefix)} {formatEurLocal(admissionGross)} {fallbackText(content.tableNoteSuffix, fallback.tableNoteSuffix)}
)}
); } // ── PDF Download Section – editorial split strip ────────────────────────────── function PDFDownloadSection({ isMobile, content }: { isMobile: boolean; content: Partial }) { const [hover, setHover] = useState<'online' | 'pdf' | null>(null); const items = fallbackArray(content.items, mitgliedWerdenContent.applicationOptions.items) as ApplicationOption[]; const online = items[0] || mitgliedWerdenContent.applicationOptions.items[0]; const pdf = items[1] || mitgliedWerdenContent.applicationOptions.items[1]; const pdfHref = mediaUrl(pdf.file, fallbackText(pdf.href, mitgliedWerdenContent.applicationOptions.items[1].href)); return (
{/* Gold top rule */}
{/* Left – online */} setHover('online')} onMouseLeave={() => setHover(null)} > {/* Number */}
{fallbackText(online.number, mitgliedWerdenContent.applicationOptions.items[0].number)}
{fallbackText(online.eyebrow, mitgliedWerdenContent.applicationOptions.items[0].eyebrow)}
{fallbackText(online.title, mitgliedWerdenContent.applicationOptions.items[0].title)}
{fallbackText(online.description, mitgliedWerdenContent.applicationOptions.items[0].description)}
{/* Arrow */}
{/* Divider */} {!isMobile && (
); } // ── FAQ Section ─────────────────────────────────────────────────────────────── function FAQSection({ isMobile, content }: { isMobile: boolean; content: Partial }) { const [openIdx, setOpenIdx] = useState(null); const items = fallbackArray(content.items, mitgliedWerdenContent.faq.items) as FaqItem[]; return (
{fallbackText(content.eyebrow, mitgliedWerdenContent.faq.eyebrow)}

{fallbackText(content.heading, mitgliedWerdenContent.faq.heading)}

{items.map((item, i) => (
{openIdx === i && (
{item.a}
)}
))}
); } // ── Quote card ──────────────────────────────────────────────────────────────── function QuoteCard({ quote, initials, name, role, }: { quote: string; initials: string; name: string; role: string; }) { return (

„{quote}“

{initials}
{name}
{role}
); } // ── Main component ──────────────────────────────────────────────────────────── const MitgliedWerden: React.FC = () => { const isMobile = useIsMobile(); const cms = (useCmsRoute()?.doc?.mitgliedWerden || {}) as MitgliedWerdenCms; const header = (cms.header || {}) as Partial; const hero = (cms.hero || {}) as NonNullable; const benefits = (cms.benefits || {}) as Partial; const about = (cms.about || {}) as Partial; const pricing = (cms.pricing || {}) as Partial; const applicationOptions = (cms.applicationOptions || {}) as NonNullable; const formIntro = (cms.formIntro || {}) as NonNullable; const testimonials = (cms.testimonials || {}) as Partial; const faq = (cms.faq || {}) as Partial; const bottomCta = (cms.bottomCta || {}) as Partial; const heroStats = fallbackArray(hero.stats, mitgliedWerdenContent.hero.stats); const benefitCards = fallbackArray(benefits.cards, mitgliedWerdenContent.benefits.cards); const aboutParagraphs = fallbackArray( about.paragraphs, mitgliedWerdenContent.about.paragraphs, ).map((item) => typeof item === 'string' ? item : fallbackText((item as { text?: string }).text, '')); const aboutFacts = fallbackArray(about.facts, mitgliedWerdenContent.about.facts); const formPromises = fallbackArray(formIntro.promises, mitgliedWerdenContent.formIntro.promises); const testimonialItems = fallbackArray(testimonials.items, mitgliedWerdenContent.testimonials.items); const iconMap: Record = { users: , star: , eye: , heart: , }; return (
{/* ── SECTION 1 – Minimal Header ──────────────────────────────────────── */}
{fallbackText(header.brandLabel, mitgliedWerdenContent.header.brandLabel)} {fallbackText(header.backLabel, mitgliedWerdenContent.header.backLabel)}
{/* ── SECTION 2 – Hero ────────────────────────────────────────────────── */}
{/* Background image */} {/* Dark overlay so text remains readable */}
{/* Left col */}
{fallbackText(hero.eyebrow, mitgliedWerdenContent.hero.eyebrow)}

{fallbackText(hero.heading, mitgliedWerdenContent.hero.heading)}

{/* Right col – typographic stat block */} {!isMobile && (
{heroStats.map((stat, index) => (
{stat.value}
{stat.label}
{index < heroStats.length - 1 && (
)} ))}
)}
{/* Gold bottom gradient rule */}
{/* ── SECTION 3 – Benefit cards ───────────────────────────────────────── */}
{fallbackText(benefits.eyebrow, mitgliedWerdenContent.benefits.eyebrow)}

{fallbackText(benefits.heading, mitgliedWerdenContent.benefits.heading)}

{benefitCards.map((card, index) => ( ))}
{/* ── SECTION 4 – Über den Verein ─────────────────────────────────────── */}
{/* Left col */}
{fallbackText(about.eyebrow, mitgliedWerdenContent.about.eyebrow)}

{fallbackText(about.heading, mitgliedWerdenContent.about.heading)}

{aboutParagraphs.map((paragraph, index) => (

{paragraph}

))}
{fallbackText(about.descriptor, mitgliedWerdenContent.about.descriptor)}
{/* Right col */}
{aboutFacts.map((item, i) => (
{item.num}
{item.label}
{item.body}
))}
{/* ── SECTION 5 – Beitragsrechner ─────────────────────────────────────── */} {/* ── Antrag-Wahl Strip ────────────────────────────────────────────────── */} {/* ── SECTION 6 – Formular ────────────────────────────────────────────── */}
{/* Left – pitch */}
{fallbackText(formIntro.eyebrow, mitgliedWerdenContent.formIntro.eyebrow)}

{fallbackText(formIntro.heading, mitgliedWerdenContent.formIntro.heading)}

{formPromises.map((item, i) => (
{item.num}
{item.label} {item.desc}
))}
{/* Center divider */}
{/* Right – wizard */}
{!isMobile && (
{mediaAlt(formIntro.image,
{fallbackText(formIntro.imageLabel, mitgliedWerdenContent.formIntro.imageLabel)}
)}
{/* ── SECTION 7 – Testimonials ────────────────────────────────────────── */}
{fallbackText(testimonials.eyebrow, mitgliedWerdenContent.testimonials.eyebrow)}

{fallbackText(testimonials.heading, mitgliedWerdenContent.testimonials.heading)}

{testimonialItems.map((item, index) => ( ))}
{/* ── FAQ ─────────────────────────────────────────────────────────────── */} {/* ── SECTION 8 – Bottom CTA ──────────────────────────────────────────── */}
{fallbackText(bottomCta.eyebrow, mitgliedWerdenContent.bottomCta.eyebrow)}

{fallbackText(bottomCta.heading, mitgliedWerdenContent.bottomCta.heading)}

); }; export default MitgliedWerden;