799 lines
56 KiB
TypeScript
799 lines
56 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import { Link } from '@/spa/router';
|
||
import { ChevronRight, X, ArrowRight, Globe, Linkedin, Building2, MapPin, ExternalLink } from 'lucide-react';
|
||
import SponsoringForm from '@/spa/components/forms/SponsoringForm';
|
||
import MunichSkylineBg from '@/spa/components/ui/munich-skyline-bg';
|
||
import { useIsMobile } from '@/spa/hooks/useIsMobile';
|
||
import { useCmsRoute } from '@/spa/cmsRoute';
|
||
import { mediaAlt, mediaUrl } from '@/spa/cmsMediaField';
|
||
import { netzwerkContent } from '@/spa/netzwerkContent';
|
||
import Image from '@/spa/components/ui/UnoptimizedImage'
|
||
|
||
const FF = '"IBM Plex Sans", sans-serif';
|
||
const FB = '"Inter", sans-serif';
|
||
const NAVY = '#111D55';
|
||
const GOLD = '#EFBF04';
|
||
const CREAM = '#E4E2E3';
|
||
|
||
const ROLE_COLOR: Record<string, string> = {
|
||
'Hauptsponsor': '#EFBF04',
|
||
'Sponsor': '#2563EB',
|
||
'Finanzpartner': '#16A34A',
|
||
'Partner': '#111D55',
|
||
'Medienpartner': '#DC2626',
|
||
};
|
||
|
||
type NetzwerkCms = Partial<typeof netzwerkContent> & {
|
||
hero?: Partial<typeof netzwerkContent.hero> & { image?: unknown }
|
||
sponsoring?: Partial<typeof netzwerkContent.sponsoring> & { image?: unknown }
|
||
}
|
||
|
||
type PatronPerson = (typeof netzwerkContent.patronage.people)[number]
|
||
type JuryMember = (typeof netzwerkContent.jury.members)[number]
|
||
type Partner = (typeof netzwerkContent.partners.items)[number] & { tier: 1 | 2 | 3; heroImg?: string; linkedin?: string; logoSubtext?: string }
|
||
type PartnerTier = (typeof netzwerkContent.partners.tiers)[number]
|
||
type ExpertiseRow = (typeof netzwerkContent.expertise.rows)[number]
|
||
type StatItem = (typeof netzwerkContent.juryIntro.stats)[number]
|
||
type BenefitItem = (typeof netzwerkContent.sponsoring.benefits)[number]
|
||
type PartnerModalCopy = typeof netzwerkContent.partners.modal
|
||
|
||
const fallbackText = (value: unknown, fallback: string) => typeof value === 'string' && value.length > 0 ? value : fallback;
|
||
const fallbackArray = <T,>(value: unknown, fallback: T[]) => Array.isArray(value) && value.length ? value as T[] : fallback;
|
||
|
||
function Lines({ text, highlight }: { text: string; highlight?: string }) {
|
||
return (
|
||
<>
|
||
{text.split('\n').map((line, i) => {
|
||
const highlighted = highlight && line.includes(highlight)
|
||
return (
|
||
<React.Fragment key={`${line}-${i}`}>
|
||
{i > 0 && <br />}
|
||
{highlighted ? (
|
||
<>
|
||
{line.replace(highlight, '')}<span style={{ color: GOLD }}>{highlight}</span>
|
||
</>
|
||
) : line}
|
||
</React.Fragment>
|
||
)
|
||
})}
|
||
</>
|
||
)
|
||
}
|
||
|
||
// ── PersonCard (Schirmherrschaft) ─────────────────────────────────────────────
|
||
|
||
interface PersonCardProps {
|
||
imgSrc: string;
|
||
imgAlt: string;
|
||
name: string;
|
||
titleLine1: string;
|
||
titleLine2: string;
|
||
institution: string;
|
||
borderRight?: boolean;
|
||
}
|
||
|
||
function PersonCard({ imgSrc, imgAlt, name, titleLine1, titleLine2, institution, borderRight }: PersonCardProps) {
|
||
const [hovered, setHovered] = useState(false);
|
||
const isMobileCard = useIsMobile();
|
||
return (
|
||
<div
|
||
style={{ borderRight: borderRight && !isMobileCard ? '1px solid rgba(255,255,255,0.08)' : 'none', display: 'flex', flexDirection: 'column', ...(isMobileCard ? { minWidth: '85vw', maxWidth: '85vw', flexShrink: 0, scrollSnapAlign: 'start', borderRadius: 16, overflow: 'hidden' } : {}) }}
|
||
onMouseEnter={() => setHovered(true)}
|
||
onMouseLeave={() => setHovered(false)}
|
||
>
|
||
<div style={{ position: 'relative', height: isMobileCard ? 250 : 320, overflow: 'hidden', width: '100%' }}>
|
||
<Image unoptimized
|
||
src={imgSrc}
|
||
alt={imgAlt}
|
||
style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block', filter: hovered ? 'grayscale(0%)' : 'grayscale(100%)', transition: 'filter 0.55s ease, transform 0.55s ease', transform: hovered ? 'scale(1.04)' : 'scale(1)' }}
|
||
/>
|
||
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to top, rgba(3,9,58,0.8) 0%, transparent 60%)', pointerEvents: 'none' }} />
|
||
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: 3, background: GOLD }} />
|
||
</div>
|
||
<div style={{ padding: isMobileCard ? '28px 24px' : '36px 40px', background: NAVY, flex: 1 }}>
|
||
<div style={{ fontFamily: FF, fontSize: 20, fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.1, marginBottom: 10 }}>{name}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 17, fontWeight: 400, color: `${GOLD}CC`, lineHeight: 1.6, marginBottom: 2 }}>{titleLine1}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 17, fontWeight: 400, color: 'rgba(255,255,255,0.5)', lineHeight: 1.6 }}>{titleLine2}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.2em', color: 'rgba(255,255,255,0.3)', marginTop: 12 }}>{institution}</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── JuryCard ──────────────────────────────────────────────────────────────────
|
||
|
||
const JuryCard: React.FC<{ member: JuryMember; idx: number }> = ({ member, idx }) => {
|
||
const [hovered, setHovered] = useState(false);
|
||
const isMobileCard = useIsMobile();
|
||
return (
|
||
<div
|
||
onMouseEnter={() => setHovered(true)}
|
||
onMouseLeave={() => setHovered(false)}
|
||
style={{ borderLeft: !isMobileCard && idx > 0 ? '1px solid rgba(255,255,255,0.07)' : 'none', display: 'flex', flexDirection: 'column', cursor: 'default', background: hovered ? 'rgba(255,255,255,0.03)' : 'transparent', transition: 'background 0.2s', ...(isMobileCard ? { minWidth: '74vw', maxWidth: '74vw', flexShrink: 0, scrollSnapAlign: 'start', border: '1px solid rgba(255,255,255,0.08)', borderRadius: 16, overflow: 'hidden' } : {}) }}
|
||
>
|
||
<div style={{ position: 'relative', overflow: 'hidden', aspectRatio: '3/4' }}>
|
||
<Image unoptimized src={member.image} alt={member.name} style={{ width: '100%', height: '100%', objectFit: 'cover', filter: hovered ? 'none' : 'grayscale(100%)', transition: 'filter 0.5s', display: 'block' }} />
|
||
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: '40%', background: 'linear-gradient(to top, rgba(3,9,58,0.65), transparent)' }} />
|
||
</div>
|
||
<div style={{ padding: isMobileCard ? '28px 28px 32px' : '32px 32px 36px' }}>
|
||
<div style={{ fontFamily: FF, fontSize: 20, fontWeight: 700, color: '#fff', marginBottom: 5 }}>{member.name}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 12, color: GOLD, textTransform: 'uppercase', letterSpacing: '0.16em', fontWeight: 700, marginBottom: 14 }}>{member.role}</div>
|
||
<div style={{ width: hovered ? '100%' : 24, height: 1, background: GOLD, opacity: 0.4, transition: 'width 0.4s ease', marginBottom: 14 }} />
|
||
<div style={{ fontFamily: FB, fontSize: 18, color: 'rgba(255,255,255,0.55)', lineHeight: 1.7 }}>{member.bio}</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
// ── JuryChairCard (Vorsitz – prominent) ───────────────────────────────────────
|
||
|
||
const JuryChairCard: React.FC<{ member: JuryMember; badge: string }> = ({ member, badge }) => {
|
||
const [hovered, setHovered] = useState(false);
|
||
const isMobileCard = useIsMobile();
|
||
return (
|
||
<div
|
||
onMouseEnter={() => setHovered(true)}
|
||
onMouseLeave={() => setHovered(false)}
|
||
style={{
|
||
display: 'grid',
|
||
gridTemplateColumns: isMobileCard ? '1fr' : '380px 1fr',
|
||
gap: 0,
|
||
background: 'rgba(255,255,255,0.03)',
|
||
border: `1px solid ${GOLD}40`,
|
||
borderRadius: 20,
|
||
overflow: 'hidden',
|
||
}}
|
||
>
|
||
<div style={{ position: 'relative', overflow: 'hidden', aspectRatio: isMobileCard ? '16/10' : '3/4', minHeight: isMobileCard ? 240 : 'auto' }}>
|
||
<Image unoptimized src={member.image} alt={member.name} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center top', filter: hovered ? 'none' : 'grayscale(100%)', transition: 'filter 0.5s', display: 'block' }} />
|
||
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: '45%', background: 'linear-gradient(to top, rgba(3,9,58,0.7), transparent)' }} />
|
||
</div>
|
||
<div style={{ padding: isMobileCard ? '32px 28px 36px' : '56px 56px', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
|
||
<span style={{ alignSelf: 'flex-start', fontFamily: FF, fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.18em', color: NAVY, background: GOLD, padding: '7px 16px', borderRadius: 999, marginBottom: 24 }}>
|
||
{badge}
|
||
</span>
|
||
<div style={{ fontFamily: FF, fontSize: isMobileCard ? 26 : 34, fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.05, marginBottom: 10 }}>{member.name}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 13, color: GOLD, textTransform: 'uppercase', letterSpacing: '0.18em', fontWeight: 700, marginBottom: 24 }}>{member.role}</div>
|
||
<div style={{ width: 48, height: 2, background: GOLD, opacity: 0.5, marginBottom: 24 }} />
|
||
<div style={{ fontFamily: FB, fontSize: isMobileCard ? 18 : 21, color: 'rgba(255,255,255,0.6)', lineHeight: 1.7, maxWidth: 620 }}>{member.bio}</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
// ── PartnerCell ───────────────────────────────────────────────────────────────
|
||
|
||
// ── SponsorCell (tiered) ──────────────────────────────────────────────────────
|
||
|
||
function PartnerLogo({ partner }: { partner: Partner }) {
|
||
const color = fallbackText(partner.logoColor, '#111D55');
|
||
const text = fallbackText(partner.logoText, partner.name);
|
||
const subtext = fallbackText(partner.logoSubtext, '');
|
||
|
||
if (partner.logoKind === 'badge') {
|
||
return <span style={{ background: color, color: '#fff', fontWeight: 900, fontSize: 14, padding: '4px 10px', lineHeight: 1.25, display: 'inline-block', textAlign: 'center' as const, fontFamily: FF }}>{text.split('\n').map((line, index) => <React.Fragment key={`${line}-${index}`}>{index > 0 && <br />}{line}</React.Fragment>)}</span>
|
||
}
|
||
|
||
if (partner.logoKind === 'monogram') {
|
||
return <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}><span style={{ width: 22, height: 22, background: '#3a3a3a', color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 900, fontSize: 11, fontFamily: FF }}>{subtext || text.slice(0, 2)}</span><span style={{ fontWeight: 700, fontSize: 15, color, fontFamily: FF }}>{text}</span></span>
|
||
}
|
||
|
||
if (partner.logoKind === 'dot') {
|
||
return <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}><span style={{ width: 20, height: 20, borderRadius: '50%', background: color, display: 'block' }} /><span style={{ fontWeight: 700, fontSize: 16, color, fontFamily: FF }}>{text}</span></span>
|
||
}
|
||
|
||
if (partner.logoKind === 'leaf') {
|
||
return <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}><svg width="20" height="20" viewBox="0 0 20 20"><circle cx="10" cy="10" r="9" fill={color} /><path d="M10 3 Q14 7 14 10 Q14 14 10 17 Q6 14 6 10 Q6 7 10 3Z" fill="white" opacity="0.8" /></svg><span style={{ fontWeight: 700, fontSize: 15, color, fontFamily: FB }}>{text}</span></span>
|
||
}
|
||
|
||
if (partner.logoKind === 'rsm') {
|
||
return <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}><span style={{ display: 'flex', gap: 2 }}><span style={{ width: 10, height: 10, background: '#6b7280', display: 'block' }} /><span style={{ width: 10, height: 10, background: '#22c55e', display: 'block' }} /><span style={{ width: 10, height: 10, background: '#3b82f6', display: 'block' }} /></span><span style={{ fontWeight: 900, fontSize: 15, letterSpacing: '-0.02em', color, fontFamily: FF }}>{text} {subtext && <span style={{ color: '#6b7280', fontWeight: 600 }}>{subtext}</span>}</span></span>
|
||
}
|
||
|
||
if (partner.logoKind === 'deutschebank') {
|
||
return <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}><svg width="20" height="20" viewBox="0 0 20 20"><rect x="1" y="1" width="18" height="18" fill="none" stroke={color} strokeWidth="1.5" /><line x1="5" y1="15" x2="15" y2="5" stroke={color} strokeWidth="2" /></svg><span style={{ fontWeight: 600, fontSize: 15, color, fontFamily: FB }}>{text}</span></span>
|
||
}
|
||
|
||
return <span style={{ fontWeight: 900, fontSize: partner.logoKind === 'serif' ? 21 : 16, letterSpacing: partner.logoKind === 'serif' ? '0.12em' : '-0.01em', color, fontFamily: partner.logoKind === 'serif' ? 'Georgia, serif' : FF }}>{text}</span>
|
||
}
|
||
|
||
const SponsorCell: React.FC<{ partner: Partner; variant: 'lg' | 'md' | 'sm'; idx: number; cols: number; detailLabel: string; premiumLabel: string; onClick: () => void }> = ({ partner, variant, idx, cols, detailLabel, premiumLabel, onClick }) => {
|
||
const [hovered, setHovered] = useState(false);
|
||
const isMobileCell = useIsMobile();
|
||
const lg = variant === 'lg';
|
||
const sm = variant === 'sm';
|
||
const lastCol = (idx % cols) === cols - 1;
|
||
return (
|
||
<div
|
||
onClick={onClick}
|
||
onMouseEnter={() => setHovered(true)}
|
||
onMouseLeave={() => setHovered(false)}
|
||
style={{
|
||
padding: isMobileCell ? '24px 20px' : lg ? '40px 36px' : sm ? '28px 28px' : '32px 32px',
|
||
borderRight: !lastCol && !isMobileCell ? '1px solid rgba(3,9,58,0.08)' : 'none',
|
||
borderBottom: '1px solid rgba(3,9,58,0.08)',
|
||
display: 'flex', flexDirection: 'column', gap: 14,
|
||
cursor: 'pointer',
|
||
background: hovered ? '#E8E5E4' : 'transparent',
|
||
transition: 'background 0.15s',
|
||
position: 'relative',
|
||
}}
|
||
>
|
||
{lg && (
|
||
<span style={{ position: 'absolute', top: 18, right: 18, fontFamily: FF, fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.18em', color: '#101828', background: GOLD, padding: '4px 10px', borderRadius: 100, zIndex: 1 }}>{premiumLabel}</span>
|
||
)}
|
||
<div style={{ height: lg ? 52 : 44, display: 'flex', alignItems: 'center' }}><PartnerLogo partner={partner} /></div>
|
||
<div style={{ width: hovered ? 48 : 20, height: 1, background: GOLD, transition: 'width 0.3s ease' }} />
|
||
<div style={{ fontFamily: FF, fontSize: lg ? 22 : 18, fontWeight: 700, color: '#101828', letterSpacing: '-0.01em', lineHeight: 1.15 }}>{partner.name}</div>
|
||
<div style={{ fontFamily: FF, fontSize: sm ? 10 : 11, color: GOLD, textTransform: 'uppercase', letterSpacing: '0.2em', fontWeight: 700 }}>{partner.role}</div>
|
||
{!sm && <div style={{ fontFamily: FB, fontSize: lg ? 17 : 16, color: 'rgba(16,24,40,0.45)', lineHeight: 1.6, flexGrow: 1 }}>{partner.desc}</div>}
|
||
<div style={{ fontFamily: FF, fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.12em', color: GOLD, fontWeight: 700, display: 'flex', alignItems: 'center', gap: 4 }}>
|
||
{detailLabel} <ArrowRight size={10} />
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
// ── PartnerModal ─────────────────────────────────────────────────────────────
|
||
|
||
function PartnerModal({ partner, copy, onClose }: { partner: Partner; copy: PartnerModalCopy; onClose: () => void }) {
|
||
const accentColor = ROLE_COLOR[partner.role] ?? GOLD;
|
||
|
||
useEffect(() => {
|
||
document.body.style.overflow = 'hidden';
|
||
return () => { document.body.style.overflow = ''; };
|
||
}, []);
|
||
|
||
return (
|
||
<div
|
||
onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
|
||
style={{
|
||
position: 'fixed', inset: 0, zIndex: 500,
|
||
background: 'rgba(0,0,0,0.4)',
|
||
backdropFilter: 'blur(10px)',
|
||
WebkitBackdropFilter: 'blur(10px)',
|
||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||
padding: 24, overflowY: 'auto',
|
||
}}
|
||
>
|
||
<div
|
||
onClick={(e) => e.stopPropagation()}
|
||
style={{
|
||
background: '#fff', width: '100%', maxWidth: 720,
|
||
boxShadow: '0 32px 80px rgba(0,0,0,0.35)',
|
||
overflow: 'hidden', flexShrink: 0,
|
||
maxHeight: '92vh', overflowY: 'auto',
|
||
}}
|
||
>
|
||
{/* ── Hero ── */}
|
||
<div style={{ position: 'relative', height: 220, flexShrink: 0, overflow: 'hidden' }}>
|
||
{/* Background */}
|
||
{partner.heroImg
|
||
? <Image unoptimized src={partner.heroImg} alt="" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }} />
|
||
: <div style={{ position: 'absolute', inset: 0, background: `linear-gradient(135deg, ${accentColor}33 0%, ${NAVY} 70%)` }} />
|
||
}
|
||
{/* Gradient to white */}
|
||
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to bottom, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0) 35%, rgba(255,255,255,0.6) 75%, #fff 100%)' }} />
|
||
|
||
{/* Category badge – top left */}
|
||
<div style={{ position: 'absolute', top: 20, left: 24 }}>
|
||
<span style={{
|
||
background: 'rgba(255,255,255,0.15)',
|
||
backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)',
|
||
border: '1px solid rgba(255,255,255,0.25)',
|
||
color: '#fff', padding: '5px 13px',
|
||
fontFamily: FF, fontSize: 9, fontWeight: 700,
|
||
textTransform: 'uppercase', letterSpacing: '0.22em',
|
||
display: 'inline-block',
|
||
}}>
|
||
{partner.role}
|
||
</span>
|
||
</div>
|
||
|
||
{/* Social + Close – top right */}
|
||
<div style={{ position: 'absolute', top: 16, right: 20, display: 'flex', gap: 8, alignItems: 'center' }}>
|
||
{partner.linkedin && (
|
||
<a href={partner.linkedin} target="_blank" rel="noopener noreferrer" style={{
|
||
width: 36, height: 36, borderRadius: '50%',
|
||
background: 'rgba(255,255,255,0.92)',
|
||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||
color: NAVY, textDecoration: 'none',
|
||
boxShadow: '0 2px 8px rgba(0,0,0,0.18)',
|
||
}}>
|
||
<Linkedin size={14} />
|
||
</a>
|
||
)}
|
||
{partner.website && (
|
||
<a href={`https://${partner.website}`} target="_blank" rel="noopener noreferrer" style={{
|
||
width: 36, height: 36, borderRadius: '50%',
|
||
background: 'rgba(255,255,255,0.92)',
|
||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||
color: NAVY, textDecoration: 'none',
|
||
boxShadow: '0 2px 8px rgba(0,0,0,0.18)',
|
||
}}>
|
||
<Globe size={14} />
|
||
</a>
|
||
)}
|
||
<button onClick={onClose} style={{
|
||
width: 36, height: 36, borderRadius: '50%',
|
||
background: 'rgba(255,255,255,0.92)',
|
||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||
border: 'none', cursor: 'pointer',
|
||
boxShadow: '0 2px 8px rgba(0,0,0,0.18)', color: NAVY,
|
||
}}>
|
||
<X size={15} />
|
||
</button>
|
||
</div>
|
||
|
||
{/* Logo box – bottom left (overflows hero) */}
|
||
<div style={{
|
||
position: 'absolute', bottom: -28, left: 28,
|
||
width: 80, height: 80, background: '#fff',
|
||
borderRadius: 12, boxShadow: '0 4px 24px rgba(0,0,0,0.14)',
|
||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||
padding: 10, zIndex: 2,
|
||
}}>
|
||
<PartnerLogo partner={partner} />
|
||
</div>
|
||
</div>
|
||
|
||
{/* ── Content ── */}
|
||
<div style={{ padding: '52px 32px 32px' }}>
|
||
<h2 style={{
|
||
fontFamily: FF, fontSize: 'clamp(1.5rem, 3vw, 2rem)',
|
||
fontWeight: 900, color: '#101828',
|
||
letterSpacing: '-0.025em', lineHeight: 1.05,
|
||
margin: '0 0 10px',
|
||
}}>
|
||
{partner.name}
|
||
</h2>
|
||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.5)', lineHeight: 1.65, margin: '0 0 24px' }}>
|
||
{partner.desc}
|
||
</p>
|
||
|
||
{/* 3 info cards */}
|
||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10, marginBottom: 28 }}>
|
||
{[
|
||
{ Icon: Building2, label: fallbackText(copy.industryLabel, netzwerkContent.partners.modal.industryLabel), value: partner.industry },
|
||
{ Icon: MapPin, label: fallbackText(copy.locationLabel, netzwerkContent.partners.modal.locationLabel), value: partner.location },
|
||
{ Icon: Globe, label: fallbackText(copy.webLabel, netzwerkContent.partners.modal.webLabel), value: partner.website },
|
||
].map(({ Icon, label, value }) => (
|
||
<div key={label} style={{ background: 'hsl(220,40%,97%)', padding: '14px 16px', borderRadius: 8 }}>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 6 }}>
|
||
<Icon size={11} color="rgba(16,24,40,0.35)" />
|
||
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.14em', color: 'rgba(16,24,40,0.35)' }}>{label}</span>
|
||
</div>
|
||
<div style={{ fontFamily: FB, fontSize: 17, fontWeight: 600, color: '#101828', lineHeight: 1.4, wordBreak: 'break-word' }}>{value}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Separator */}
|
||
<div style={{ height: 1, background: 'rgba(16,24,40,0.08)', marginBottom: 28 }} />
|
||
|
||
{/* Über section */}
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
|
||
<div style={{ width: 4, height: 20, background: accentColor, borderRadius: 2, flexShrink: 0 }} />
|
||
<span style={{ fontFamily: FF, fontSize: 17, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.12em', color: '#101828' }}>
|
||
{fallbackText(copy.aboutLabel, netzwerkContent.partners.modal.aboutLabel)}
|
||
</span>
|
||
</div>
|
||
<p style={{ fontFamily: FB, fontSize: 19, color: 'rgba(16,24,40,0.6)', lineHeight: 1.82, marginBottom: 36 }}>
|
||
{partner.fullDesc}
|
||
</p>
|
||
|
||
{/* CTAs */}
|
||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
|
||
{partner.website && (
|
||
<a
|
||
href={`https://${partner.website}`}
|
||
target="_blank" rel="noopener noreferrer"
|
||
style={{
|
||
padding: '13px 28px', background: GOLD, color: '#101828',
|
||
fontFamily: FF, fontSize: 11, fontWeight: 700,
|
||
textTransform: 'uppercase', letterSpacing: '0.1em',
|
||
textDecoration: 'none',
|
||
display: 'inline-flex', alignItems: 'center', gap: 8,
|
||
transition: 'background 0.15s, box-shadow 0.2s',
|
||
}}
|
||
onMouseEnter={e => { const el = e.currentTarget as HTMLElement; el.style.background = '#FFD130'; el.style.boxShadow = '0 0 18px rgba(239,191,4,0.65), 0 0 40px rgba(239,191,4,0.3)'; }}
|
||
onMouseLeave={e => { const el = e.currentTarget as HTMLElement; el.style.background = GOLD; el.style.boxShadow = 'none'; }}
|
||
>
|
||
{fallbackText(copy.websiteLabel, netzwerkContent.partners.modal.websiteLabel)} <ExternalLink size={12} />
|
||
</a>
|
||
)}
|
||
<button
|
||
onClick={onClose}
|
||
style={{
|
||
padding: '13px 28px', background: 'transparent',
|
||
color: 'rgba(16,24,40,0.45)', border: '1px solid rgba(16,24,40,0.12)',
|
||
fontFamily: FF, fontSize: 11, fontWeight: 700,
|
||
textTransform: 'uppercase', letterSpacing: '0.1em',
|
||
cursor: 'pointer', transition: 'all 0.15s',
|
||
}}
|
||
onMouseEnter={e => { const el = e.currentTarget as HTMLElement; el.style.background = NAVY; el.style.color = '#fff'; el.style.borderColor = NAVY; }}
|
||
onMouseLeave={e => { const el = e.currentTarget as HTMLElement; el.style.background = 'transparent'; el.style.color = 'rgba(16,24,40,0.45)'; el.style.borderColor = 'rgba(16,24,40,0.12)'; }}
|
||
>
|
||
{fallbackText(copy.closeLabel, netzwerkContent.partners.modal.closeLabel)}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ── Footer strip ── */}
|
||
<div style={{
|
||
background: 'hsl(220,20%,96%)', padding: '14px 32px',
|
||
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
|
||
borderTop: '1px solid rgba(16,24,40,0.06)',
|
||
}}>
|
||
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.12em', color: 'rgba(16,24,40,0.3)' }}>
|
||
{fallbackText(copy.footerTitle, netzwerkContent.partners.modal.footerTitle)}
|
||
</span>
|
||
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.12em', color: accentColor }}>
|
||
{fallbackText(copy.footerRolePrefix, netzwerkContent.partners.modal.footerRolePrefix)} {partner.role}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ── Netzwerk ──────────────────────────────────────────────────────────────────
|
||
|
||
const Netzwerk: React.FC = () => {
|
||
const isMobile = useIsMobile();
|
||
const [selectedPartner, setSelectedPartner] = useState<Partner | null>(null);
|
||
const cms = (useCmsRoute()?.doc?.netzwerk || {}) as NetzwerkCms;
|
||
const hero = { ...netzwerkContent.hero, ...(cms.hero || {}) };
|
||
const patronage = { ...netzwerkContent.patronage, ...(cms.patronage || {}) };
|
||
const juryIntro = { ...netzwerkContent.juryIntro, ...(cms.juryIntro || {}) };
|
||
const jury = { ...netzwerkContent.jury, ...(cms.jury || {}) };
|
||
const expertise = { ...netzwerkContent.expertise, ...(cms.expertise || {}) };
|
||
const partnerSection = { ...netzwerkContent.partners, ...(cms.partners || {}) };
|
||
const partnerModal = { ...netzwerkContent.partners.modal, ...((cms.partners as typeof netzwerkContent.partners | undefined)?.modal || {}) };
|
||
const sponsoring = { ...netzwerkContent.sponsoring, ...(cms.sponsoring || {}) };
|
||
const patronPeople = fallbackArray<PatronPerson>(patronage.people, netzwerkContent.patronage.people);
|
||
const juryMembers = fallbackArray<JuryMember>(jury.members, netzwerkContent.jury.members);
|
||
const expertiseRows = fallbackArray<ExpertiseRow>(expertise.rows, netzwerkContent.expertise.rows);
|
||
const partners = fallbackArray<Partner>(partnerSection.items, netzwerkContent.partners.items as Partner[]);
|
||
const partnerTiers = fallbackArray<PartnerTier>(partnerSection.tiers, netzwerkContent.partners.tiers);
|
||
const mobileStats = fallbackArray<StatItem>(juryIntro.stats, netzwerkContent.juryIntro.stats);
|
||
const desktopStats = fallbackArray<StatItem>(juryIntro.desktopStats, netzwerkContent.juryIntro.desktopStats);
|
||
const sponsorBenefits = fallbackArray<BenefitItem>(sponsoring.benefits, netzwerkContent.sponsoring.benefits);
|
||
|
||
return (
|
||
<div className="animate-fade-in">
|
||
|
||
{/* ── 1. HERO ──────────────────────────────────────────────────────────── */}
|
||
<section style={{ position: 'relative', minHeight: '72vh', display: 'flex', alignItems: 'flex-end', overflow: 'hidden', background: '#060C14' }}>
|
||
<Image unoptimized src={mediaUrl(hero.image, `/images/${netzwerkContent.hero.imageFilename}`)} alt={mediaAlt(hero.image, fallbackText(hero.imageAlt, netzwerkContent.hero.imageAlt))} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 30%' }} />
|
||
<div style={{ position: 'absolute', inset: 0, background: isMobile ? 'linear-gradient(to top, rgba(2,9,48,0.95) 0%, rgba(2,9,48,0.72) 38%, rgba(2,9,48,0.22) 72%, transparent 100%)' : 'linear-gradient(to right, #020930 0%, rgba(2,9,48,0.90) 38%, rgba(2,9,48,0.18) 65%, transparent 100%)' }} />
|
||
<div style={{ position: 'absolute', top: 0, left: 80, width: 2, height: '100%', background: `linear-gradient(to bottom, transparent, ${GOLD}, transparent)`, opacity: 0.4 }} />
|
||
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: 2, background: 'linear-gradient(to right, #EFBF04, rgba(239,191,4,0.3), transparent)', zIndex: 2 }} />
|
||
|
||
<div style={{ position: 'relative', zIndex: 1, padding: isMobile ? '0 24px 48px' : '0 80px 88px', maxWidth: 860 }}>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 28 }}>
|
||
<div style={{ width: 40, height: 2, background: GOLD }} />
|
||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, letterSpacing: '0.3em', textTransform: 'uppercase', color: GOLD }}>{fallbackText(hero.eyebrow, netzwerkContent.hero.eyebrow)}</span>
|
||
</div>
|
||
<h1 style={{ fontFamily: FF, fontSize: isMobile ? 'clamp(1.6rem, 6vw, 5rem)' : 'clamp(2.8rem, 6vw, 5rem)', fontWeight: 900, color: '#fff', lineHeight: 0.95, letterSpacing: '-0.03em', textTransform: 'uppercase', margin: '0 0 28px' }}>
|
||
<Lines text={fallbackText(hero.heading, netzwerkContent.hero.heading)} highlight={fallbackText(hero.highlight, netzwerkContent.hero.highlight)} />
|
||
</h1>
|
||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(255,255,255,0.65)', lineHeight: 1.7, maxWidth: 560, margin: 0, fontWeight: 300 }}>
|
||
{fallbackText(hero.description, netzwerkContent.hero.description)}
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
{/* ── 2. SCHIRMHERRSCHAFT ──────────────────────────────────────────────── */}
|
||
<section id="schirmherrschaft" style={{ background: NAVY }}>
|
||
<div style={{ padding: isMobile ? '48px 24px 32px' : '80px 80px 56px' }}>
|
||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, letterSpacing: '0.3em', textTransform: 'uppercase', color: GOLD, display: 'block', marginBottom: 16 }}>{fallbackText(patronage.eyebrow, netzwerkContent.patronage.eyebrow)}</span>
|
||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(2rem, 4vw, 3rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1, margin: '0 0 20px' }}>{fallbackText(patronage.heading, netzwerkContent.patronage.heading)}</h2>
|
||
<div style={{ width: 40, height: 2, background: GOLD, marginBottom: 14 }} />
|
||
<p style={{ fontFamily: FF, fontSize: 18, color: 'rgba(255,255,255,0.5)', margin: 0 }}>{fallbackText(patronage.description, netzwerkContent.patronage.description)}</p>
|
||
</div>
|
||
|
||
<div style={{ display: isMobile ? 'flex' : 'grid', gridTemplateColumns: isMobile ? undefined : '1fr 1fr', overflowX: isMobile ? 'auto' : undefined, scrollSnapType: isMobile ? 'x mandatory' : undefined, gap: isMobile ? 14 : 0, padding: isMobile ? '0 24px 28px' : 0, WebkitOverflowScrolling: 'touch', scrollbarWidth: 'none' }}>
|
||
{patronPeople.map((person, index) => (
|
||
<PersonCard
|
||
key={`${person.name}-${index}`}
|
||
imgSrc={fallbackText(person.image, netzwerkContent.patronage.people[index]?.image || '')}
|
||
imgAlt={fallbackText(person.imageAlt, person.name)}
|
||
name={fallbackText(person.name, netzwerkContent.patronage.people[index]?.name || '')}
|
||
titleLine1={fallbackText(person.titleLine1, netzwerkContent.patronage.people[index]?.titleLine1 || '')}
|
||
titleLine2={fallbackText(person.titleLine2, netzwerkContent.patronage.people[index]?.titleLine2 || '')}
|
||
institution={fallbackText(person.institution, netzwerkContent.patronage.people[index]?.institution || '')}
|
||
borderRight={index === 0}
|
||
/>
|
||
))}
|
||
</div>
|
||
|
||
{/* Grußwort – Ilse Aigner */}
|
||
<div style={{ background: 'rgba(255,255,255,0.03)', borderTop: '1px solid rgba(255,255,255,0.07)' }}>
|
||
<div style={{ padding: isMobile ? '32px 24px' : '64px 80px', display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '40% 60%', gap: isMobile ? '24px 0' : '0 64px', alignItems: 'flex-start' }}>
|
||
<div>
|
||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, letterSpacing: '0.3em', textTransform: 'uppercase', color: GOLD, display: 'block', marginBottom: 8 }}>{fallbackText(patronage.greetingEyebrow, netzwerkContent.patronage.greetingEyebrow)}</span>
|
||
<div style={{ fontFamily: FF, fontSize: 11, fontWeight: 400, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'rgba(255,255,255,0.3)', marginBottom: 16 }}>{fallbackText(patronage.greetingRole, netzwerkContent.patronage.greetingRole)}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 18, fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.15, marginBottom: 10 }}>{fallbackText(patronage.greetingName, netzwerkContent.patronage.greetingName)}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 18, color: 'rgba(255,255,255,0.5)', lineHeight: 1.6, marginBottom: 4 }}>{fallbackText(patronage.greetingTitleLine1, netzwerkContent.patronage.greetingTitleLine1)}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 17, color: 'rgba(255,255,255,0.4)', lineHeight: 1.6 }}>{fallbackText(patronage.greetingTitleLine2, netzwerkContent.patronage.greetingTitleLine2)}</div>
|
||
</div>
|
||
<div>
|
||
<blockquote style={{ fontFamily: FF, fontSize: 18, fontStyle: 'italic', color: 'rgba(255,255,255,0.8)', lineHeight: 1.75, margin: 0, paddingLeft: 24, borderLeft: `3px solid ${GOLD}` }}>
|
||
{fallbackText(patronage.greetingQuote, netzwerkContent.patronage.greetingQuote)}
|
||
</blockquote>
|
||
<div style={{ fontFamily: FF, fontSize: 11, color: 'rgba(255,255,255,0.35)', marginTop: 20, paddingLeft: 24 }}>
|
||
{fallbackText(patronage.greetingAttribution, netzwerkContent.patronage.greetingAttribution)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style={{ height: 2, background: `linear-gradient(to right, ${GOLD}, rgba(239,191,4,0.2), transparent)` }} />
|
||
</section>
|
||
|
||
|
||
{/* ── 3. JURY-INTRO (editorial 2-col split) ────────────────────────────── */}
|
||
<section style={{ display: isMobile ? 'block' : 'grid', gridTemplateColumns: isMobile ? undefined : '40% 60%', minHeight: isMobile ? 'auto' : 400, position: 'relative', overflow: 'hidden', isolation: 'isolate', background: isMobile ? NAVY : undefined }}>
|
||
{!isMobile && <MunichSkylineBg />}
|
||
{isMobile && <MunichSkylineBg inset opacity={0.07} />}
|
||
{isMobile ? (
|
||
/* Mobile: one cohesive navy module – copy + compact stat strip */
|
||
<div style={{ padding: '48px 24px', position: 'relative', zIndex: 1 }}>
|
||
<span style={{ fontFamily: FF, fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, color: GOLD, display: 'block', marginBottom: 18 }}>{fallbackText(juryIntro.eyebrow, netzwerkContent.juryIntro.eyebrow)}</span>
|
||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(1.7rem, 8vw, 2.2rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.08, margin: '0 0 22px' }}>
|
||
<Lines text={fallbackText(juryIntro.heading, netzwerkContent.juryIntro.heading)} />
|
||
</h2>
|
||
<div style={{ width: 40, height: 2, background: GOLD, marginBottom: 22 }} />
|
||
<p style={{ fontFamily: FB, fontSize: 16, color: 'rgba(255,255,255,0.55)', lineHeight: 1.75, margin: 0 }}>
|
||
{fallbackText(juryIntro.description, netzwerkContent.juryIntro.description)}
|
||
</p>
|
||
{/* Stat strip */}
|
||
<div style={{ display: 'flex', marginTop: 32, paddingTop: 26, borderTop: '1px solid rgba(255,255,255,0.14)' }}>
|
||
{mobileStats.map((s, i) => (
|
||
<div key={i} style={{ flex: 1, minWidth: 0, paddingLeft: i > 0 ? 14 : 0, borderLeft: i > 0 ? '1px solid rgba(255,255,255,0.14)' : 'none' }}>
|
||
<div style={{ fontFamily: FF, fontSize: 'clamp(2.2rem, 9vw, 3rem)', fontWeight: 900, color: GOLD, letterSpacing: '-0.03em', lineHeight: 1 }}>{fallbackText(s.value, netzwerkContent.juryIntro.stats[i]?.value || '')}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.12em', color: 'rgba(255,255,255,0.5)', marginTop: 8, lineHeight: 1.3 }}>{fallbackText(s.label, netzwerkContent.juryIntro.stats[i]?.label || '')}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<>
|
||
{/* Left – Cream, stats */}
|
||
<div style={{ background: CREAM, padding: '72px 64px', display: 'flex', flexDirection: 'column', justifyContent: 'center', position: 'relative', zIndex: 1 }}>
|
||
<div>
|
||
<div style={{ fontFamily: FF, fontSize: 'clamp(5rem, 8.5vw, 7.5rem)', fontWeight: 900, color: '#101828', letterSpacing: '-0.04em', lineHeight: 1 }}>{fallbackText(desktopStats[0]?.value, netzwerkContent.juryIntro.desktopStats[0].value)}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.15em', color: 'rgba(16,24,40,0.4)', marginTop: 4 }}>{fallbackText(desktopStats[0]?.label, netzwerkContent.juryIntro.desktopStats[0].label)}</div>
|
||
</div>
|
||
<div style={{ width: '100%', height: 1, background: 'rgba(3,9,58,0.1)', margin: '32px 0' }} />
|
||
<div>
|
||
<div style={{ fontFamily: FF, fontSize: 'clamp(2.4rem, 4vw, 3.4rem)', fontWeight: 900, color: '#101828', letterSpacing: '-0.03em', lineHeight: 1 }}>{fallbackText(desktopStats[1]?.value, netzwerkContent.juryIntro.desktopStats[1].value)}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.15em', color: 'rgba(16,24,40,0.4)', marginTop: 8 }}>{fallbackText(desktopStats[1]?.label, netzwerkContent.juryIntro.desktopStats[1].label)}</div>
|
||
</div>
|
||
<div style={{ width: '100%', height: 1, background: 'rgba(3,9,58,0.1)', margin: '32px 0' }} />
|
||
<div>
|
||
<div style={{ fontFamily: FF, fontSize: 'clamp(5rem, 8.5vw, 7.5rem)', fontWeight: 900, color: '#101828', letterSpacing: '-0.04em', lineHeight: 1 }}>{fallbackText(desktopStats[2]?.value, netzwerkContent.juryIntro.desktopStats[2].value)}</div>
|
||
<div style={{ fontFamily: FF, fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.15em', color: 'rgba(16,24,40,0.4)', marginTop: 4 }}>{fallbackText(desktopStats[2]?.label, netzwerkContent.juryIntro.desktopStats[2].label)}</div>
|
||
</div>
|
||
</div>
|
||
{/* Right – Navy, editorial copy */}
|
||
<div style={{ background: NAVY, padding: '72px 64px', display: 'flex', flexDirection: 'column', justifyContent: 'center', position: 'relative', zIndex: 1 }}>
|
||
<span style={{ fontFamily: FF, fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, color: GOLD, marginBottom: 20 }}>{fallbackText(juryIntro.eyebrow, netzwerkContent.juryIntro.eyebrow)}</span>
|
||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(1.6rem, 2.5vw, 2.2rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.08, margin: '0 0 24px' }}>
|
||
<Lines text={fallbackText(juryIntro.heading, netzwerkContent.juryIntro.heading)} />
|
||
</h2>
|
||
<div style={{ width: 40, height: 2, background: GOLD }} />
|
||
<p style={{ marginTop: 24, fontFamily: FB, fontSize: 19, color: 'rgba(255,255,255,0.45)', lineHeight: 1.85 }}>
|
||
{fallbackText(juryIntro.description, netzwerkContent.juryIntro.description)}
|
||
</p>
|
||
</div>
|
||
</>
|
||
)}
|
||
</section>
|
||
|
||
|
||
{/* ── 4. JURY-MITGLIEDER ───────────────────────────────────────────────── */}
|
||
<section id="jury" style={{ background: NAVY, overflow: 'hidden', position: 'relative' }}>
|
||
<div style={{ padding: isMobile ? '48px 24px 32px' : '80px 80px 56px', display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: isMobile ? 16 : 48, alignItems: 'flex-end', borderBottom: '1px solid rgba(255,255,255,0.07)' }}>
|
||
<div>
|
||
<span style={{ fontFamily: FF, fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, color: GOLD, display: 'block', marginBottom: 16 }}>{fallbackText(jury.eyebrow, netzwerkContent.jury.eyebrow)}</span>
|
||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(2rem, 3.5vw, 3rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.025em', lineHeight: 0.95, margin: 0 }}><Lines text={fallbackText(jury.heading, netzwerkContent.jury.heading)} /></h2>
|
||
</div>
|
||
<p style={{ fontFamily: FB, fontSize: 19, color: 'rgba(255,255,255,0.4)', lineHeight: 1.8, margin: 0 }}>
|
||
{fallbackText(jury.description, netzwerkContent.jury.description)}
|
||
</p>
|
||
</div>
|
||
{/* ── Prominenter Vorsitz-Bereich ─────────────────────────────────── */}
|
||
{/* TODO: echten Jury-Vorsitzenden bestätigen (Name & Foto sind Platzhalter) */}
|
||
<div style={{ padding: isMobile ? '28px 24px 8px' : '64px 80px 16px' }}>
|
||
<JuryChairCard member={juryMembers[0]} badge={fallbackText(jury.chairBadge, netzwerkContent.jury.chairBadge)} />
|
||
</div>
|
||
|
||
{/* ── Übrige Jury-Mitglieder ──────────────────────────────────────── */}
|
||
<div style={{ display: isMobile ? 'flex' : 'grid', gridTemplateColumns: isMobile ? undefined : 'repeat(3, 1fr)', overflowX: isMobile ? 'auto' : undefined, scrollSnapType: isMobile ? 'x mandatory' : undefined, gap: isMobile ? 14 : 0, padding: isMobile ? '24px 24px 12px' : '0', WebkitOverflowScrolling: 'touch', scrollbarWidth: 'none', borderTop: isMobile ? 'none' : '1px solid rgba(255,255,255,0.07)' }}>
|
||
{juryMembers.slice(1).map((member, idx) => (
|
||
<JuryCard key={idx} member={member} idx={idx} />
|
||
))}
|
||
</div>
|
||
|
||
{/* ── Hinweis: Doppelrolle Jury / Partner (#179) ──────────────────── */}
|
||
<div style={{ padding: isMobile ? '4px 24px 36px' : '8px 80px 56px' }}>
|
||
<p style={{ fontFamily: FB, fontSize: isMobile ? 13 : 14, color: 'rgba(255,255,255,0.35)', lineHeight: 1.6, margin: 0, maxWidth: 720 }}>
|
||
{fallbackText(jury.note, netzwerkContent.jury.note)}
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
{/* ── 5. HINTERGRUND & EXPERTISE ───────────────────────────────────────── */}
|
||
<section style={{ background: '#101828', overflow: 'hidden', display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '55% 45%', borderTop: '1px solid rgba(255,255,255,0.07)' }}>
|
||
<div style={{ padding: isMobile ? '48px 24px' : 80, display: 'flex', flexDirection: 'column', justifyContent: 'center', background: '#101828' }}>
|
||
<span style={{ fontFamily: FF, fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, color: GOLD, display: 'block', marginBottom: 16 }}>{fallbackText(expertise.eyebrow, netzwerkContent.expertise.eyebrow)}</span>
|
||
<h2 style={{ fontFamily: FF, fontSize: 'clamp(1.8rem, 3vw, 2.6rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.025em', lineHeight: 1.0, margin: '0 0 24px' }}><Lines text={fallbackText(expertise.heading, netzwerkContent.expertise.heading)} /></h2>
|
||
<div style={{ width: 40, height: 2, background: GOLD, marginBottom: 40 }} />
|
||
<div>
|
||
{expertiseRows.map((row) => (
|
||
<div key={row.num} style={{ display: 'grid', gridTemplateColumns: '40px 1fr', gap: '0 20px', padding: '20px 0', borderBottom: '1px solid rgba(255,255,255,0.06)', alignItems: 'center' }}>
|
||
<div style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: 'rgba(239,191,4,0.5)' }}>{row.num}</div>
|
||
<div>
|
||
<div style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, color: '#fff', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 3 }}>{row.label}</div>
|
||
<div style={{ fontFamily: FB, fontSize: 18, color: 'rgba(255,255,255,0.4)', lineHeight: 1.6 }}>{row.body}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
<div style={{ padding: isMobile ? '48px 24px' : '80px 64px', display: 'flex', flexDirection: 'column', justifyContent: 'center', borderLeft: isMobile ? 'none' : '1px solid rgba(255,255,255,0.07)', borderTop: isMobile ? '1px solid rgba(255,255,255,0.07)' : 'none', overflow: 'hidden', background: '#101828' }}>
|
||
<span style={{ fontFamily: 'Georgia, serif', fontSize: 'clamp(60px, 8vw, 120px)', color: 'rgba(239,191,4,0.1)', lineHeight: 0.8, display: 'block', marginBottom: -20 }}>“</span>
|
||
<p style={{ fontFamily: FF, fontSize: 'clamp(1.1rem, 1.8vw, 1.4rem)', fontWeight: 300, color: 'rgba(255,255,255,0.75)', lineHeight: 1.65, margin: '0 0 40px' }}>
|
||
{fallbackText(expertise.quote, netzwerkContent.expertise.quote)}
|
||
</p>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
|
||
<div style={{ width: 40, height: 2, background: GOLD, flexShrink: 0 }} />
|
||
<span style={{ fontFamily: FF, fontSize: 17, color: 'rgba(255,255,255,0.4)' }}>{fallbackText(expertise.quoteAttribution, netzwerkContent.expertise.quoteAttribution)}</span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
{/* ── 6. PARTNER & SPONSOREN ───────────────────────────────────────────── */}
|
||
<section id="partner" style={{ background: CREAM, overflow: 'hidden', position: 'relative', isolation: 'isolate' }}>
|
||
<MunichSkylineBg />
|
||
<div style={{ padding: isMobile ? '48px 24px 32px' : '80px 80px 56px', display: 'flex', flexDirection: isMobile ? 'column' : 'row', gap: isMobile ? 12 : 0, justifyContent: 'space-between', alignItems: isMobile ? 'flex-start' : 'flex-end', borderBottom: '1px solid rgba(3,9,58,0.1)' }}>
|
||
<div>
|
||
<span style={{ fontFamily: FF, fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, color: '#4A8FC9', display: 'block', marginBottom: 16 }}>{fallbackText(partnerSection.eyebrow, netzwerkContent.partners.eyebrow)}</span>
|
||
<h2 style={{ fontFamily: FF, fontWeight: 900, color: '#101828', textTransform: 'uppercase', letterSpacing: '-0.025em', fontSize: 'clamp(2rem, 3.5vw, 3rem)', lineHeight: 0.95, margin: 0 }}><Lines text={fallbackText(partnerSection.heading, netzwerkContent.partners.heading)} /></h2>
|
||
</div>
|
||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.4)', maxWidth: isMobile ? '100%' : 220, textAlign: isMobile ? 'left' : 'right', lineHeight: 1.6, margin: 0 }}>
|
||
{fallbackText(partnerSection.description, netzwerkContent.partners.description)}
|
||
</p>
|
||
</div>
|
||
|
||
{/* Tiered grid – continuous, ranks marked by label bars */}
|
||
<div style={{ position: 'relative', zIndex: 1 }}>
|
||
{partnerTiers.map(t => {
|
||
const tier = Number(t.tier) as 1 | 2 | 3;
|
||
const variant = tier === 1 ? 'lg' as const : tier === 2 ? 'md' as const : 'sm' as const;
|
||
const cols = isMobile ? (tier === 1 ? 1 : 2) : (tier === 3 ? 4 : 2);
|
||
const items = partners.filter(p => p.tier === tier);
|
||
return (
|
||
<div key={t.tier} style={{ display: 'grid', gridTemplateColumns: `repeat(${cols}, 1fr)` }}>
|
||
{items.map((p, i) => (
|
||
<SponsorCell key={p.id} partner={p} variant={variant} idx={i} cols={cols} detailLabel={fallbackText(partnerSection.detailLabel, netzwerkContent.partners.detailLabel)} premiumLabel={fallbackText(partnerSection.premiumLabel, netzwerkContent.partners.premiumLabel)} onClick={() => setSelectedPartner(p)} />
|
||
))}
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</section>
|
||
|
||
|
||
{/* ── 7. SPONSORING-FORM ───────────────────────────────────────────────── */}
|
||
<section id="sponsoring" style={{
|
||
background: NAVY, overflow: 'hidden', position: 'relative',
|
||
minHeight: isMobile ? 'auto' : 'calc(100vh - 60px)',
|
||
display: 'flex', flexDirection: 'column',
|
||
}}>
|
||
<div style={{ flex: 1, position: 'relative', zIndex: 1, display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '4fr 1px 8fr', minHeight: 0, overflow: isMobile ? 'visible' : 'visible' }}>
|
||
{/* Left – pitch */}
|
||
<div style={{ padding: isMobile ? '32px 24px 24px' : '36px 36px 32px 52px', display: 'flex', flexDirection: 'column', justifyContent: 'center', overflow: 'visible' }}>
|
||
<span style={{ fontFamily: FF, fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, color: GOLD, display: 'block', marginBottom: 8 }}>{fallbackText(sponsoring.eyebrow, netzwerkContent.sponsoring.eyebrow)}</span>
|
||
<h2 style={{ fontFamily: FF, fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.025em', fontSize: 'clamp(1.6rem, 2.4vw, 2.4rem)', lineHeight: 1.0, margin: '0 0 12px' }}><Lines text={fallbackText(sponsoring.heading, netzwerkContent.sponsoring.heading)} /></h2>
|
||
<div style={{ width: 36, height: 2, background: GOLD, margin: '0 0 14px', flexShrink: 0 }} />
|
||
<p style={{ fontFamily: FB, fontSize: 'clamp(15px, 1.2vw, 17px)', color: 'rgba(255,255,255,0.75)', lineHeight: 1.65, marginBottom: 16 }}>
|
||
{fallbackText(sponsoring.description, netzwerkContent.sponsoring.description)}
|
||
</p>
|
||
{sponsorBenefits.map((item) => (
|
||
<div key={item.num} style={{ display: 'grid', gridTemplateColumns: '32px 1fr', gap: '0 12px', padding: '10px 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.5)' }}>{item.num}</span>
|
||
<div>
|
||
<div style={{ fontFamily: FF, fontSize: 'clamp(14px, 1.05vw, 16px)', fontWeight: 700, color: '#fff', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 2 }}>{item.label}</div>
|
||
<div style={{ fontFamily: FB, fontSize: 'clamp(13px, 1vw, 15px)', color: 'rgba(255,255,255,0.65)', lineHeight: 1.4 }}>{item.sub}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
{/* Center divider */}
|
||
{!isMobile && <div style={{ background: 'rgba(255,255,255,0.07)' }} />}
|
||
{/* Right – gold form panel */}
|
||
<div style={{ display: 'flex', flexDirection: 'column', overflow: 'visible', minHeight: 0, position: 'relative', background: 'linear-gradient(160deg,#DDB84A 0%,#C9A227 52%,#A87800 100%)' }}>
|
||
<svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', pointerEvents: 'none', zIndex: 0 }} aria-hidden="true">
|
||
<defs>
|
||
<pattern id="sponsoringCross" width="18" height="18" patternUnits="userSpaceOnUse">
|
||
<path d="M0,0 L18,18 M18,0 L0,18" stroke="rgba(17,29,85,0.055)" strokeWidth="0.65" />
|
||
</pattern>
|
||
<pattern id="sponsoringDiamond" width="36" height="36" patternUnits="userSpaceOnUse">
|
||
<polygon points="18,2 34,18 18,34 2,18" fill="none" stroke="rgba(17,29,85,0.045)" strokeWidth="0.6" />
|
||
</pattern>
|
||
</defs>
|
||
<rect width="100%" height="100%" fill="url(#sponsoringCross)" />
|
||
<rect width="100%" height="100%" fill="url(#sponsoringDiamond)" />
|
||
</svg>
|
||
<div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(ellipse at center, transparent 55%, rgba(100,70,0,0.25) 100%)', pointerEvents: 'none', zIndex: 0 }} />
|
||
|
||
{!isMobile && (
|
||
<div style={{ height: 220, position: 'relative', overflow: 'hidden', flexShrink: 0, zIndex: 1 }}>
|
||
<Image unoptimized src={mediaUrl(sponsoring.image, `/images/${netzwerkContent.sponsoring.imageFilename}`)} alt={mediaAlt(sponsoring.image, fallbackText(sponsoring.imageAlt, netzwerkContent.sponsoring.imageAlt))} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 40%', filter: 'sepia(0.18) brightness(0.92)' }} />
|
||
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to bottom, rgba(168,120,0,0.1) 0%, rgba(168,120,0,0.25) 50%, rgba(168,120,0,0.92) 88%, #A87800 100%)' }} />
|
||
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: 2, background: 'rgba(17,29,85,0.35)' }} />
|
||
<div style={{ position: 'absolute', bottom: 14, left: 40, display: 'flex', alignItems: 'center', gap: 8 }}>
|
||
<div style={{ width: 5, height: 5, borderRadius: '50%', background: 'rgba(17,29,85,0.7)' }} />
|
||
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, color: 'rgba(17,29,85,0.75)', textTransform: 'uppercase', letterSpacing: '0.2em' }}>{fallbackText(sponsoring.imageLabel, netzwerkContent.sponsoring.imageLabel)}</span>
|
||
</div>
|
||
</div>
|
||
)}
|
||
<div style={{ padding: isMobile ? '28px 20px' : '24px 48px 32px 40px', flex: 1, display: 'flex', flexDirection: 'column', minHeight: isMobile ? 560 : 0, position: 'relative', zIndex: 1 }}>
|
||
<span style={{ fontFamily: FF, fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, color: 'rgba(17,29,85,0.5)', display: 'block', marginBottom: 8 }}>{fallbackText(sponsoring.formEyebrow, netzwerkContent.sponsoring.formEyebrow)}</span>
|
||
<SponsoringForm theme="gold" content={cms.sponsoringForm} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{/* Footnote */}
|
||
<div style={{ position: 'relative', zIndex: 1, padding: isMobile ? '16px 20px 20px' : '14px 56px 18px', display: 'flex', flexWrap: 'wrap', justifyContent: 'center', alignItems: 'center', borderTop: '1px solid rgba(255,255,255,0.06)', flexShrink: 0 }}>
|
||
<span style={{ fontFamily: '"IBM Plex Sans", sans-serif', fontSize: 15, color: 'rgba(255,255,255,0.45)', marginRight: 10 }}>{fallbackText(sponsoring.footnotePrefix, netzwerkContent.sponsoring.footnotePrefix)}</span>
|
||
<Link
|
||
to={fallbackText(sponsoring.footnoteUrl, netzwerkContent.sponsoring.footnoteUrl)}
|
||
style={{
|
||
fontFamily: '"IBM Plex Sans", sans-serif',
|
||
fontSize: 15,
|
||
fontWeight: 700,
|
||
letterSpacing: '0.12em',
|
||
textTransform: 'uppercase',
|
||
color: 'rgba(239,191,4,0.7)',
|
||
textDecoration: 'none',
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 6,
|
||
borderBottom: '1px solid rgba(239,191,4,0.3)',
|
||
paddingBottom: 1,
|
||
transition: 'color 0.15s, borderColor 0.15s',
|
||
}}
|
||
onMouseEnter={e => {
|
||
(e.currentTarget as HTMLElement).style.color = '#EFBF04';
|
||
(e.currentTarget as HTMLElement).style.borderBottomColor = '#EFBF04';
|
||
}}
|
||
onMouseLeave={e => {
|
||
(e.currentTarget as HTMLElement).style.color = 'rgba(239,191,4,0.7)';
|
||
(e.currentTarget as HTMLElement).style.borderBottomColor = 'rgba(239,191,4,0.3)';
|
||
}}
|
||
>
|
||
{fallbackText(sponsoring.footnoteLabel, netzwerkContent.sponsoring.footnoteLabel)} <ChevronRight size={11} />
|
||
</Link>
|
||
</div>
|
||
<div style={{ position: 'relative', zIndex: 1, height: 2, background: `linear-gradient(to right, ${GOLD}, rgba(239,191,4,0.3), transparent)`, flexShrink: 0 }} />
|
||
</section>
|
||
|
||
|
||
{/* ── 8. PARTNER MODAL ─────────────────────────────────────────────────── */}
|
||
{selectedPartner && (
|
||
<PartnerModal partner={selectedPartner} copy={partnerModal} onClose={() => setSelectedPartner(null)} />
|
||
)}
|
||
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default Netzwerk;
|