Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -272,7 +272,7 @@ export const participationFields: Field[] = [
|
|||||||
name: 'awardsGrid',
|
name: 'awardsGrid',
|
||||||
label: '06 · Awards grid',
|
label: '06 · Awards grid',
|
||||||
type: 'group',
|
type: 'group',
|
||||||
admin: sectionAdmin('Three-column awards grid rendered directly after the dates banner.'),
|
admin: sectionAdmin('Awards section rendered after the dates banner. On the participation page, Goldene Bavaria is featured above the remaining award cards.'),
|
||||||
fields: [
|
fields: [
|
||||||
text('eyebrow', 'Eyebrow', participationContent.awardsGrid.eyebrow),
|
text('eyebrow', 'Eyebrow', participationContent.awardsGrid.eyebrow),
|
||||||
textarea('heading', 'Heading', participationContent.awardsGrid.heading),
|
textarea('heading', 'Heading', participationContent.awardsGrid.heading),
|
||||||
|
|||||||
@@ -949,7 +949,7 @@ export interface Page {
|
|||||||
| null;
|
| null;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Three-column awards grid rendered directly after the dates banner.
|
* Awards section rendered after the dates banner. On the participation page, Goldene Bavaria is featured above the remaining award cards.
|
||||||
*/
|
*/
|
||||||
awardsGrid?: {
|
awardsGrid?: {
|
||||||
eyebrow?: string | null;
|
eyebrow?: string | null;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const GOLD = '#EFBF04';
|
|||||||
|
|
||||||
type AwardCard = (typeof participationContent.awardsGrid.cards)[number];
|
type AwardCard = (typeof participationContent.awardsGrid.cards)[number];
|
||||||
type AwardCardCms = Partial<AwardCard> & { image?: unknown };
|
type AwardCardCms = Partial<AwardCard> & { image?: unknown };
|
||||||
|
type ResolvedAwardCard = AwardCard & { image?: unknown };
|
||||||
|
|
||||||
export type AwardsGridContent = Partial<typeof participationContent.awardsGrid> & {
|
export type AwardsGridContent = Partial<typeof participationContent.awardsGrid> & {
|
||||||
cards?: AwardCardCms[];
|
cards?: AwardCardCms[];
|
||||||
@@ -23,19 +24,151 @@ export type AwardsGridContent = Partial<typeof participationContent.awardsGrid>
|
|||||||
const fallbackText = (value: unknown, fallback: string) =>
|
const fallbackText = (value: unknown, fallback: string) =>
|
||||||
typeof value === 'string' && value.length > 0 ? value : fallback;
|
typeof value === 'string' && value.length > 0 ? value : fallback;
|
||||||
|
|
||||||
const mergeAwardCards = (value: unknown, fallback: AwardCard[]): (AwardCard & { image?: unknown })[] => {
|
const normalizeLookupText = (value: unknown) =>
|
||||||
|
fallbackText(value, '').replace(/\s+/g, ' ').trim().toLowerCase();
|
||||||
|
|
||||||
|
const findAwardFallback = (card: AwardCardCms, index: number, fallback: AwardCard[]) => {
|
||||||
|
const ctaUrl = normalizeLookupText(card.articleCta?.url);
|
||||||
|
const title = normalizeLookupText(card.title);
|
||||||
|
|
||||||
|
return (
|
||||||
|
fallback.find((item) => ctaUrl.length > 0 && normalizeLookupText(item.articleCta?.url) === ctaUrl) ||
|
||||||
|
fallback.find((item) => title.length > 0 && normalizeLookupText(item.title) === title) ||
|
||||||
|
fallback[index] ||
|
||||||
|
fallback[0]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeAwardCards = (value: unknown, fallback: AwardCard[]): ResolvedAwardCard[] => {
|
||||||
const cards = Array.isArray(value) && value.length > 0 ? (value as AwardCardCms[]) : fallback;
|
const cards = Array.isArray(value) && value.length > 0 ? (value as AwardCardCms[]) : fallback;
|
||||||
return cards.map((card, index) => ({ ...(fallback[index] || fallback[0]), ...card }));
|
return cards.map((card, index) => ({ ...findAwardFallback(card, index, fallback), ...card }));
|
||||||
};
|
};
|
||||||
|
|
||||||
function Lines({ text }: { text: string }) {
|
function Lines({ text }: { text: string }) {
|
||||||
return <>{text.split('\n').map((line, i) => <React.Fragment key={`${line}-${i}`}>{i > 0 && <br />}{line}</React.Fragment>)}</>;
|
return <>{text.split('\n').map((line, i) => <React.Fragment key={`${line}-${i}`}>{i > 0 && <br />}{line}</React.Fragment>)}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AwardsGridSection({ content }: { content?: AwardsGridContent }) {
|
const isGoldeneBavariaCard = (card: ResolvedAwardCard) =>
|
||||||
|
normalizeLookupText(card.articleCta?.url).includes('goldene-bavaria') ||
|
||||||
|
normalizeLookupText(card.title).includes('goldene') ||
|
||||||
|
normalizeLookupText(card.label).includes('goldene');
|
||||||
|
|
||||||
|
const splitFeaturedAwardCard = (cards: ResolvedAwardCard[], enabled: boolean) => {
|
||||||
|
if (!enabled) return { featuredCard: null, supportingCards: cards };
|
||||||
|
|
||||||
|
const featuredIndex = cards.findIndex(isGoldeneBavariaCard);
|
||||||
|
if (featuredIndex < 0) return { featuredCard: null, supportingCards: cards };
|
||||||
|
|
||||||
|
return {
|
||||||
|
featuredCard: cards[featuredIndex],
|
||||||
|
supportingCards: cards.filter((_, index) => index !== featuredIndex),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAwardImageFilename = (card: ResolvedAwardCard, fallbackIndex: number) =>
|
||||||
|
fallbackText(
|
||||||
|
card.imageFilename,
|
||||||
|
participationContent.awardsGrid.cards[fallbackIndex]?.imageFilename || participationContent.awardsGrid.cards[0].imageFilename,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isContainedAwardImage = (imageFilename: string) =>
|
||||||
|
imageFilename.endsWith('.png') || imageFilename.includes('roland-berger');
|
||||||
|
|
||||||
|
function FeaturedAwardCard({ card }: { card: ResolvedAwardCard }) {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
const imageFilename = getAwardImageFilename(card, 0);
|
||||||
|
const isContainedImage = isContainedAwardImage(imageFilename);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section aria-label={fallbackText(card.title, 'Goldene Bavaria')} style={{ position: 'relative', zIndex: 1, display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'minmax(0, 1fr) minmax(320px, 1fr)', borderBottom: '1px solid rgba(3,9,58,0.1)' }}>
|
||||||
|
<div style={{ position: 'relative', height: isMobile ? 180 : 600, background: 'linear-gradient(145deg, #111D55 0%, #1E2B69 44%, #C9A227 100%)', display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
|
||||||
|
<Image unoptimized
|
||||||
|
src={mediaUrl(card.image, `/images/${imageFilename}`)}
|
||||||
|
alt={mediaAlt(card.image, fallbackText(card.imageAlt, fallbackText(card.title, 'Auszeichnung')))}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
objectFit: isContainedImage ? 'contain' : 'cover',
|
||||||
|
objectPosition: 'center',
|
||||||
|
padding: imageFilename.endsWith('.png') ? (isMobile ? 22 : 30) : 0,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
display: 'block',
|
||||||
|
filter: imageFilename.endsWith('.png') ? 'drop-shadow(0 32px 46px rgba(3,9,58,0.34))' : undefined,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to top, rgba(3,9,58,0.42), transparent 42%)', pointerEvents: 'none' }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ background: NAVY, color: '#fff', padding: isMobile ? '24px 24px 30px' : '34px 48px', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
|
||||||
|
<span style={{ fontFamily: FF, fontSize: 10, fontWeight: 700, letterSpacing: '0.32em', textTransform: 'uppercase', color: 'rgba(239,191,4,0.86)', marginBottom: 16 }}>
|
||||||
|
{fallbackText(card.label, 'Auszeichnung 01')}
|
||||||
|
</span>
|
||||||
|
<h3 style={{ fontFamily: FF, fontSize: isMobile ? 'clamp(1.7rem, 8vw, 2.8rem)' : 'clamp(2.1rem, 3.2vw, 3.3rem)', fontWeight: 900, color: '#fff', textTransform: 'uppercase', letterSpacing: '-0.025em', lineHeight: 0.98, margin: '0 0 14px', overflowWrap: 'anywhere' }}>
|
||||||
|
<Lines text={fallbackText(card.title, '')} />
|
||||||
|
</h3>
|
||||||
|
<div style={{ width: 44, height: 2, background: GOLD, marginBottom: 18 }} />
|
||||||
|
<p style={{ fontFamily: FB, fontSize: isMobile ? 16 : 17, color: 'rgba(255,255,255,0.68)', lineHeight: 1.6, margin: 0, maxWidth: 560 }}>
|
||||||
|
{fallbackText(card.description, '')}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', flexDirection: isMobile ? 'column' : 'row', alignItems: 'stretch', gap: 10, marginTop: 24 }}>
|
||||||
|
<Link
|
||||||
|
to={fallbackText(card.articleCta?.url, '#bewerben')}
|
||||||
|
style={{
|
||||||
|
minHeight: 44,
|
||||||
|
padding: '0 18px',
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 8,
|
||||||
|
background: GOLD,
|
||||||
|
color: '#101828',
|
||||||
|
fontFamily: FF,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: 700,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: '0.08em',
|
||||||
|
textDecoration: 'none',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{fallbackText(card.articleCta?.label, 'Artikel lesen')} <ArrowRight size={13} />
|
||||||
|
</Link>
|
||||||
|
<a
|
||||||
|
href={fallbackText(card.mailtoCta?.url, `mailto:${participationContent.form.eligibility.ineligibleContactEmail}`)}
|
||||||
|
style={{
|
||||||
|
minHeight: 44,
|
||||||
|
padding: '0 18px',
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 8,
|
||||||
|
color: '#fff',
|
||||||
|
border: '1px solid rgba(255,255,255,0.24)',
|
||||||
|
fontFamily: FF,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: 700,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: '0.08em',
|
||||||
|
textDecoration: 'none',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Mail size={13} /> {fallbackText(card.mailtoCta?.label, 'Kontakt aufnehmen')}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AwardsGridSection({ content, featuredGoldeneBavaria = false }: { content?: AwardsGridContent; featuredGoldeneBavaria?: boolean }) {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const section = { ...participationContent.awardsGrid, ...(content || {}) };
|
const section = { ...participationContent.awardsGrid, ...(content || {}) };
|
||||||
const cards = mergeAwardCards(section.cards, participationContent.awardsGrid.cards);
|
const cards = mergeAwardCards(section.cards, participationContent.awardsGrid.cards);
|
||||||
|
const { featuredCard, supportingCards } = splitFeaturedAwardCard(cards, featuredGoldeneBavaria);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="auszeichnungen" style={{ background: '#F7F7F5', position: 'relative', overflow: 'hidden', isolation: 'isolate' }}>
|
<section id="auszeichnungen" style={{ background: '#F7F7F5', position: 'relative', overflow: 'hidden', isolation: 'isolate' }}>
|
||||||
@@ -54,105 +187,111 @@ export default function AwardsGridSection({ content }: { content?: AwardsGridCon
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ position: 'relative', zIndex: 1, display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'repeat(3, minmax(0, 1fr))', borderBottom: '1px solid rgba(3,9,58,0.08)' }}>
|
{featuredCard ? <FeaturedAwardCard card={featuredCard} /> : null}
|
||||||
{cards.map((card, index) => {
|
|
||||||
const imageFilename = fallbackText(card.imageFilename, participationContent.awardsGrid.cards[index]?.imageFilename || participationContent.awardsGrid.cards[0].imageFilename);
|
|
||||||
const isContainedImage = imageFilename.endsWith('.png') || imageFilename.includes('roland-berger');
|
|
||||||
const isLast = index === cards.length - 1;
|
|
||||||
|
|
||||||
return (
|
{supportingCards.length > 0 ? (
|
||||||
<article
|
<div style={{ position: 'relative', zIndex: 1, display: 'grid', gridTemplateColumns: isMobile ? '1fr' : `repeat(${Math.min(Math.max(supportingCards.length, 1), 3)}, minmax(0, 1fr))`, borderBottom: '1px solid rgba(3,9,58,0.08)' }}>
|
||||||
key={`${fallbackText(card.title, 'award')}-${index}`}
|
{supportingCards.map((card, index) => {
|
||||||
style={{
|
const sourceIndex = cards.indexOf(card);
|
||||||
display: 'flex',
|
const imageFilename = getAwardImageFilename(card, sourceIndex >= 0 ? sourceIndex : index);
|
||||||
flexDirection: 'column',
|
const isContainedImage = isContainedAwardImage(imageFilename);
|
||||||
minHeight: isMobile ? 'auto' : 620,
|
const isLast = index === supportingCards.length - 1;
|
||||||
borderRight: !isMobile && !isLast ? '1px solid rgba(3,9,58,0.1)' : 'none',
|
const isDarkCard = !featuredCard && index === 1;
|
||||||
borderBottom: isMobile && !isLast ? '1px solid rgba(3,9,58,0.1)' : 'none',
|
|
||||||
background: index === 1 ? NAVY : 'rgba(255,255,255,0.72)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ height: isMobile ? 220 : 260, background: imageFilename.includes('roland-berger') ? '#DDE7E2' : NAVY, display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
|
|
||||||
<Image unoptimized
|
|
||||||
src={mediaUrl(card.image, `/images/${imageFilename}`)}
|
|
||||||
alt={mediaAlt(card.image, fallbackText(card.imageAlt, fallbackText(card.title, 'Auszeichnung')))}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
objectFit: isContainedImage ? 'contain' : 'cover',
|
|
||||||
objectPosition: index === 1 ? 'center top' : 'center',
|
|
||||||
padding: imageFilename.endsWith('.png') ? 22 : 0,
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
display: 'block',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ padding: isMobile ? '28px 24px 32px' : '36px 36px 40px', display: 'flex', flexDirection: 'column', flex: 1 }}>
|
return (
|
||||||
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, letterSpacing: '0.26em', textTransform: 'uppercase', color: index === 1 ? 'rgba(239,191,4,0.8)' : '#4A8FC9', marginBottom: 12 }}>
|
<article
|
||||||
{fallbackText(card.label, '')}
|
key={`${fallbackText(card.title, 'award')}-${index}`}
|
||||||
</span>
|
style={{
|
||||||
<h3 style={{ fontFamily: FF, fontSize: 'clamp(1.25rem, 2vw, 1.7rem)', fontWeight: 900, color: index === 1 ? '#fff' : '#101828', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.08, margin: '0 0 16px', overflowWrap: 'anywhere' }}>
|
display: 'flex',
|
||||||
<Lines text={fallbackText(card.title, '')} />
|
flexDirection: 'column',
|
||||||
</h3>
|
minHeight: isMobile ? 'auto' : featuredCard ? 540 : 620,
|
||||||
<div style={{ width: 36, height: 2, background: GOLD, marginBottom: 22 }} />
|
borderRight: !isMobile && !isLast ? '1px solid rgba(3,9,58,0.1)' : 'none',
|
||||||
<p style={{ fontFamily: FB, fontSize: 18, color: index === 1 ? 'rgba(255,255,255,0.62)' : 'rgba(16,24,40,0.55)', lineHeight: 1.7, margin: 0, flex: 1 }}>
|
borderBottom: isMobile && !isLast ? '1px solid rgba(3,9,58,0.1)' : 'none',
|
||||||
{fallbackText(card.description, '')}
|
background: isDarkCard ? NAVY : 'rgba(255,255,255,0.72)',
|
||||||
</p>
|
}}
|
||||||
|
>
|
||||||
<div style={{ display: 'flex', flexDirection: isMobile ? 'column' : 'row', gap: 10, marginTop: 32 }}>
|
<div style={{ height: isMobile ? 220 : 260, background: imageFilename.includes('roland-berger') ? '#DDE7E2' : NAVY, display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
|
||||||
<Link
|
<Image unoptimized
|
||||||
to={fallbackText(card.articleCta?.url, '#bewerben')}
|
src={mediaUrl(card.image, `/images/${imageFilename}`)}
|
||||||
|
alt={mediaAlt(card.image, fallbackText(card.imageAlt, fallbackText(card.title, 'Auszeichnung')))}
|
||||||
style={{
|
style={{
|
||||||
minHeight: 46,
|
width: '100%',
|
||||||
padding: '0 18px',
|
height: '100%',
|
||||||
display: 'inline-flex',
|
objectFit: isContainedImage ? 'contain' : 'cover',
|
||||||
alignItems: 'center',
|
objectPosition: isDarkCard ? 'center top' : 'center',
|
||||||
justifyContent: 'center',
|
padding: imageFilename.endsWith('.png') ? 22 : 0,
|
||||||
gap: 8,
|
boxSizing: 'border-box',
|
||||||
background: GOLD,
|
display: 'block',
|
||||||
color: '#101828',
|
|
||||||
fontFamily: FF,
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: 700,
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
letterSpacing: '0.08em',
|
|
||||||
textDecoration: 'none',
|
|
||||||
whiteSpace: 'normal',
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
{fallbackText(card.articleCta?.label, 'Artikel lesen')} <ArrowRight size={13} />
|
|
||||||
</Link>
|
|
||||||
<a
|
|
||||||
href={fallbackText(card.mailtoCta?.url, `mailto:${participationContent.form.eligibility.ineligibleContactEmail}`)}
|
|
||||||
style={{
|
|
||||||
minHeight: 46,
|
|
||||||
padding: '0 18px',
|
|
||||||
display: 'inline-flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
gap: 8,
|
|
||||||
color: index === 1 ? '#fff' : NAVY,
|
|
||||||
border: `1px solid ${index === 1 ? 'rgba(255,255,255,0.22)' : 'rgba(3,9,58,0.18)'}`,
|
|
||||||
fontFamily: FF,
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: 700,
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
letterSpacing: '0.08em',
|
|
||||||
textDecoration: 'none',
|
|
||||||
whiteSpace: 'normal',
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Mail size={13} /> {fallbackText(card.mailtoCta?.label, 'Kontakt aufnehmen')}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</article>
|
<div style={{ padding: isMobile ? '28px 24px 32px' : featuredCard ? '38px 44px 42px' : '36px 36px 40px', display: 'flex', flexDirection: 'column', flex: 1 }}>
|
||||||
);
|
<span style={{ fontFamily: FF, fontSize: 9, fontWeight: 700, letterSpacing: '0.26em', textTransform: 'uppercase', color: isDarkCard ? 'rgba(239,191,4,0.8)' : '#4A8FC9', marginBottom: 12 }}>
|
||||||
})}
|
{fallbackText(card.label, '')}
|
||||||
</div>
|
</span>
|
||||||
|
<h3 style={{ fontFamily: FF, fontSize: 'clamp(1.25rem, 2vw, 1.7rem)', fontWeight: 900, color: isDarkCard ? '#fff' : '#101828', textTransform: 'uppercase', letterSpacing: '-0.02em', lineHeight: 1.08, margin: '0 0 16px', overflowWrap: 'anywhere' }}>
|
||||||
|
<Lines text={fallbackText(card.title, '')} />
|
||||||
|
</h3>
|
||||||
|
<div style={{ width: 36, height: 2, background: GOLD, marginBottom: 22 }} />
|
||||||
|
<p style={{ fontFamily: FB, fontSize: 18, color: isDarkCard ? 'rgba(255,255,255,0.62)' : 'rgba(16,24,40,0.55)', lineHeight: 1.7, margin: 0, flex: 1 }}>
|
||||||
|
{fallbackText(card.description, '')}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', flexDirection: isMobile ? 'column' : 'row', gap: 10, marginTop: 32 }}>
|
||||||
|
<Link
|
||||||
|
to={fallbackText(card.articleCta?.url, '#bewerben')}
|
||||||
|
style={{
|
||||||
|
minHeight: 46,
|
||||||
|
padding: '0 18px',
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 8,
|
||||||
|
background: GOLD,
|
||||||
|
color: '#101828',
|
||||||
|
fontFamily: FF,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: 700,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: '0.08em',
|
||||||
|
textDecoration: 'none',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{fallbackText(card.articleCta?.label, 'Artikel lesen')} <ArrowRight size={13} />
|
||||||
|
</Link>
|
||||||
|
<a
|
||||||
|
href={fallbackText(card.mailtoCta?.url, `mailto:${participationContent.form.eligibility.ineligibleContactEmail}`)}
|
||||||
|
style={{
|
||||||
|
minHeight: 46,
|
||||||
|
padding: '0 18px',
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 8,
|
||||||
|
color: isDarkCard ? '#fff' : NAVY,
|
||||||
|
border: `1px solid ${isDarkCard ? 'rgba(255,255,255,0.22)' : 'rgba(3,9,58,0.18)'}`,
|
||||||
|
fontFamily: FF,
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: 700,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: '0.08em',
|
||||||
|
textDecoration: 'none',
|
||||||
|
whiteSpace: 'normal',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Mail size={13} /> {fallbackText(card.mailtoCta?.label, 'Kontakt aufnehmen')}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export default function WegZurAuszeichnungSection({ content }: { content?: Proce
|
|||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p style={{ fontFamily: FB, fontSize: 15, color: 'rgba(255,255,255,0.68)', lineHeight: 1.7, margin: 0 }}>
|
<p style={{ fontFamily: FB, fontSize: 17, color: 'rgba(255,255,255,0.68)', lineHeight: 1.7, margin: 0 }}>
|
||||||
{fallbackText(item.desc, '')}
|
{fallbackText(item.desc, '')}
|
||||||
</p>
|
</p>
|
||||||
{/* Step progress indicator */}
|
{/* Step progress indicator */}
|
||||||
@@ -171,7 +171,7 @@ export default function WegZurAuszeichnungSection({ content }: { content?: Proce
|
|||||||
{fallbackText(item.title, '')}
|
{fallbackText(item.title, '')}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<p style={{ fontFamily: FB, fontSize: 15, color: 'rgba(255,255,255,0.68)', lineHeight: 1.75, margin: 0 }}>
|
<p style={{ fontFamily: FB, fontSize: 17, color: 'rgba(255,255,255,0.68)', lineHeight: 1.7, margin: 0 }}>
|
||||||
{fallbackText(item.desc, '')}
|
{fallbackText(item.desc, '')}
|
||||||
</p>
|
</p>
|
||||||
{/* Step progress indicator */}
|
{/* Step progress indicator */}
|
||||||
|
|||||||
@@ -26,22 +26,29 @@ const fallbackText = (value: unknown, fallback: string) =>
|
|||||||
|
|
||||||
const logoShellStyle: React.CSSProperties = {
|
const logoShellStyle: React.CSSProperties = {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
boxSizing: 'border-box',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
maxHeight: '100%',
|
maxHeight: '100%',
|
||||||
|
maxWidth: '100%',
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
|
overflow: 'hidden',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}
|
}
|
||||||
|
|
||||||
const containedTextStyle: React.CSSProperties = {
|
const containedTextStyle: React.CSSProperties = {
|
||||||
|
display: 'block',
|
||||||
|
maxWidth: '100%',
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
|
textAlign: 'center',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
}
|
}
|
||||||
|
|
||||||
const fittedTextSize = (text: string, preferred: number, compact = 12) => {
|
const fittedTextSize = (text: string, preferred: number, compact = 12) => {
|
||||||
if (text.length > 18) return compact - 1
|
if (text.length > 24) return Math.max(9, compact - 3)
|
||||||
if (text.length > 13) return compact
|
if (text.length > 18) return Math.max(10, compact - 2)
|
||||||
|
if (text.length > 13) return compact - 1
|
||||||
if (text.length > 9) return Math.max(compact + 1, preferred - 3)
|
if (text.length > 9) return Math.max(compact + 1, preferred - 3)
|
||||||
return preferred
|
return preferred
|
||||||
}
|
}
|
||||||
@@ -80,7 +87,7 @@ export function PartnerLogoMark({
|
|||||||
if (partner.logoKind === 'badge') {
|
if (partner.logoKind === 'badge') {
|
||||||
return (
|
return (
|
||||||
<span style={logoShellStyle}>
|
<span style={logoShellStyle}>
|
||||||
<span style={{ background: color, color: '#fff', fontWeight: 900, fontSize: size(14), padding: `${size(4)}px ${size(10)}px`, lineHeight: 1.25, display: 'inline-block', textAlign: 'center', fontFamily: FF }}>
|
<span style={{ background: color, color: '#fff', fontWeight: 900, fontSize: size(fittedTextSize(text, 14, 11)), padding: `${size(4)}px ${size(10)}px`, lineHeight: 1.25, display: 'inline-block', maxWidth: '100%', textAlign: 'center', fontFamily: FF }}>
|
||||||
{text.split('\n').map((line, index) => (
|
{text.split('\n').map((line, index) => (
|
||||||
<React.Fragment key={`${line}-${index}`}>{index > 0 && <br />}{line}</React.Fragment>
|
<React.Fragment key={`${line}-${index}`}>{index > 0 && <br />}{line}</React.Fragment>
|
||||||
))}
|
))}
|
||||||
@@ -92,7 +99,7 @@ export function PartnerLogoMark({
|
|||||||
if (partner.logoKind === 'monogram') {
|
if (partner.logoKind === 'monogram') {
|
||||||
return (
|
return (
|
||||||
<span style={logoShellStyle}>
|
<span style={logoShellStyle}>
|
||||||
<span style={{ display: 'flex', alignItems: 'center', gap: size(6), minWidth: 0 }}>
|
<span style={{ display: 'flex', alignItems: 'center', gap: size(6), maxWidth: '100%', minWidth: 0 }}>
|
||||||
<span style={{ width: size(22), height: size(22), background: '#3a3a3a', color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 900, fontSize: size(11), fontFamily: FF, flexShrink: 0 }}>{subtext || text.slice(0, 2)}</span>
|
<span style={{ width: size(22), height: size(22), background: '#3a3a3a', color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 900, fontSize: size(11), fontFamily: FF, flexShrink: 0 }}>{subtext || text.slice(0, 2)}</span>
|
||||||
<span style={{ ...containedTextStyle, fontWeight: 700, fontSize: size(fittedTextSize(text, 15, 11)), color, fontFamily: FF }}>{text}</span>
|
<span style={{ ...containedTextStyle, fontWeight: 700, fontSize: size(fittedTextSize(text, 15, 11)), color, fontFamily: FF }}>{text}</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -103,7 +110,7 @@ export function PartnerLogoMark({
|
|||||||
if (partner.logoKind === 'dot') {
|
if (partner.logoKind === 'dot') {
|
||||||
return (
|
return (
|
||||||
<span style={logoShellStyle}>
|
<span style={logoShellStyle}>
|
||||||
<span style={{ display: 'flex', alignItems: 'center', gap: size(6), minWidth: 0 }}>
|
<span style={{ display: 'flex', alignItems: 'center', gap: size(6), maxWidth: '100%', minWidth: 0 }}>
|
||||||
<span style={{ width: size(20), height: size(20), borderRadius: '50%', background: color, display: 'block', flexShrink: 0 }} />
|
<span style={{ width: size(20), height: size(20), borderRadius: '50%', background: color, display: 'block', flexShrink: 0 }} />
|
||||||
<span style={{ ...containedTextStyle, fontWeight: 700, fontSize: size(fittedTextSize(text, 16, 11)), color, fontFamily: FF }}>{text}</span>
|
<span style={{ ...containedTextStyle, fontWeight: 700, fontSize: size(fittedTextSize(text, 16, 11)), color, fontFamily: FF }}>{text}</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -114,7 +121,7 @@ export function PartnerLogoMark({
|
|||||||
if (partner.logoKind === 'leaf') {
|
if (partner.logoKind === 'leaf') {
|
||||||
return (
|
return (
|
||||||
<span style={logoShellStyle}>
|
<span style={logoShellStyle}>
|
||||||
<span style={{ display: 'flex', alignItems: 'center', gap: size(6), minWidth: 0 }}>
|
<span style={{ display: 'flex', alignItems: 'center', gap: size(6), maxWidth: '100%', minWidth: 0 }}>
|
||||||
<svg width={size(20)} height={size(20)} viewBox="0 0 20 20" aria-hidden="true" style={{ flexShrink: 0 }}><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>
|
<svg width={size(20)} height={size(20)} viewBox="0 0 20 20" aria-hidden="true" style={{ flexShrink: 0 }}><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={{ ...containedTextStyle, fontWeight: 700, fontSize: size(fittedTextSize(text, 15, 11)), color, fontFamily: FB }}>{text}</span>
|
<span style={{ ...containedTextStyle, fontWeight: 700, fontSize: size(fittedTextSize(text, 15, 11)), color, fontFamily: FB }}>{text}</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -125,7 +132,7 @@ export function PartnerLogoMark({
|
|||||||
if (partner.logoKind === 'rsm') {
|
if (partner.logoKind === 'rsm') {
|
||||||
return (
|
return (
|
||||||
<span style={logoShellStyle}>
|
<span style={logoShellStyle}>
|
||||||
<span style={{ display: 'flex', alignItems: 'center', gap: size(6), minWidth: 0 }}>
|
<span style={{ display: 'flex', alignItems: 'center', gap: size(6), maxWidth: '100%', minWidth: 0 }}>
|
||||||
<span style={{ display: 'flex', gap: Math.max(2, size(2)), flexShrink: 0 }}>
|
<span style={{ display: 'flex', gap: Math.max(2, size(2)), flexShrink: 0 }}>
|
||||||
<span style={{ width: size(10), height: size(10), background: '#6b7280', display: 'block' }} />
|
<span style={{ width: size(10), height: size(10), background: '#6b7280', display: 'block' }} />
|
||||||
<span style={{ width: size(10), height: size(10), background: '#22c55e', display: 'block' }} />
|
<span style={{ width: size(10), height: size(10), background: '#22c55e', display: 'block' }} />
|
||||||
@@ -140,7 +147,7 @@ export function PartnerLogoMark({
|
|||||||
if (partner.logoKind === 'deutschebank') {
|
if (partner.logoKind === 'deutschebank') {
|
||||||
return (
|
return (
|
||||||
<span style={logoShellStyle}>
|
<span style={logoShellStyle}>
|
||||||
<span style={{ display: 'flex', alignItems: 'center', gap: size(8), minWidth: 0 }}>
|
<span style={{ display: 'flex', alignItems: 'center', gap: size(8), maxWidth: '100%', minWidth: 0 }}>
|
||||||
<svg width={size(20)} height={size(20)} viewBox="0 0 20 20" aria-hidden="true" style={{ flexShrink: 0 }}><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>
|
<svg width={size(20)} height={size(20)} viewBox="0 0 20 20" aria-hidden="true" style={{ flexShrink: 0 }}><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={{ ...containedTextStyle, fontWeight: 600, fontSize: size(fittedTextSize(text, 15, 11)), color, fontFamily: FB }}>{text}</span>
|
<span style={{ ...containedTextStyle, fontWeight: 600, fontSize: size(fittedTextSize(text, 15, 11)), color, fontFamily: FB }}>{text}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { Link } from '@/spa/router';
|
import { Link } from '@/spa/router';
|
||||||
import { ChevronRight, X, ArrowRight, Globe, Linkedin, Building2, MapPin, ExternalLink, Play } from 'lucide-react';
|
import { X, ArrowRight, Globe, Linkedin, Building2, MapPin, ExternalLink, Play } from 'lucide-react';
|
||||||
import SponsoringForm from '@/spa/components/forms/SponsoringForm';
|
import SponsoringForm from '@/spa/components/forms/SponsoringForm';
|
||||||
import MunichSkylineBg from '@/spa/components/ui/munich-skyline-bg';
|
import MunichSkylineBg from '@/spa/components/ui/munich-skyline-bg';
|
||||||
import { useIsMobile } from '@/spa/hooks/useIsMobile';
|
import { useIsMobile } from '@/spa/hooks/useIsMobile';
|
||||||
@@ -16,7 +16,6 @@ const FB = '"Inter", sans-serif';
|
|||||||
const NAVY = '#111D55';
|
const NAVY = '#111D55';
|
||||||
const GOLD = '#EFBF04';
|
const GOLD = '#EFBF04';
|
||||||
const CREAM = '#E4E2E3';
|
const CREAM = '#E4E2E3';
|
||||||
const HIDE_MEMBERSHIP_NETWORK_CTA = true;
|
|
||||||
|
|
||||||
type NetzwerkCms = Partial<typeof netzwerkContent> & {
|
type NetzwerkCms = Partial<typeof netzwerkContent> & {
|
||||||
hero?: Partial<typeof netzwerkContent.hero> & { image?: unknown }
|
hero?: Partial<typeof netzwerkContent.hero> & { image?: unknown }
|
||||||
@@ -98,12 +97,6 @@ const sortedJuryMembers = (members: JuryMember[]) =>
|
|||||||
.map((member, index) => ({ member, index }))
|
.map((member, index) => ({ member, index }))
|
||||||
.sort((a, b) => Number(a.member.sortOrder ?? a.index) - Number(b.member.sortOrder ?? b.index))
|
.sort((a, b) => Number(a.member.sortOrder ?? a.index) - Number(b.member.sortOrder ?? b.index))
|
||||||
.map(({ member }) => member)
|
.map(({ member }) => member)
|
||||||
const isMembershipCta = (cta?: { label?: string | null; url?: string | null } | null) => {
|
|
||||||
const normalizedLabel = cta?.label?.trim().toLowerCase();
|
|
||||||
|
|
||||||
return cta?.url === '/mitglied-werden' || normalizedLabel === 'mitglied werden' || normalizedLabel === 'vereinsmitglied werden';
|
|
||||||
}
|
|
||||||
|
|
||||||
function Lines({ text, highlight }: { text: string; highlight?: string }) {
|
function Lines({ text, highlight }: { text: string; highlight?: string }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -174,10 +167,10 @@ const JuryCard: React.FC<{ member: JuryMember }> = ({ member }) => {
|
|||||||
<div
|
<div
|
||||||
onMouseEnter={() => setHovered(true)}
|
onMouseEnter={() => setHovered(true)}
|
||||||
onMouseLeave={() => setHovered(false)}
|
onMouseLeave={() => setHovered(false)}
|
||||||
style={{ display: 'flex', flexDirection: 'column', height: '100%', cursor: 'default', background: hovered ? 'rgba(255,255,255,0.04)' : NAVY, transition: 'background 0.2s', ...(isMobileCard ? { minWidth: '58vw', maxWidth: '58vw', flexShrink: 0, scrollSnapAlign: 'start', border: '1px solid rgba(255,255,255,0.08)', borderRadius: 16, overflow: 'hidden' } : { minWidth: 0, border: '1px solid rgba(255,255,255,0.07)' }) }}
|
style={{ display: 'flex', flexDirection: 'column', height: '100%', cursor: 'default', background: hovered ? 'rgba(255,255,255,0.04)' : NAVY, transition: 'background 0.2s', ...(isMobileCard ? { minWidth: '72vw', maxWidth: '72vw', flexShrink: 0, scrollSnapAlign: 'start', border: '1px solid rgba(255,255,255,0.08)', borderRadius: 16, overflow: 'hidden' } : { minWidth: 0, border: '1px solid rgba(255,255,255,0.07)' }) }}
|
||||||
>
|
>
|
||||||
<div style={{ position: 'relative', overflow: 'hidden', height: isMobileCard ? 142 : 170, flexShrink: 0 }}>
|
<div style={{ position: 'relative', overflow: 'hidden', height: isMobileCard ? 142 : 170, flexShrink: 0, background: 'rgba(255,255,255,0.03)' }}>
|
||||||
<Image unoptimized src={managedImage(member.imageUpload, member.image)} alt={fallbackText(member.imageAlt, member.name)} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center top', filter: hovered ? 'none' : 'grayscale(100%)', transition: 'filter 0.5s', display: 'block' }} />
|
<Image unoptimized src={managedImage(member.imageUpload, member.image)} alt={fallbackText(member.imageAlt, member.name)} style={{ width: '100%', height: '100%', objectFit: 'contain', 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: '40%', background: 'linear-gradient(to top, rgba(3,9,58,0.65), transparent)' }} />
|
<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>
|
||||||
<div style={{ padding: isMobileCard ? '18px 18px 22px' : '20px 20px 24px', flex: 1, minWidth: 0, overflowWrap: 'anywhere', wordBreak: 'break-word' }}>
|
<div style={{ padding: isMobileCard ? '18px 18px 22px' : '20px 20px 24px', flex: 1, minWidth: 0, overflowWrap: 'anywhere', wordBreak: 'break-word' }}>
|
||||||
@@ -215,8 +208,8 @@ const JuryChairCard: React.FC<{ member: JuryMember; badge: string }> = ({ member
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ position: 'relative', overflow: 'hidden', aspectRatio: isMobileCard ? '16/10' : '3/4', minHeight: isMobileCard ? 240 : 'auto' }}>
|
<div style={{ position: 'relative', overflow: 'hidden', aspectRatio: isMobileCard ? '16/10' : '3/4', minHeight: isMobileCard ? 240 : 'auto', background: 'rgba(255,255,255,0.03)' }}>
|
||||||
<Image unoptimized src={managedImage(member.imageUpload, member.image)} alt={fallbackText(member.imageAlt, member.name)} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center top', filter: hovered ? 'none' : 'grayscale(100%)', transition: 'filter 0.5s', display: 'block' }} />
|
<Image unoptimized src={managedImage(member.imageUpload, member.image)} alt={fallbackText(member.imageAlt, member.name)} style={{ width: '100%', height: '100%', objectFit: 'contain', 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 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>
|
||||||
<div style={{ padding: isMobileCard ? '32px 28px 36px' : '56px 56px', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
|
<div style={{ padding: isMobileCard ? '32px 28px 36px' : '56px 56px', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
|
||||||
@@ -354,20 +347,25 @@ function PartnerModal({ partner, copy, onClose }: { partner: Partner; copy: Part
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Logo box – bottom left (overflows hero) */}
|
{/* Logo box – bottom left */}
|
||||||
<div style={{
|
<div style={{
|
||||||
position: 'absolute', bottom: -28, left: 28,
|
position: 'absolute', bottom: -38, left: 28,
|
||||||
width: 80, height: 80, background: '#fff',
|
width: 'min(260px, calc(100% - 56px))', minWidth: 112, height: 112, background: '#fff',
|
||||||
borderRadius: 12, boxShadow: '0 4px 24px rgba(0,0,0,0.14)',
|
borderRadius: 12, boxShadow: '0 4px 24px rgba(0,0,0,0.14)',
|
||||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
padding: 10, zIndex: 2,
|
boxSizing: 'border-box',
|
||||||
|
flexShrink: 0,
|
||||||
|
overflow: 'hidden',
|
||||||
|
padding: 14, zIndex: 2,
|
||||||
}}>
|
}}>
|
||||||
<PartnerLogoMark partner={partner} />
|
<div style={{ width: '100%', height: '100%', minWidth: 0, overflow: 'hidden' }}>
|
||||||
|
<PartnerLogoMark partner={partner} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Content ── */}
|
{/* ── Content ── */}
|
||||||
<div style={{ padding: '52px 32px 32px' }}>
|
<div style={{ padding: '72px 32px 32px' }}>
|
||||||
<h2 style={{
|
<h2 style={{
|
||||||
fontFamily: FF, fontSize: 'clamp(1.5rem, 3vw, 2rem)',
|
fontFamily: FF, fontSize: 'clamp(1.5rem, 3vw, 2rem)',
|
||||||
fontWeight: 900, color: '#101828',
|
fontWeight: 900, color: '#101828',
|
||||||
@@ -507,9 +505,6 @@ const Netzwerk: React.FC = () => {
|
|||||||
const greetingVideoTitle = fallbackText(homeVideoModal.title, homeContent.videoModal.title);
|
const greetingVideoTitle = fallbackText(homeVideoModal.title, homeContent.videoModal.title);
|
||||||
const greetingVideoLabel = fallbackText(home.hero?.videoLabel, homeContent.hero.videoLabel);
|
const greetingVideoLabel = fallbackText(home.hero?.videoLabel, homeContent.hero.videoLabel);
|
||||||
const greetingVideoCloseLabel = fallbackText(homeVideoModal.closeLabel, homeContent.videoModal.closeLabel);
|
const greetingVideoCloseLabel = fallbackText(homeVideoModal.closeLabel, homeContent.videoModal.closeLabel);
|
||||||
const showMembershipFootnote =
|
|
||||||
!HIDE_MEMBERSHIP_NETWORK_CTA ||
|
|
||||||
!isMembershipCta({ label: sponsoring.footnoteLabel, url: sponsoring.footnoteUrl });
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!showGreetingVideo || !greetingVideoSrc) return;
|
if (!showGreetingVideo || !greetingVideoSrc) return;
|
||||||
@@ -712,18 +707,11 @@ const Netzwerk: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Übrige Jury-Mitglieder ──────────────────────────────────────── */}
|
{/* ── Übrige Jury-Mitglieder ──────────────────────────────────────── */}
|
||||||
<div style={{ display: isMobile ? 'flex' : 'grid', gridTemplateColumns: isMobile ? undefined : 'repeat(auto-fill, minmax(210px, 1fr))', overflowX: isMobile ? 'auto' : undefined, scrollSnapType: isMobile ? 'x mandatory' : undefined, gap: isMobile ? 12 : 0, padding: isMobile ? '24px 24px 12px' : '0', WebkitOverflowScrolling: 'touch', scrollbarWidth: 'none', borderTop: isMobile ? 'none' : '1px solid rgba(255,255,255,0.07)' }}>
|
<div style={{ display: isMobile ? 'flex' : 'grid', gridTemplateColumns: isMobile ? undefined : 'repeat(auto-fill, minmax(260px, 1fr))', overflowX: isMobile ? 'auto' : undefined, scrollSnapType: isMobile ? 'x mandatory' : undefined, gap: isMobile ? 12 : 0, padding: isMobile ? '24px 24px 36px' : '0 0 56px', WebkitOverflowScrolling: 'touch', scrollbarWidth: 'none', borderTop: isMobile ? 'none' : '1px solid rgba(255,255,255,0.07)' }}>
|
||||||
{juryMembers.slice(1).map((member, idx) => (
|
{juryMembers.slice(1).map((member, idx) => (
|
||||||
<JuryCard key={idx} member={member} />
|
<JuryCard key={idx} member={member} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</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>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
@@ -850,40 +838,6 @@ const Netzwerk: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Footnote */}
|
|
||||||
{showMembershipFootnote && (
|
|
||||||
<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 }} />
|
<div style={{ position: 'relative', zIndex: 1, height: 2, background: `linear-gradient(to right, ${GOLD}, rgba(239,191,4,0.3), transparent)`, flexShrink: 0 }} />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -169,14 +169,6 @@ const Participation: React.FC = () => {
|
|||||||
>
|
>
|
||||||
{fallbackText(eligibility.primaryCta?.label, participationContent.eligibility.primaryCta.label)} <ArrowRight size={13} />
|
{fallbackText(eligibility.primaryCta?.label, participationContent.eligibility.primaryCta.label)} <ArrowRight size={13} />
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
|
||||||
to={fallbackText(eligibility.secondaryCta?.url, participationContent.eligibility.secondaryCta.url)}
|
|
||||||
style={{ fontFamily: FF, fontSize: 16, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: NAVY, border: `1.5px solid rgba(3,9,58,0.3)`, padding: '10px 22px', textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 8, transition: 'border-color 0.2s, color 0.2s', whiteSpace: 'nowrap' }}
|
|
||||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.borderColor = NAVY; }}
|
|
||||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.borderColor = 'rgba(3,9,58,0.3)'; }}
|
|
||||||
>
|
|
||||||
{fallbackText(eligibility.secondaryCta?.label, participationContent.eligibility.secondaryCta.label)}
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.5)', lineHeight: 1.8, margin: 0, textAlign: isMobile ? 'left' : 'right' }}>
|
<p style={{ fontFamily: FB, fontSize: 18, color: 'rgba(16,24,40,0.5)', lineHeight: 1.8, margin: 0, textAlign: isMobile ? 'left' : 'right' }}>
|
||||||
{fallbackText(eligibility.description, participationContent.eligibility.description)}
|
{fallbackText(eligibility.description, participationContent.eligibility.description)}
|
||||||
@@ -269,7 +261,7 @@ const Participation: React.FC = () => {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── AWARDS GRID ─────────────────────────────────────────────────────── */}
|
{/* ── AWARDS GRID ─────────────────────────────────────────────────────── */}
|
||||||
<AwardsGridSection content={awardsGrid} />
|
<AwardsGridSection content={awardsGrid} featuredGoldeneBavaria />
|
||||||
|
|
||||||
{/* ── FORM SECTION ─────────────────────────────────────────────────────── */}
|
{/* ── 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">
|
<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">
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user