1377 lines
54 KiB
TypeScript
1377 lines
54 KiB
TypeScript
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 (
|
||
<div
|
||
style={{
|
||
padding: isMobile ? '28px 24px' : '40px 36px',
|
||
borderRight: isLast || isMobile ? 'none' : '1px solid rgba(3,9,58,0.08)',
|
||
borderBottom: isMobile && !isLast ? '1px solid rgba(3,9,58,0.08)' : undefined,
|
||
borderTop: `3px solid ${hovered ? GOLD : 'transparent'}`,
|
||
transition: 'border-top-color 0.25s, background 0.25s',
|
||
cursor: 'default',
|
||
background: hovered ? 'rgba(3,9,58,0.02)' : 'transparent',
|
||
}}
|
||
onMouseEnter={() => setHovered(true)}
|
||
onMouseLeave={() => setHovered(false)}
|
||
>
|
||
<div style={{ marginBottom: 20 }}>{icon}</div>
|
||
<div
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 16,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase' as const,
|
||
letterSpacing: '0.07em',
|
||
color: '#101828',
|
||
marginBottom: 12,
|
||
}}
|
||
>
|
||
{title}
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontFamily: FB,
|
||
fontSize: 18,
|
||
color: 'rgba(16,24,40,0.55)',
|
||
lineHeight: 1.7,
|
||
}}
|
||
>
|
||
{body}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Beitragsrechner ───────────────────────────────────────────────────────────
|
||
|
||
type MitgliedWerdenCms = Partial<typeof mitgliedWerdenContent> & {
|
||
hero?: Partial<typeof mitgliedWerdenContent.hero> & { image?: unknown }
|
||
formIntro?: Partial<typeof mitgliedWerdenContent.formIntro> & { image?: unknown }
|
||
applicationOptions?: Partial<typeof mitgliedWerdenContent.applicationOptions> & {
|
||
items?: Array<Partial<(typeof mitgliedWerdenContent.applicationOptions.items)[number]> & { 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 = <T,>(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) => (
|
||
<React.Fragment key={`${line}-${i}`}>
|
||
{i > 0 && <br />}
|
||
{line}
|
||
</React.Fragment>
|
||
))}
|
||
</>
|
||
)
|
||
}
|
||
|
||
function BeitragsrechnerSection({ isMobile, content }: { isMobile: boolean; content: Partial<PricingContent> }) {
|
||
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 (
|
||
<section style={{ background: CREAM, position: 'relative', overflow: 'hidden', isolation: 'isolate' }}>
|
||
<MunichSkylineBg />
|
||
<div style={{ position: 'relative', zIndex: 1, padding: isMobile ? '48px 24px' : '80px' }}>
|
||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.32em', color: BLUE, display: 'block', marginBottom: 16 }}>
|
||
{fallbackText(content.eyebrow, fallback.eyebrow)}
|
||
</span>
|
||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(2rem, 4vw, 3rem)', fontWeight: 900, color: '#101828', textTransform: 'uppercase', letterSpacing: '-0.025em', lineHeight: 1.03, margin: '0 0 20px' }}>
|
||
{fallbackText(content.heading, fallback.heading)}
|
||
</h2>
|
||
<div style={{ width: 40, height: 2, background: GOLD, marginBottom: 48 }} />
|
||
|
||
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 40, alignItems: 'start' }}>
|
||
{/* Left: dropdown + result */}
|
||
<div>
|
||
<label style={{ display: 'block', fontFamily: FF, fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.12em', color: 'rgba(16,24,40,0.5)', marginBottom: 10 }}>
|
||
{fallbackText(content.employeeLabel, fallback.employeeLabel)}
|
||
</label>
|
||
<select
|
||
value={selected}
|
||
onChange={e => setSelected(e.target.value)}
|
||
style={{
|
||
width: '100%',
|
||
padding: '14px 18px',
|
||
background: '#fff',
|
||
border: `2px solid ${selected ? GOLD : 'rgba(16,24,40,0.15)'}`,
|
||
borderRadius: 8,
|
||
fontFamily: FF,
|
||
fontSize: 16,
|
||
fontWeight: 600,
|
||
color: selected ? '#101828' : 'rgba(16,24,40,0.4)',
|
||
cursor: 'pointer',
|
||
outline: 'none',
|
||
appearance: 'none',
|
||
backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23EFBF04' stroke-width='1.8' fill='none' stroke-linecap='round'/%3E%3C/svg%3E")`,
|
||
backgroundRepeat: 'no-repeat',
|
||
backgroundPosition: 'right 16px center',
|
||
transition: 'border-color 0.2s',
|
||
}}
|
||
>
|
||
<option value="">{fallbackText(content.selectPlaceholder, fallback.selectPlaceholder)}</option>
|
||
{tiers.map(t => (
|
||
<option key={t.label} value={t.label}>{t.label} {fallbackText(content.optionSuffix, fallback.optionSuffix)}</option>
|
||
))}
|
||
</select>
|
||
|
||
{/* Result card */}
|
||
<div style={{
|
||
marginTop: 24,
|
||
background: NAVY,
|
||
borderRadius: 12,
|
||
padding: '24px 28px',
|
||
opacity: selected ? 1 : 0,
|
||
transform: selected ? 'translateY(0)' : 'translateY(8px)',
|
||
transition: 'opacity 0.3s ease, transform 0.3s ease',
|
||
pointerEvents: selected ? 'auto' : 'none',
|
||
}}>
|
||
<div style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.15em', color: GOLD, marginBottom: 18 }}>
|
||
{fallbackText(content.resultEyebrow, fallback.resultEyebrow)}
|
||
</div>
|
||
{[
|
||
[fallbackText(content.annualNetLabel, fallback.annualNetLabel), formatEurLocal(annualNet)],
|
||
[fallbackText(content.annualGrossLabel, fallback.annualGrossLabel), formatEurLocal(annualGross)],
|
||
[fallbackText(content.admissionGrossLabel, fallback.admissionGrossLabel), formatEurLocal(admissionGross)],
|
||
].map(([l, v]) => (
|
||
<div key={l} style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10, fontSize: 14 }}>
|
||
<span style={{ color: 'rgba(255,255,255,0.5)', fontFamily: FF }}>{l}</span>
|
||
<span style={{ color: '#fff', fontWeight: 600, fontFamily: FF }}>{v}</span>
|
||
</div>
|
||
))}
|
||
<div style={{ height: 1, background: 'rgba(239,191,4,0.3)', margin: '14px 0' }} />
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 15 }}>
|
||
<span style={{ color: GOLD, fontWeight: 700, fontFamily: FF }}>{fallbackText(content.totalYearOneLabel, fallback.totalYearOneLabel)}</span>
|
||
<span style={{ color: GOLD, fontWeight: 900, fontFamily: FF, fontSize: 18 }}>{formatEurLocal(anteilig)}</span>
|
||
</div>
|
||
<div style={{ marginTop: 12, fontSize: 11, color: 'rgba(255,255,255,0.3)', fontFamily: FF, lineHeight: 1.5 }}>
|
||
{fallbackText(content.footnote, fallback.footnote)}
|
||
</div>
|
||
</div>
|
||
|
||
<a
|
||
href={fallbackText(content.ctaHref, fallback.ctaHref)}
|
||
style={{
|
||
display: 'block',
|
||
marginTop: 20,
|
||
background: GOLD,
|
||
color: '#101828',
|
||
fontFamily: FF,
|
||
fontSize: 14,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.15em',
|
||
padding: '14px 28px',
|
||
textDecoration: 'none',
|
||
textAlign: 'center',
|
||
borderRadius: 4,
|
||
transition: 'background 0.2s, box-shadow 0.2s',
|
||
}}
|
||
onMouseEnter={e => {
|
||
(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)}
|
||
</a>
|
||
</div>
|
||
|
||
{/* Right: Staffeltabelle */}
|
||
<div>
|
||
<button
|
||
onClick={() => setTableOpen(!tableOpen)}
|
||
style={{
|
||
width: '100%',
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
background: '#fff',
|
||
border: '1px solid rgba(16,24,40,0.12)',
|
||
borderBottom: tableOpen ? 'none' : '1px solid rgba(16,24,40,0.12)',
|
||
borderRadius: tableOpen ? '8px 8px 0 0' : 8,
|
||
padding: '14px 18px',
|
||
cursor: 'pointer',
|
||
fontFamily: FF,
|
||
fontSize: 13,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.1em',
|
||
color: '#101828',
|
||
}}
|
||
>
|
||
{fallbackText(content.tableToggleLabel, fallback.tableToggleLabel)}
|
||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" style={{ transform: tableOpen ? 'rotate(180deg)' : 'none', transition: 'transform 0.25s' }}>
|
||
<path d="M2 4l5 5 5-5" stroke={GOLD} strokeWidth="1.8" strokeLinecap="round" />
|
||
</svg>
|
||
</button>
|
||
{tableOpen && (
|
||
<div style={{ background: '#fff', border: '1px solid rgba(16,24,40,0.12)', borderTop: 'none', borderRadius: '0 0 8px 8px', overflow: 'hidden' }}>
|
||
<table style={{ width: '100%', borderCollapse: 'collapse', fontFamily: FF, tableLayout: isMobile ? 'fixed' : 'auto' }}>
|
||
<thead>
|
||
<tr style={{ background: NAVY }}>
|
||
<th style={{ padding: isMobile ? '10px 10px' : '12px 18px', textAlign: 'left', fontSize: 10, fontWeight: 700, letterSpacing: '0.15em', textTransform: 'uppercase', color: GOLD }}>{fallbackText(content.tableEmployeeHeader, fallback.tableEmployeeHeader)}</th>
|
||
<th style={{ padding: isMobile ? '10px 10px' : '12px 18px', textAlign: 'right', fontSize: 10, fontWeight: 700, letterSpacing: '0.15em', textTransform: 'uppercase', color: GOLD }}>{fallbackText(content.tableNetHeader, fallback.tableNetHeader)}</th>
|
||
<th style={{ padding: isMobile ? '10px 10px' : '12px 18px', textAlign: 'right', fontSize: 10, fontWeight: 700, letterSpacing: '0.15em', textTransform: 'uppercase', color: GOLD }}>{fallbackText(content.tableGrossHeader, fallback.tableGrossHeader)}</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{tiers.map((t, i) => (
|
||
<tr key={t.label} style={{ background: i % 2 === 0 ? 'rgba(239,191,4,0.04)' : '#fff', borderBottom: '1px solid rgba(16,24,40,0.06)' }}>
|
||
<td style={{ padding: isMobile ? '10px 10px' : '12px 18px', fontSize: isMobile ? 13 : 14, color: '#101828', fontWeight: selected === t.label ? 700 : 400 }}>{t.label}</td>
|
||
<td style={{ padding: isMobile ? '10px 10px' : '12px 18px', fontSize: isMobile ? 13 : 14, textAlign: 'right', color: 'rgba(16,24,40,0.6)' }}>{formatEurLocal(t.annual)}</td>
|
||
<td style={{ padding: isMobile ? '10px 10px' : '12px 18px', fontSize: isMobile ? 13 : 14, textAlign: 'right', fontWeight: 600, color: selected === t.label ? NAVY : '#101828' }}>{formatEurLocal(t.annual * (1 + vatRate))}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
<div style={{ padding: '10px 18px', fontSize: 11, color: 'rgba(16,24,40,0.4)', fontFamily: FF, borderTop: '1px solid rgba(16,24,40,0.06)' }}>
|
||
{fallbackText(content.tableNotePrefix, fallback.tableNotePrefix)} {formatEurLocal(admissionGross)} {fallbackText(content.tableNoteSuffix, fallback.tableNoteSuffix)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
// ── PDF Download Section – editorial split strip ──────────────────────────────
|
||
|
||
function PDFDownloadSection({ isMobile, content }: { isMobile: boolean; content: Partial<typeof mitgliedWerdenContent.applicationOptions> }) {
|
||
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 (
|
||
<section style={{
|
||
background: CREAM,
|
||
borderTop: '1px solid rgba(16,24,40,0.08)',
|
||
borderBottom: '1px solid rgba(16,24,40,0.08)',
|
||
position: 'relative',
|
||
overflow: 'hidden',
|
||
}}>
|
||
{/* Gold top rule */}
|
||
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 2, background: `linear-gradient(90deg, ${GOLD} 0%, rgba(239,191,4,0.3) 60%, transparent 100%)` }} />
|
||
|
||
<div style={{
|
||
display: 'grid',
|
||
gridTemplateColumns: isMobile ? '1fr' : '1fr 1px 1fr',
|
||
minHeight: isMobile ? undefined : 140,
|
||
}}>
|
||
{/* Left – online */}
|
||
<a
|
||
href={fallbackText(online.href, mitgliedWerdenContent.applicationOptions.items[0].href)}
|
||
style={{
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 24,
|
||
padding: isMobile ? '32px 24px' : '40px 52px',
|
||
textDecoration: 'none',
|
||
background: hover === 'online' ? 'rgba(17,29,85,0.04)' : 'transparent',
|
||
transition: 'background 0.3s ease',
|
||
cursor: 'pointer',
|
||
}}
|
||
onMouseEnter={() => setHover('online')}
|
||
onMouseLeave={() => setHover(null)}
|
||
>
|
||
{/* Number */}
|
||
<div style={{
|
||
flexShrink: 0,
|
||
fontFamily: FF,
|
||
fontSize: 'clamp(2.4rem, 4vw, 3.6rem)',
|
||
fontWeight: 900,
|
||
color: hover === 'online' ? GOLD : 'rgba(16,24,40,0.08)',
|
||
lineHeight: 1,
|
||
letterSpacing: '-0.04em',
|
||
transition: 'color 0.3s ease',
|
||
userSelect: 'none',
|
||
}}>
|
||
{fallbackText(online.number, mitgliedWerdenContent.applicationOptions.items[0].number)}
|
||
</div>
|
||
|
||
<div style={{ flex: 1 }}>
|
||
<div style={{
|
||
fontFamily: FF,
|
||
fontSize: 9,
|
||
fontWeight: 700,
|
||
letterSpacing: '0.28em',
|
||
textTransform: 'uppercase',
|
||
color: BLUE,
|
||
marginBottom: 8,
|
||
}}>
|
||
{fallbackText(online.eyebrow, mitgliedWerdenContent.applicationOptions.items[0].eyebrow)}
|
||
</div>
|
||
<div style={{
|
||
fontFamily: FF,
|
||
fontSize: 'clamp(1rem, 1.6vw, 1.2rem)',
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.04em',
|
||
color: '#101828',
|
||
marginBottom: 6,
|
||
}}>
|
||
{fallbackText(online.title, mitgliedWerdenContent.applicationOptions.items[0].title)}
|
||
</div>
|
||
<div style={{
|
||
fontFamily: FB,
|
||
fontSize: 13,
|
||
color: 'rgba(16,24,40,0.45)',
|
||
lineHeight: 1.6,
|
||
}}>
|
||
{fallbackText(online.description, mitgliedWerdenContent.applicationOptions.items[0].description)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Arrow */}
|
||
<div style={{
|
||
flexShrink: 0,
|
||
width: 36,
|
||
height: 36,
|
||
border: `1px solid ${hover === 'online' ? GOLD : 'rgba(16,24,40,0.15)'}`,
|
||
borderRadius: '50%',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
transition: 'border-color 0.3s, background 0.3s, transform 0.3s',
|
||
background: hover === 'online' ? GOLD : 'transparent',
|
||
transform: hover === 'online' ? 'translateX(4px)' : 'none',
|
||
}}>
|
||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
|
||
<path d="M3 7h8M8 4l3 3-3 3" stroke={hover === 'online' ? NAVY : 'rgba(16,24,40,0.5)'} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
</a>
|
||
|
||
{/* Divider */}
|
||
{!isMobile && (
|
||
<div style={{ background: 'rgba(16,24,40,0.08)', position: 'relative' }}>
|
||
{/* Gold dot at center */}
|
||
<div style={{
|
||
position: 'absolute',
|
||
top: '50%',
|
||
left: '50%',
|
||
transform: 'translate(-50%, -50%)',
|
||
width: 6,
|
||
height: 6,
|
||
background: GOLD,
|
||
borderRadius: '50%',
|
||
}} />
|
||
</div>
|
||
)}
|
||
|
||
{/* Right – PDF */}
|
||
<a
|
||
href={pdfHref}
|
||
download={pdf.download || undefined}
|
||
style={{
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 24,
|
||
padding: isMobile ? '32px 24px' : '40px 52px',
|
||
textDecoration: 'none',
|
||
background: hover === 'pdf' ? 'rgba(17,29,85,0.04)' : 'transparent',
|
||
borderTop: isMobile ? '1px solid rgba(16,24,40,0.08)' : 'none',
|
||
transition: 'background 0.3s ease',
|
||
cursor: 'pointer',
|
||
}}
|
||
onMouseEnter={() => setHover('pdf')}
|
||
onMouseLeave={() => setHover(null)}
|
||
>
|
||
{/* Number */}
|
||
<div style={{
|
||
flexShrink: 0,
|
||
fontFamily: FF,
|
||
fontSize: 'clamp(2.4rem, 4vw, 3.6rem)',
|
||
fontWeight: 900,
|
||
color: hover === 'pdf' ? GOLD : 'rgba(16,24,40,0.08)',
|
||
lineHeight: 1,
|
||
letterSpacing: '-0.04em',
|
||
transition: 'color 0.3s ease',
|
||
userSelect: 'none',
|
||
}}>
|
||
{fallbackText(pdf.number, mitgliedWerdenContent.applicationOptions.items[1].number)}
|
||
</div>
|
||
|
||
<div style={{ flex: 1 }}>
|
||
<div style={{
|
||
fontFamily: FF,
|
||
fontSize: 9,
|
||
fontWeight: 700,
|
||
letterSpacing: '0.28em',
|
||
textTransform: 'uppercase',
|
||
color: BLUE,
|
||
marginBottom: 8,
|
||
}}>
|
||
{fallbackText(pdf.eyebrow, mitgliedWerdenContent.applicationOptions.items[1].eyebrow)}
|
||
</div>
|
||
<div style={{
|
||
fontFamily: FF,
|
||
fontSize: 'clamp(1rem, 1.6vw, 1.2rem)',
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.04em',
|
||
color: '#101828',
|
||
marginBottom: 6,
|
||
}}>
|
||
{fallbackText(pdf.title, mitgliedWerdenContent.applicationOptions.items[1].title)}
|
||
</div>
|
||
<div style={{
|
||
fontFamily: FB,
|
||
fontSize: 13,
|
||
color: 'rgba(16,24,40,0.45)',
|
||
lineHeight: 1.6,
|
||
}}>
|
||
{fallbackText(pdf.description, mitgliedWerdenContent.applicationOptions.items[1].description)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Download icon */}
|
||
<div style={{
|
||
flexShrink: 0,
|
||
width: 36,
|
||
height: 36,
|
||
border: `1px solid ${hover === 'pdf' ? GOLD : 'rgba(16,24,40,0.15)'}`,
|
||
borderRadius: '50%',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
transition: 'border-color 0.3s, background 0.3s, transform 0.3s',
|
||
background: hover === 'pdf' ? GOLD : 'transparent',
|
||
transform: hover === 'pdf' ? 'translateY(2px)' : 'none',
|
||
}}>
|
||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
|
||
<path d="M7 2v7M4 6l3 3 3-3M2 11h10" stroke={hover === 'pdf' ? NAVY : 'rgba(16,24,40,0.5)'} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
</a>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
// ── FAQ Section ───────────────────────────────────────────────────────────────
|
||
|
||
function FAQSection({ isMobile, content }: { isMobile: boolean; content: Partial<typeof mitgliedWerdenContent.faq> }) {
|
||
const [openIdx, setOpenIdx] = useState<number | null>(null);
|
||
const items = fallbackArray(content.items, mitgliedWerdenContent.faq.items) as FaqItem[];
|
||
|
||
return (
|
||
<section style={{ background: CREAM, padding: isMobile ? '48px 24px' : '80px' }}>
|
||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.32em', color: BLUE, display: 'block', marginBottom: 16 }}>
|
||
{fallbackText(content.eyebrow, mitgliedWerdenContent.faq.eyebrow)}
|
||
</span>
|
||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(2rem, 4vw, 3rem)', fontWeight: 900, color: '#101828', textTransform: 'uppercase', letterSpacing: '-0.025em', lineHeight: 1.03, margin: '0 0 20px' }}>
|
||
{fallbackText(content.heading, mitgliedWerdenContent.faq.heading)}
|
||
</h2>
|
||
<div style={{ width: 40, height: 2, background: GOLD, marginBottom: 48 }} />
|
||
|
||
<div style={{ maxWidth: 800 }}>
|
||
{items.map((item, i) => (
|
||
<div key={i} style={{ borderBottom: '1px solid rgba(16,24,40,0.1)' }}>
|
||
<button
|
||
onClick={() => setOpenIdx(openIdx === i ? null : i)}
|
||
style={{
|
||
width: '100%',
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
padding: '20px 0',
|
||
background: 'transparent',
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
textAlign: 'left',
|
||
gap: 16,
|
||
}}
|
||
>
|
||
<span style={{
|
||
fontFamily: FF,
|
||
fontSize: 16,
|
||
fontWeight: openIdx === i ? 700 : 600,
|
||
color: openIdx === i ? NAVY : '#101828',
|
||
transition: 'color 0.2s',
|
||
lineHeight: 1.4,
|
||
}}>
|
||
{item.q}
|
||
</span>
|
||
<svg
|
||
width="16" height="16" viewBox="0 0 16 16" fill="none"
|
||
style={{ flexShrink: 0, transform: openIdx === i ? 'rotate(180deg)' : 'none', transition: 'transform 0.25s' }}
|
||
>
|
||
<path d="M3 5l5 5 5-5" stroke={openIdx === i ? GOLD : 'rgba(16,24,40,0.4)'} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
|
||
</svg>
|
||
</button>
|
||
{openIdx === i && (
|
||
<div style={{
|
||
padding: '0 0 20px',
|
||
fontFamily: FB,
|
||
fontSize: 17,
|
||
color: 'rgba(16,24,40,0.6)',
|
||
lineHeight: 1.8,
|
||
borderLeft: `3px solid ${GOLD}`,
|
||
paddingLeft: 20,
|
||
marginLeft: 2,
|
||
}}>
|
||
{item.a}
|
||
</div>
|
||
)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
// ── Quote card ────────────────────────────────────────────────────────────────
|
||
|
||
function QuoteCard({
|
||
quote,
|
||
initials,
|
||
name,
|
||
role,
|
||
}: {
|
||
quote: string;
|
||
initials: string;
|
||
name: string;
|
||
role: string;
|
||
}) {
|
||
return (
|
||
<div
|
||
style={{
|
||
background: CREAM,
|
||
padding: '32px',
|
||
borderTop: `3px solid ${GOLD}`,
|
||
display: 'flex',
|
||
flexDirection: 'column' as const,
|
||
}}
|
||
>
|
||
<p
|
||
style={{
|
||
fontFamily: FB,
|
||
fontSize: 18,
|
||
fontStyle: 'italic',
|
||
color: 'rgba(16,24,40,0.7)',
|
||
lineHeight: 1.75,
|
||
margin: '0 0 24px',
|
||
flex: 1,
|
||
}}
|
||
>
|
||
„{quote}“
|
||
</p>
|
||
<div style={{ width: 32, height: 1, background: GOLD, marginBottom: 20 }} />
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
|
||
<div
|
||
style={{
|
||
width: 40,
|
||
height: 40,
|
||
background: NAVY,
|
||
color: GOLD,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
fontFamily: FF,
|
||
fontSize: 16,
|
||
fontWeight: 700,
|
||
flexShrink: 0,
|
||
}}
|
||
>
|
||
{initials}
|
||
</div>
|
||
<div>
|
||
<div
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 16,
|
||
fontWeight: 700,
|
||
color: '#101828',
|
||
marginBottom: 2,
|
||
}}
|
||
>
|
||
{name}
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontFamily: FB,
|
||
fontSize: 16,
|
||
color: 'rgba(16,24,40,0.45)',
|
||
textTransform: 'uppercase' as const,
|
||
letterSpacing: '0.08em',
|
||
}}
|
||
>
|
||
{role}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Main component ────────────────────────────────────────────────────────────
|
||
|
||
const MitgliedWerden: React.FC = () => {
|
||
const isMobile = useIsMobile();
|
||
const cms = (useCmsRoute()?.doc?.mitgliedWerden || {}) as MitgliedWerdenCms;
|
||
const header = (cms.header || {}) as Partial<typeof mitgliedWerdenContent.header>;
|
||
const hero = (cms.hero || {}) as NonNullable<MitgliedWerdenCms['hero']>;
|
||
const benefits = (cms.benefits || {}) as Partial<typeof mitgliedWerdenContent.benefits>;
|
||
const about = (cms.about || {}) as Partial<typeof mitgliedWerdenContent.about>;
|
||
const pricing = (cms.pricing || {}) as Partial<typeof mitgliedWerdenContent.pricing>;
|
||
const applicationOptions = (cms.applicationOptions || {}) as NonNullable<MitgliedWerdenCms['applicationOptions']>;
|
||
const formIntro = (cms.formIntro || {}) as NonNullable<MitgliedWerdenCms['formIntro']>;
|
||
const testimonials = (cms.testimonials || {}) as Partial<typeof mitgliedWerdenContent.testimonials>;
|
||
const faq = (cms.faq || {}) as Partial<typeof mitgliedWerdenContent.faq>;
|
||
const bottomCta = (cms.bottomCta || {}) as Partial<typeof mitgliedWerdenContent.bottomCta>;
|
||
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<string, React.ReactNode> = {
|
||
users: <Users size={24} color={NAVY} strokeWidth={1.5} />,
|
||
star: <Star size={24} color={NAVY} strokeWidth={1.5} />,
|
||
eye: <Eye size={24} color={NAVY} strokeWidth={1.5} />,
|
||
heart: <Heart size={24} color={NAVY} strokeWidth={1.5} />,
|
||
};
|
||
|
||
return (
|
||
<div className="animate-fade-in">
|
||
|
||
{/* ── SECTION 1 – Minimal Header ──────────────────────────────────────── */}
|
||
<header
|
||
style={{
|
||
background: NAVY,
|
||
padding: isMobile ? '16px 24px' : '18px 80px',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
borderBottom: '1px solid rgba(239,191,4,0.25)',
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 14,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.1em',
|
||
color: GOLD,
|
||
}}
|
||
>
|
||
{fallbackText(header.brandLabel, mitgliedWerdenContent.header.brandLabel)}
|
||
</span>
|
||
<Link
|
||
to={fallbackText(header.backUrl, mitgliedWerdenContent.header.backUrl)}
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.15em',
|
||
color: 'rgba(255,255,255,0.4)',
|
||
textDecoration: 'none',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 6,
|
||
}}
|
||
>
|
||
<ArrowLeft size={12} />
|
||
{fallbackText(header.backLabel, mitgliedWerdenContent.header.backLabel)}
|
||
</Link>
|
||
</header>
|
||
|
||
{/* ── SECTION 2 – Hero ────────────────────────────────────────────────── */}
|
||
<section
|
||
style={{
|
||
background: NAVY,
|
||
padding: isMobile ? '64px 24px 48px' : '100px 80px 80px',
|
||
position: 'relative',
|
||
overflow: 'hidden',
|
||
}}
|
||
>
|
||
{/* Background image */}
|
||
<Image unoptimized
|
||
src={mediaUrl(hero.image, `/images/${mitgliedWerdenContent.hero.imageFilename}`)}
|
||
alt={mediaAlt(hero.image, fallbackText(hero.imageAlt, mitgliedWerdenContent.hero.imageAlt))}
|
||
aria-hidden="true"
|
||
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 40%' }}
|
||
/>
|
||
{/* Dark overlay so text remains readable */}
|
||
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to right, rgba(17,29,85,0.92) 0%, rgba(17,29,85,0.75) 55%, rgba(17,29,85,0.45) 100%)' }} />
|
||
<div
|
||
style={{
|
||
position: 'relative',
|
||
zIndex: 1,
|
||
display: 'grid',
|
||
gridTemplateColumns: isMobile ? '1fr' : '1fr auto',
|
||
alignItems: 'center',
|
||
gap: isMobile ? 40 : 60,
|
||
}}
|
||
>
|
||
{/* Left col */}
|
||
<div style={{ maxWidth: 760 }}>
|
||
<span
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
letterSpacing: '0.35em',
|
||
textTransform: 'uppercase',
|
||
color: GOLD,
|
||
display: 'block',
|
||
marginBottom: 24,
|
||
}}
|
||
>
|
||
{fallbackText(hero.eyebrow, mitgliedWerdenContent.hero.eyebrow)}
|
||
</span>
|
||
<h1
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: isMobile ? 'clamp(2rem, 9vw, 2.6rem)' : 'clamp(2.8rem, 6vw, 5rem)',
|
||
fontWeight: 900,
|
||
color: '#fff',
|
||
textTransform: 'uppercase',
|
||
lineHeight: 0.95,
|
||
letterSpacing: '-0.03em',
|
||
margin: '0 0 28px',
|
||
}}
|
||
>
|
||
{fallbackText(hero.heading, mitgliedWerdenContent.hero.heading)}
|
||
</h1>
|
||
<div style={{ width: 48, height: 3, background: GOLD, marginBottom: 28 }} />
|
||
<p
|
||
style={{
|
||
fontFamily: FB,
|
||
fontSize: 18,
|
||
fontWeight: 300,
|
||
color: 'rgba(255,255,255,0.55)',
|
||
maxWidth: 520,
|
||
lineHeight: 1.75,
|
||
margin: 0,
|
||
}}
|
||
>
|
||
<TextLines text={fallbackText(hero.description, mitgliedWerdenContent.hero.description)} />
|
||
</p>
|
||
</div>
|
||
|
||
{/* Right col – typographic stat block */}
|
||
{!isMobile && (
|
||
<div
|
||
style={{
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'flex-end',
|
||
paddingRight: 16,
|
||
}}
|
||
>
|
||
{heroStats.map((stat, index) => (
|
||
<React.Fragment key={`${stat.value}-${stat.label}`}>
|
||
<div style={{ textAlign: 'right' }}>
|
||
<div
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: index === 0 ? 72 : 48,
|
||
fontWeight: 900,
|
||
color: index === 0 ? GOLD : '#fff',
|
||
lineHeight: 1,
|
||
letterSpacing: index === 0 ? '-0.03em' : '-0.02em',
|
||
}}
|
||
>
|
||
{stat.value}
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.3em',
|
||
color: 'rgba(255,255,255,0.35)',
|
||
marginTop: 4,
|
||
}}
|
||
>
|
||
{stat.label}
|
||
</div>
|
||
</div>
|
||
|
||
{index < heroStats.length - 1 && (
|
||
<div
|
||
style={{
|
||
width: '100%',
|
||
height: 1,
|
||
background: 'rgba(255,255,255,0.1)',
|
||
margin: '32px 0',
|
||
}}
|
||
/>
|
||
)}
|
||
</React.Fragment>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Gold bottom gradient rule */}
|
||
<div
|
||
style={{
|
||
position: 'absolute',
|
||
bottom: 0,
|
||
left: 0,
|
||
right: 0,
|
||
height: 2,
|
||
background: `linear-gradient(to right, ${GOLD}, rgba(239,191,4,0.3), transparent)`,
|
||
}}
|
||
/>
|
||
</section>
|
||
|
||
{/* ── SECTION 3 – Benefit cards ───────────────────────────────────────── */}
|
||
<section style={{ background: CREAM, position: 'relative', overflow: 'hidden', isolation: 'isolate' }}>
|
||
<MunichSkylineBg />
|
||
<div
|
||
style={{
|
||
position: 'relative',
|
||
zIndex: 1,
|
||
padding: isMobile ? '48px 24px' : '80px 80px 72px',
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.32em',
|
||
color: BLUE,
|
||
display: 'block',
|
||
marginBottom: 16,
|
||
}}
|
||
>
|
||
{fallbackText(benefits.eyebrow, mitgliedWerdenContent.benefits.eyebrow)}
|
||
</span>
|
||
<h2
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 'clamp(2rem, 4vw, 3rem)',
|
||
fontWeight: 900,
|
||
color: '#101828',
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '-0.025em',
|
||
lineHeight: 1.03,
|
||
margin: '0 0 20px',
|
||
}}
|
||
>
|
||
{fallbackText(benefits.heading, mitgliedWerdenContent.benefits.heading)}
|
||
</h2>
|
||
<div style={{ width: 40, height: 2, background: GOLD, marginBottom: 48 }} />
|
||
|
||
<div
|
||
style={{
|
||
display: 'grid',
|
||
gridTemplateColumns: isMobile ? '1fr' : 'repeat(4, 1fr)',
|
||
borderTop: '1px solid rgba(3,9,58,0.1)',
|
||
}}
|
||
>
|
||
{benefitCards.map((card, index) => (
|
||
<BenefitCard
|
||
key={`${card.title}-${index}`}
|
||
icon={iconMap[card.icon] || iconMap.users}
|
||
title={card.title}
|
||
body={card.body}
|
||
isLast={index === benefitCards.length - 1}
|
||
isMobile={isMobile}
|
||
/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* ── SECTION 4 – Über den Verein ─────────────────────────────────────── */}
|
||
<section
|
||
style={{
|
||
background: '#fff',
|
||
display: 'grid',
|
||
gridTemplateColumns: isMobile ? '1fr' : '45% 55%',
|
||
position: 'relative',
|
||
overflow: 'hidden',
|
||
}}
|
||
>
|
||
{/* Left col */}
|
||
<div style={{ padding: isMobile ? '48px 24px' : '80px 64px' }}>
|
||
<span
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.32em',
|
||
color: BLUE,
|
||
display: 'block',
|
||
marginBottom: 20,
|
||
}}
|
||
>
|
||
{fallbackText(about.eyebrow, mitgliedWerdenContent.about.eyebrow)}
|
||
</span>
|
||
<h2
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 'clamp(2rem, 3.5vw, 3rem)',
|
||
fontWeight: 900,
|
||
color: '#101828',
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '-0.025em',
|
||
lineHeight: 1.03,
|
||
margin: '0 0 20px',
|
||
}}
|
||
>
|
||
{fallbackText(about.heading, mitgliedWerdenContent.about.heading)}
|
||
</h2>
|
||
<div style={{ width: 40, height: 2, background: GOLD, marginBottom: 32 }} />
|
||
{aboutParagraphs.map((paragraph, index) => (
|
||
<p
|
||
key={`${paragraph}-${index}`}
|
||
style={{
|
||
fontFamily: FB,
|
||
fontSize: 18,
|
||
color: 'rgba(16,24,40,0.55)',
|
||
lineHeight: 1.85,
|
||
margin: index === aboutParagraphs.length - 1 ? '0 0 40px' : index === 0 ? '0 0 20px' : '0 0 40px',
|
||
}}
|
||
>
|
||
{paragraph}
|
||
</p>
|
||
))}
|
||
<div style={{ width: 40, height: 1, background: GOLD, marginBottom: 16 }} />
|
||
<span
|
||
style={{
|
||
fontFamily: FB,
|
||
fontSize: 17,
|
||
color: 'rgba(16,24,40,0.5)',
|
||
letterSpacing: '0.05em',
|
||
}}
|
||
>
|
||
{fallbackText(about.descriptor, mitgliedWerdenContent.about.descriptor)}
|
||
</span>
|
||
</div>
|
||
|
||
{/* Right col */}
|
||
<div
|
||
style={{
|
||
padding: isMobile ? '0 24px 48px' : '80px 64px',
|
||
borderLeft: isMobile ? 'none' : '1px solid rgba(16,24,40,0.08)',
|
||
borderTop: isMobile ? '1px solid rgba(16,24,40,0.08)' : 'none',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
justifyContent: 'center',
|
||
}}
|
||
>
|
||
{aboutFacts.map((item, i) => (
|
||
<div
|
||
key={i}
|
||
style={{
|
||
display: 'grid',
|
||
gridTemplateColumns: '40px 1fr',
|
||
gap: '0 16px',
|
||
padding: '20px 0',
|
||
borderBottom: '1px solid rgba(16,24,40,0.08)',
|
||
alignItems: 'start',
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
color: GOLD,
|
||
letterSpacing: '0.1em',
|
||
paddingTop: 2,
|
||
}}
|
||
>
|
||
{item.num}
|
||
</span>
|
||
<div>
|
||
<div
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 16,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.07em',
|
||
color: '#101828',
|
||
marginBottom: 6,
|
||
}}
|
||
>
|
||
{item.label}
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontFamily: FB,
|
||
fontSize: 18,
|
||
color: 'rgba(16,24,40,0.5)',
|
||
lineHeight: 1.6,
|
||
}}
|
||
>
|
||
{item.body}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
{/* ── SECTION 5 – Beitragsrechner ─────────────────────────────────────── */}
|
||
<BeitragsrechnerSection isMobile={isMobile} content={pricing} />
|
||
|
||
{/* ── Antrag-Wahl Strip ────────────────────────────────────────────────── */}
|
||
<PDFDownloadSection isMobile={isMobile} content={applicationOptions} />
|
||
|
||
{/* ── SECTION 6 – Formular ────────────────────────────────────────────── */}
|
||
<section
|
||
id="mitglied-formular"
|
||
style={{ background: NAVY, position: 'relative', overflow: 'hidden', height: isMobile ? 'auto' : 'calc(100vh - 60px)', display: 'flex', flexDirection: 'column' }}
|
||
>
|
||
<div
|
||
style={{
|
||
display: 'grid',
|
||
gridTemplateColumns: isMobile ? '1fr' : '4fr 1px 8fr',
|
||
flex: 1,
|
||
minHeight: 0,
|
||
overflow: isMobile ? 'visible' : 'hidden',
|
||
}}
|
||
>
|
||
{/* Left – pitch */}
|
||
<div
|
||
style={{
|
||
padding: isMobile ? '32px 24px 24px' : '36px 36px 32px 52px',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
justifyContent: 'center',
|
||
overflow: isMobile ? 'visible' : 'hidden',
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.32em',
|
||
color: GOLD,
|
||
display: 'block',
|
||
marginBottom: 20,
|
||
}}
|
||
>
|
||
{fallbackText(formIntro.eyebrow, mitgliedWerdenContent.formIntro.eyebrow)}
|
||
</span>
|
||
<h2
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 'clamp(2rem, 3.2vw, 2.8rem)',
|
||
fontWeight: 900,
|
||
color: '#fff',
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '-0.025em',
|
||
lineHeight: 1.04,
|
||
margin: '0 0 20px',
|
||
}}
|
||
>
|
||
{fallbackText(formIntro.heading, mitgliedWerdenContent.formIntro.heading)}
|
||
</h2>
|
||
<div style={{ width: 36, height: 2, background: GOLD, marginBottom: 28 }} />
|
||
<p
|
||
style={{
|
||
fontFamily: FB,
|
||
fontSize: 18,
|
||
color: 'rgba(255,255,255,0.5)',
|
||
lineHeight: 1.8,
|
||
maxWidth: 400,
|
||
marginBottom: 48,
|
||
}}
|
||
>
|
||
<TextLines text={fallbackText(formIntro.description, mitgliedWerdenContent.formIntro.description)} />
|
||
</p>
|
||
<div style={{ borderTop: '1px solid rgba(255,255,255,0.08)' }}>
|
||
{formPromises.map((item, i) => (
|
||
<div
|
||
key={i}
|
||
style={{
|
||
display: 'grid',
|
||
gridTemplateColumns: '36px 1fr',
|
||
gap: '0 16px',
|
||
padding: '16px 0',
|
||
borderBottom: '1px solid rgba(255,255,255,0.06)',
|
||
alignItems: 'center',
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
color: 'rgba(239,191,4,0.6)',
|
||
letterSpacing: '0.1em',
|
||
}}
|
||
>
|
||
{item.num}
|
||
</span>
|
||
<div
|
||
style={{
|
||
display: 'flex',
|
||
flexDirection: isMobile ? 'column' : 'row',
|
||
justifyContent: 'space-between',
|
||
alignItems: isMobile ? 'flex-start' : 'baseline',
|
||
gap: isMobile ? 4 : 0,
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 15,
|
||
fontWeight: 700,
|
||
color: '#fff',
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.08em',
|
||
}}
|
||
>
|
||
{item.label}
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontFamily: FB,
|
||
fontSize: isMobile ? 15 : 17,
|
||
color: 'rgba(255,255,255,0.3)',
|
||
}}
|
||
>
|
||
{item.desc}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Center divider */}
|
||
<div
|
||
style={{
|
||
background: 'rgba(255,255,255,0.07)',
|
||
display: isMobile ? 'none' : undefined,
|
||
}}
|
||
/>
|
||
|
||
{/* Right – wizard */}
|
||
<div style={{ display: 'flex', flexDirection: 'column', background: 'rgba(0,0,0,0.18)', overflowY: isMobile ? 'visible' : 'auto', minHeight: 0 }}>
|
||
{!isMobile && (
|
||
<div style={{ height: 180, position: 'relative', overflow: 'hidden', flexShrink: 0 }}>
|
||
<Image unoptimized src={mediaUrl(formIntro.image, `/images/${mitgliedWerdenContent.formIntro.imageFilename}`)} alt={mediaAlt(formIntro.image, fallbackText(formIntro.imageAlt, mitgliedWerdenContent.formIntro.imageAlt))} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 30%' }} />
|
||
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to bottom, rgba(17,29,85,0.5) 0%, rgba(0,0,0,0.1) 50%, rgba(0,0,0,0.45) 100%)' }} />
|
||
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: 3, background: GOLD }} />
|
||
<div style={{ position: 'absolute', bottom: 16, left: 40, display: 'flex', alignItems: 'center', gap: 8 }}>
|
||
<div style={{ width: 5, height: 5, borderRadius: '50%', background: GOLD }} />
|
||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: 'rgba(255,255,255,0.85)', textTransform: 'uppercase', letterSpacing: '0.2em' }}>{fallbackText(formIntro.imageLabel, mitgliedWerdenContent.formIntro.imageLabel)}</span>
|
||
</div>
|
||
</div>
|
||
)}
|
||
<div style={{ padding: isMobile ? '24px' : '28px 40px 32px', flex: 1, overflowY: isMobile ? 'visible' : 'auto' }}>
|
||
<MembershipWizard content={cms.wizard} pricing={pricing} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style={{ height: 2, background: 'linear-gradient(to right, #EFBF04, rgba(239,191,4,0.25), transparent)', flexShrink: 0 }} />
|
||
</section>
|
||
|
||
{/* ── SECTION 7 – Testimonials ────────────────────────────────────────── */}
|
||
<section style={{ background: '#fff', position: 'relative', overflow: 'hidden' }}>
|
||
<div style={{ padding: isMobile ? '48px 24px' : '80px' }}>
|
||
<span
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.32em',
|
||
color: BLUE,
|
||
display: 'block',
|
||
marginBottom: 16,
|
||
}}
|
||
>
|
||
{fallbackText(testimonials.eyebrow, mitgliedWerdenContent.testimonials.eyebrow)}
|
||
</span>
|
||
<h2
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 'clamp(2rem, 4vw, 3rem)',
|
||
fontWeight: 900,
|
||
color: '#101828',
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '-0.025em',
|
||
lineHeight: 1.03,
|
||
margin: '0 0 20px',
|
||
}}
|
||
>
|
||
{fallbackText(testimonials.heading, mitgliedWerdenContent.testimonials.heading)}
|
||
</h2>
|
||
<div style={{ width: 40, height: 2, background: GOLD, marginBottom: 48 }} />
|
||
|
||
<div
|
||
style={{
|
||
display: 'grid',
|
||
gridTemplateColumns: isMobile ? '1fr' : 'repeat(3, 1fr)',
|
||
gap: 24,
|
||
}}
|
||
>
|
||
{testimonialItems.map((item, index) => (
|
||
<QuoteCard key={`${item.name}-${index}`} quote={item.quote} initials={item.initials} name={item.name} role={item.role} />
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* ── FAQ ─────────────────────────────────────────────────────────────── */}
|
||
<FAQSection isMobile={isMobile} content={faq} />
|
||
|
||
{/* ── SECTION 8 – Bottom CTA ──────────────────────────────────────────── */}
|
||
<section
|
||
style={{
|
||
background: NAVY,
|
||
padding: isMobile ? '48px 24px' : '80px',
|
||
textAlign: 'center',
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.32em',
|
||
color: GOLD,
|
||
display: 'block',
|
||
marginBottom: 20,
|
||
}}
|
||
>
|
||
{fallbackText(bottomCta.eyebrow, mitgliedWerdenContent.bottomCta.eyebrow)}
|
||
</span>
|
||
<h2
|
||
style={{
|
||
fontFamily: FF,
|
||
fontSize: 'clamp(2rem, 4vw, 3.2rem)',
|
||
fontWeight: 900,
|
||
color: '#fff',
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '-0.025em',
|
||
lineHeight: 1.03,
|
||
margin: '0 auto 20px',
|
||
maxWidth: 640,
|
||
}}
|
||
>
|
||
{fallbackText(bottomCta.heading, mitgliedWerdenContent.bottomCta.heading)}
|
||
</h2>
|
||
<div
|
||
style={{
|
||
width: 40,
|
||
height: 2,
|
||
background: GOLD,
|
||
margin: '0 auto 40px',
|
||
}}
|
||
/>
|
||
<a
|
||
href={fallbackText(bottomCta.ctaHref, mitgliedWerdenContent.bottomCta.ctaHref)}
|
||
style={{
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 10,
|
||
background: GOLD,
|
||
color: '#101828',
|
||
fontFamily: FF,
|
||
fontSize: 17,
|
||
fontWeight: 700,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.15em',
|
||
padding: '16px 36px',
|
||
textDecoration: 'none',
|
||
transition: 'background 0.2s, box-shadow 0.2s',
|
||
}}
|
||
onMouseEnter={e => {
|
||
(e.currentTarget as HTMLElement).style.background = '#FFD130';
|
||
(e.currentTarget as HTMLElement).style.boxShadow =
|
||
'0 0 18px rgba(239,191,4,0.65), 0 0 40px rgba(239,191,4,0.3)';
|
||
}}
|
||
onMouseLeave={e => {
|
||
(e.currentTarget as HTMLElement).style.background = GOLD;
|
||
(e.currentTarget as HTMLElement).style.boxShadow = 'none';
|
||
}}
|
||
>
|
||
{fallbackText(bottomCta.ctaLabel, mitgliedWerdenContent.bottomCta.ctaLabel)}
|
||
<ChevronRight size={14} />
|
||
</a>
|
||
</section>
|
||
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default MitgliedWerden;
|