feat: add application form configuration and migration for shared application form data

- Introduced a new configuration file for the application form with various fields and validation messages.
- Created a migration script to set up the database schema for the application form, including necessary tables and default values.
- Added a migration for supporting awards copy to enhance the pages with additional columns.
- Implemented a new SPA component for the application form section, integrating form logic and UI elements.
This commit is contained in:
syntaxbullet
2026-07-13 16:07:58 +02:00
parent ee7e891a7d
commit 9ebe544987
19 changed files with 1026 additions and 259 deletions

View File

@@ -4,21 +4,19 @@ import { Play, ChevronLeft, ChevronRight, X, Star } from 'lucide-react';
import { Link } from '@/spa/router';
import { PartnerTicker } from '@/spa/components/ui/partner-ticker';
import { WINNERS } from '@/spa/data/winners';
import BewerbungsForm from '@/spa/components/forms/BewerbungsForm';
import { NewsletterInterestForm } from '@/spa/components/forms/NewsletterInterestForm';
import { ApplicationFormSection } from '@/spa/components/forms/ApplicationFormSection';
import TestimonialsSection from '@/spa/components/TestimonialsSection';
import MunichSkylineBg from '@/spa/components/ui/munich-skyline-bg';
import { useIsMobile } from '@/spa/hooks/useIsMobile';
import Image from '@/spa/components/ui/UnoptimizedImage'
import type { SpaApplicationPhaseData, SpaApplicationPhaseItem } from '@/spa/applicationPhase'
import { useApplicationPhase, useCmsCollection, useCmsRoute, useNewsletterForm } from '@/spa/cmsRoute'
import { useApplicationPhase, useCmsCollection, useCmsRoute } from '@/spa/cmsRoute'
import { mediaAlt, mediaUrl } from '@/spa/cmsMediaField'
import { homeContent } from '@/spa/homeContent'
import { resolveHomeWinners } from '@/spa/homeWinnerSelection'
import { netzwerkContent } from '@/spa/netzwerkContent'
import { usePreistraegerPlaceholderImage } from '@/spa/preistraegerPlaceholder'
import { PartnerLogoMark, type PartnerLogoData } from '@/spa/components/ui/partner-logo'
import { newsletterFormCopyForPhase, newsletterPhaseForApplicationPhase } from '@/spa/newsletterForm'
const FF = '"IBM Plex Sans", sans-serif';
@@ -82,7 +80,6 @@ type HomeTestimonialsCms = {
type HomeCms = {
application?: HomeSectionCms;
benefits?: HomeSectionCms;
form?: Partial<typeof homeContent.form>;
hero?: HomeHeroCms;
intro?: HomeSectionCms;
quickCheck?: HomeSectionCms;
@@ -117,7 +114,6 @@ const fallbackArray = <T,>(value: unknown, fallback: readonly T[]) =>
Array.isArray(value) && value.length > 0 ? (value as T[]) : fallback;
const HIDE_MEMBERSHIP_HOME_HERO_CTA = true;
const APPLICATION_PHASE_OPEN = '0';
const STATUS_SECTION_BACKGROUND = '#111D55';
const LEGACY_STATUS_SECTION_BACKGROUNDS = new Set(['#020A1E', '#060D1C', '#06090F']);
@@ -242,15 +238,8 @@ const Home: React.FC = () => {
const intro = home.intro || {};
const winnersSection = home.winners || {};
const benefits = home.benefits || {};
const application = home.application || {};
const form = home.form || {};
const videoModal = home.videoModal || {};
const applicationPhase = useApplicationPhase();
const newsletterForm = useNewsletterForm();
const activeApplicationPhase = applicationPhase?.activePhase || APPLICATION_PHASE_OPEN;
const showApplicationForm = activeApplicationPhase === APPLICATION_PHASE_OPEN;
const newsletterCopy = newsletterFormCopyForPhase(newsletterForm, activeApplicationPhase);
const newsletterPhase = newsletterPhaseForApplicationPhase(activeApplicationPhase);
const cmsPreistraeger = useCmsCollection('preistraeger');
const heroSecondaryCta = {
label: fallbackText(hero.secondaryCta?.label, homeContent.hero.secondaryCta.label),
@@ -263,8 +252,6 @@ const Home: React.FC = () => {
const quickCriteria = fallbackArray(quickCheck.criteria, homeContent.quickCheck.criteria);
const introStats = fallbackArray(intro.stats, homeContent.intro.stats);
const benefitItems = fallbackArray(benefits.items, homeContent.benefits.items);
const applicationBenefits = fallbackArray(application.benefits, homeContent.application.benefits);
const finalSectionBenefits = showApplicationForm ? applicationBenefits : newsletterCopy.benefits;
const winnerFallbackImage = placeholderImage;
const videoTitle = fallbackText(videoModal.title, homeContent.videoModal.title);
const videoSrc = mediaUrl(videoModal.video, '');
@@ -662,92 +649,7 @@ const Home: React.FC = () => {
</div>
</section>
{/* CTA Section – with phase-aware embedded form */}
<section style={{ background: '#24366A', position: 'relative', overflow: 'hidden', height: isMobile ? 'auto' : showApplicationForm ? 'calc(100vh - 60px)' : 'auto', minHeight: isMobile ? undefined : showApplicationForm ? undefined : 560, display: 'flex', flexDirection: 'column' }} id="bewerben">
{/* Subtle radial glow behind left copy */}
<div style={{ position: 'absolute', left: -120, top: '50%', transform: 'translateY(-50%)', width: 600, height: 600, borderRadius: '50%', background: 'radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 70%)', pointerEvents: 'none' }} />
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '4fr 1px 8fr', position: 'relative', flex: isMobile ? 'none' : 1, minHeight: 0, overflow: 'hidden' }}>
{/* Left – pitch copy */}
<div style={{ padding: isMobile ? '32px 24px 24px' : '36px 36px 32px 52px', display: 'flex', flexDirection: 'column', justifyContent: 'center', overflow: 'hidden' }}>
<span style={{ fontFamily: FF, fontSize: 10, color: '#EFBF04', textTransform: 'uppercase', letterSpacing: '0.36em', fontWeight: 700, display: 'block', marginBottom: 16, opacity: 0.8 }}>{showApplicationForm ? fallbackText(application.eyebrow, homeContent.application.eyebrow) : newsletterCopy.eyebrow}</span>
<h2 style={{ fontFamily: FF, fontSize: isMobile ? 'clamp(1.8rem, 8vw, 2.6rem)' : 'clamp(1.8rem, 2.8vw, 2.6rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.03em', lineHeight: 1.0, marginBottom: 16 }}>
<Lines text={showApplicationForm ? fallbackText(application.heading, homeContent.application.heading) : newsletterCopy.heading} />
</h2>
<div style={{ width: 32, height: 2, background: '#EFBF04', marginBottom: 20 }} />
<p style={{ fontFamily: FF, fontSize: 15, color: 'rgba(255,255,255,0.6)', lineHeight: 1.7, marginBottom: 32 }}>
{showApplicationForm ? fallbackText(application.body, homeContent.application.body) : newsletterCopy.body}
</p>
{/* Benefit rows */}
<div style={{ borderTop: '1px solid rgba(255,255,255,0.08)' }}>
{finalSectionBenefits.map(item => (
<div key={item.num} style={{ display: 'grid', gridTemplateColumns: '24px 1fr', gap: '0 14px', padding: '13px 0', borderBottom: '1px solid rgba(255,255,255,0.06)', alignItems: 'center' }}>
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, color: 'rgba(239,191,4,0.55)', letterSpacing: '0.1em' }}>{item.num}</span>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8 }}>
<span style={{ fontFamily: FF, fontSize: 14, fontWeight: 700, color: '#fff', textTransform: 'uppercase', letterSpacing: '0.08em' }}>{item.label}</span>
<span style={{ fontFamily: FF, fontSize: 14, color: 'rgba(255,255,255,0.45)', textAlign: 'right' }}>{item.desc}</span>
</div>
</div>
))}
</div>
</div>
{/* Divider */}
{!isMobile && <div style={{ background: 'rgba(255,255,255,0.07)' }} />}
{/* Right – gold premium panel */}
<div style={{ display: 'flex', flexDirection: 'column', overflow: 'hidden', minHeight: 0, position: 'relative', background: 'linear-gradient(160deg,#DDB84A 0%,#C9A227 52%,#A87800 100%)' }}>
{/* Premium award crosshatch pattern */}
<svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', pointerEvents: 'none', zIndex: 0 }} aria-hidden="true">
<defs>
<pattern id="bmpCross" 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>
</defs>
<rect width="100%" height="100%" fill="url(#bmpCross)" />
</svg>
{/* Inner vignette – edges slightly deeper gold */}
<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 }} />
{/* Award ceremony photo */}
{!isMobile && (
<div style={{ height: 220, position: 'relative', overflow: 'hidden', flexShrink: 0, zIndex: 1 }}>
<Image unoptimized
src={mediaUrl(application.awardImage, `/images/${homeContent.application.awardImageFilename}`)}
alt={mediaAlt(application.awardImage, fallbackText(application.awardImageAlt, homeContent.application.awardImageAlt))}
style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 25%', filter: 'sepia(0.18) brightness(0.92)' }}
/>
{/* Blend photo into gold panel */}
<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(application.awardCaption, homeContent.application.awardCaption)}</span>
</div>
</div>
)}
{/* Form area */}
<div style={{ padding: isMobile ? '32px 24px' : '24px 48px 32px 40px', flex: isMobile ? 'none' : 1, height: isMobile && showApplicationForm ? '82svh' : undefined, display: 'flex', flexDirection: 'column', minHeight: 0, position: 'relative', zIndex: 1 }}>
<span style={{ fontFamily: FF, fontSize: 10, color: 'rgba(17,29,85,0.5)', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 8 }}>{showApplicationForm ? fallbackText(application.formEyebrow, homeContent.application.formEyebrow) : newsletterCopy.formEyebrow}</span>
<h3 style={{ fontFamily: FF, fontSize: 'clamp(1.2rem, 1.8vw, 1.6rem)', fontWeight: 900, color: '#111D55', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.1, marginBottom: 24 }}>{showApplicationForm ? fallbackText(application.formTitle, homeContent.application.formTitle) : newsletterCopy.formTitle}</h3>
{showApplicationForm ? <BewerbungsForm theme="gold" content={form} /> : <NewsletterInterestForm copy={newsletterCopy} phase={newsletterPhase} source="homepage" />}
</div>
</div>
</div>
{/* Footnote row */}
<div style={{ position: 'relative', zIndex: 1, padding: isMobile ? '16px 24px 20px' : '14px 56px 18px', display: 'flex', flexWrap: 'wrap', justifyContent: 'center', textAlign: 'center', borderTop: '1px solid rgba(255,255,255,0.06)', flexShrink: 0 }}>
<span style={{ fontFamily: FF, fontSize: 15, color: 'rgba(255,255,255,0.45)', marginRight: isMobile ? 6 : 10 }}>{showApplicationForm ? fallbackText(application.footnote, homeContent.application.footnote) : newsletterCopy.footnote}</span>
<span style={{ fontFamily: FF, fontSize: 15, fontWeight: 700, color: 'rgba(239,191,4,0.8)' }}>{showApplicationForm ? fallbackText(application.footnoteStrong, homeContent.application.footnoteStrong) : newsletterCopy.footnoteStrong}</span>
</div>
<div style={{ height: 2, background: 'linear-gradient(to right, #EFBF04, rgba(239,191,4,0.25), transparent)', flexShrink: 0 }} />
</section>
<ApplicationFormSection source="homepage" />
{/* Video Modal */}
{showVideo && (

View File

@@ -1,16 +1,14 @@
import React, { useRef } from 'react';
import { MapPin, Users, TrendingUp, Building2, ArrowRight, CheckCircle2, UserCheck, Send, Star } from 'lucide-react';
import { Link } from '@/spa/router';
import BewerbungsForm from '@/spa/components/forms/BewerbungsForm';
import { NewsletterInterestForm } from '@/spa/components/forms/NewsletterInterestForm';
import { ApplicationFormSection } from '@/spa/components/forms/ApplicationFormSection';
import AwardsGridSection, { type AwardsGridContent } from '@/spa/components/AwardsGridSection';
import WegZurAuszeichnungSection from '@/spa/components/WegZurAuszeichnungSection';
import { useIsMobile } from '@/spa/hooks/useIsMobile';
import { mediaAlt, mediaUrl } from '@/spa/cmsMediaField';
import { useApplicationPhase, useCmsRoute, useNewsletterForm } from '@/spa/cmsRoute';
import { useCmsRoute } from '@/spa/cmsRoute';
import { participationContent } from '@/spa/participationContent';
import Image from '@/spa/components/ui/UnoptimizedImage'
import { newsletterFormCopyForPhase, newsletterPhaseForApplicationPhase } from '@/spa/newsletterForm';
const FF = '"IBM Plex Sans", sans-serif';
const FB = '"Inter", sans-serif';
@@ -20,11 +18,6 @@ const GOLD = '#EFBF04';
type ParticipationCms = Partial<typeof participationContent> & {
hero?: Partial<typeof participationContent.hero> & { backgroundImage?: unknown };
awardsGrid?: AwardsGridContent;
applicationForm?: ParticipationApplicationFormCms;
};
type ParticipationApplicationFormCms = Partial<typeof participationContent.applicationForm> & {
image?: unknown;
};
const ICONS = {
@@ -43,8 +36,6 @@ const fallbackText = (value: unknown, fallback: string) =>
const fallbackArray = <T,>(value: unknown, fallback: readonly T[]) =>
Array.isArray(value) && value.length > 0 ? (value as T[]) : fallback;
const APPLICATION_PHASE_OPEN = '0';
const isFutureAwardApplicationWay = (value: { label?: unknown; title?: unknown }) => {
const label = typeof value.label === 'string' ? value.label.toLowerCase() : '';
const title = typeof value.title === 'string' ? value.title.toLowerCase() : '';
@@ -60,26 +51,16 @@ const Participation: React.FC = () => {
const isMobile = useIsMobile();
const qualSectionRef = useRef<HTMLElement>(null);
const cms = (useCmsRoute()?.doc?.participation || {}) as ParticipationCms;
const applicationPhase = useApplicationPhase();
const newsletterForm = useNewsletterForm();
const hero = { ...participationContent.hero, ...(cms.hero || {}) };
const process = { ...participationContent.process, ...(cms.process || {}) };
const eligibility = { ...participationContent.eligibility, ...(cms.eligibility || {}) };
const applicationWays = { ...participationContent.applicationWays, ...(cms.applicationWays || {}) };
const datesBanner = { ...participationContent.datesBanner, ...(cms.datesBanner || {}) };
const awardsGrid = { ...participationContent.awardsGrid, ...(cms.awardsGrid || {}) };
const applicationForm = { ...participationContent.applicationForm, ...(cms.applicationForm || {}) };
const form = { ...participationContent.form, ...(cms.form || {}) };
const activeApplicationPhase = applicationPhase?.activePhase || APPLICATION_PHASE_OPEN;
const showApplicationForm = activeApplicationPhase === APPLICATION_PHASE_OPEN;
const newsletterCopy = newsletterFormCopyForPhase(newsletterForm, activeApplicationPhase);
const newsletterPhase = newsletterPhaseForApplicationPhase(activeApplicationPhase);
const eligibilityCriteria = fallbackArray(eligibility.criteria, participationContent.eligibility.criteria);
const eligibilityNotes = fallbackArray(eligibility.notes, participationContent.eligibility.notes);
const mobileDates = fallbackArray(datesBanner.mobileItems, participationContent.datesBanner.mobileItems);
const desktopDates = fallbackArray(datesBanner.desktopItems, participationContent.datesBanner.desktopItems);
const applicationFacts = fallbackArray(applicationForm.facts, participationContent.applicationForm.facts);
const finalSectionFacts = showApplicationForm ? applicationFacts : newsletterCopy.benefits;
return (
<div className="animate-fade-in">
@@ -219,98 +200,7 @@ const Participation: React.FC = () => {
{/* ── AWARDS GRID ─────────────────────────────────────────────────────── */}
<AwardsGridSection content={awardsGrid} featuredGoldeneBavaria />
{/* ── FORM SECTION ─────────────────────────────────────────────────────── */}
<section style={{ background: NAVY, position: 'relative', overflow: 'hidden', height: isMobile ? 'auto' : showApplicationForm ? 'calc(100vh - 60px)' : 'auto', minHeight: isMobile ? undefined : showApplicationForm ? undefined : 560, display: 'flex', flexDirection: 'column' }} id="bewerben">
<div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '4fr 1px 8fr', flex: 1, minHeight: 0, overflow: isMobile ? 'visible' : 'hidden' }}>
{/* Left – copy */}
<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, color: '#EFBF04', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 20 }}>{showApplicationForm ? fallbackText(applicationForm.eyebrow, participationContent.applicationForm.eyebrow) : newsletterCopy.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' }}>
<Lines text={showApplicationForm ? fallbackText(applicationForm.heading, participationContent.applicationForm.heading) : newsletterCopy.heading} />
</h2>
<div style={{ width: 36, height: 2, background: GOLD, marginBottom: 28 }} />
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(255,255,255,0.68)', lineHeight: 1.8, maxWidth: 400, marginBottom: 48 }}>
{showApplicationForm ? fallbackText(applicationForm.description, participationContent.applicationForm.description) : newsletterCopy.body}
</p>
<div style={{ borderTop: '1px solid rgba(255,255,255,0.08)' }}>
{finalSectionFacts.map((item, i) => (
<div key={`${fallbackText(item.num, String(i))}-${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.5)', letterSpacing: '0.1em' }}>{fallbackText(item.num, '')}</span>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
<span style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, color: '#fff', textTransform: 'uppercase', letterSpacing: '0.08em' }}>{fallbackText(item.label, '')}</span>
<span style={{ fontFamily: FB, fontSize: 16, color: 'rgba(255,255,255,0.62)' }}>{fallbackText(item.desc, '')}</span>
</div>
</div>
))}
</div>
</div>
{/* Divider */}
{!isMobile && <div style={{ background: 'rgba(255,255,255,0.07)' }} />}
{/* Right – gold form panel */}
<div style={{ display: 'flex', flexDirection: 'column', overflow: isMobile ? 'visible' : 'hidden', 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="participationCross" 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="participationDiamond" 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(#participationCross)" />
<rect width="100%" height="100%" fill="url(#participationDiamond)" />
</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(applicationForm.image, `/images/${participationContent.applicationForm.imageFilename}`)} alt={mediaAlt(applicationForm.image, fallbackText(applicationForm.imageAlt, participationContent.applicationForm.imageAlt))} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 30%', 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: 16, 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(applicationForm.imageCaption, participationContent.applicationForm.imageCaption)}</span>
</div>
</div>
)}
<div style={{ padding: isMobile ? '28px 20px' : '24px 48px 32px 40px', flex: 1, display: 'flex', flexDirection: 'column', minHeight: isMobile && showApplicationForm ? 560 : 0, position: 'relative', zIndex: 1 }}>
{showApplicationForm ? (
<>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16, marginBottom: 10 }}>
<span style={{ fontFamily: FF, fontSize: 10, color: 'rgba(17,29,85,0.5)', textTransform: 'uppercase', letterSpacing: '0.28em', fontWeight: 700 }}>{fallbackText(applicationForm.formEyebrow, participationContent.applicationForm.formEyebrow)}</span>
</div>
<h3 style={{ fontFamily: FF, fontSize: 'clamp(1.2rem, 1.8vw, 1.6rem)', fontWeight: 900, color: '#111D55', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.1, marginBottom: 24 }}>{fallbackText(applicationForm.formTitle, participationContent.applicationForm.formTitle)}</h3>
<BewerbungsForm theme="gold" content={form} />
</>
) : (
<>
<span style={{ fontFamily: FF, fontSize: 10, color: 'rgba(17,29,85,0.5)', textTransform: 'uppercase', letterSpacing: '0.32em', fontWeight: 700, display: 'block', marginBottom: 8 }}>{newsletterCopy.formEyebrow}</span>
<h3 style={{ fontFamily: FF, fontSize: 'clamp(1.2rem, 1.8vw, 1.6rem)', fontWeight: 900, color: '#111D55', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.1, marginBottom: 24 }}>{newsletterCopy.formTitle}</h3>
<NewsletterInterestForm copy={newsletterCopy} phase={newsletterPhase} source="participation" />
</>
)}
</div>
</div>
</div>
<div style={{ position: 'relative', zIndex: 1, padding: isMobile ? '16px 20px 20px' : '14px 56px 18px', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, justifyContent: 'center', textAlign: 'center', borderTop: '1px solid rgba(255,255,255,0.06)', flexShrink: 0 }}>
{showApplicationForm ? (
<>
<span style={{ fontFamily: FB, fontSize: 14, color: 'rgba(255,255,255,0.55)', lineHeight: 1.6, maxWidth: 520 }}>
{fallbackText(applicationForm.uploadPrompt, participationContent.applicationForm.uploadPrompt)}
</span>
</>
) : (
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: isMobile ? '0 6px' : '0 10px' }}>
<span style={{ fontFamily: FF, fontSize: 15, color: 'rgba(255,255,255,0.45)' }}>{newsletterCopy.footnote}</span>
<span style={{ fontFamily: FF, fontSize: 15, fontWeight: 700, color: 'rgba(239,191,4,0.8)' }}>{newsletterCopy.footnoteStrong}</span>
</div>
)}
</div>
<div style={{ height: 2, background: 'linear-gradient(to right, #EFBF04, rgba(239,191,4,0.25), transparent)', flexShrink: 0 }} />
</section>
<ApplicationFormSection source="participation" />
</div>
);
};