feat: connect header and footer to cms as globals
This commit is contained in:
@@ -5,79 +5,26 @@ import { Link, useLocation } from '@/spa/router';
|
||||
import { ArrowRight, Upload } from 'lucide-react';
|
||||
import LoadingScreen from '@/spa/components/ui/LoadingScreen';
|
||||
import { useIsMobile } from '@/spa/hooks/useIsMobile';
|
||||
import {
|
||||
defaultFooterData,
|
||||
defaultHeaderData,
|
||||
type SpaFooterData,
|
||||
type SpaHeaderData,
|
||||
type SpaIconName,
|
||||
} from '@/spa/cmsNavigation';
|
||||
|
||||
interface LayoutProps { children: React.ReactNode; }
|
||||
interface LayoutProps { children: React.ReactNode; header?: SpaHeaderData; footer?: SpaFooterData; }
|
||||
|
||||
/* ── SVG icons ─────────────────────────────────────────── */
|
||||
const IgIcon = () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="2" width="20" height="20" rx="5"/><circle cx="12" cy="12" r="4"/><circle cx="17.5" cy="6.5" r=".8" fill="currentColor" stroke="none"/></svg>;
|
||||
const LiIcon = () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M16 8a6 6 0 016 6v7h-4v-7a2 2 0 00-2-2 2 2 0 00-2 2v7h-4v-7a6 6 0 016-6z"/><rect x="2" y="9" width="4" height="12"/><circle cx="4" cy="4" r="2"/></svg>;
|
||||
const FbIcon = () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M18 2h-3a5 5 0 00-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 011-1h3z"/></svg>;
|
||||
const SOCIAL = [
|
||||
{ href:'https://instagram.com', Icon:IgIcon, label:'Instagram' },
|
||||
{ href:'https://linkedin.com', Icon:LiIcon, label:'LinkedIn' },
|
||||
{ href:'https://facebook.com', Icon:FbIcon, label:'Facebook' },
|
||||
];
|
||||
const ICONS: Record<SpaIconName, () => React.ReactElement> = {
|
||||
instagram: IgIcon,
|
||||
linkedin: LiIcon,
|
||||
facebook: FbIcon,
|
||||
};
|
||||
|
||||
/* ── Nav data ──────────────────────────────────────────── */
|
||||
interface NavItem {
|
||||
key: string; label: string; path: string;
|
||||
subLabel?: string;
|
||||
sub?: { label: string; path: string }[];
|
||||
}
|
||||
const NAV: NavItem[] = [
|
||||
{
|
||||
key: 'preistraeger', label: 'Preisträger:innen', path: '/preistraeger',
|
||||
subLabel: 'PREISTRÄGER:INNEN',
|
||||
sub: [
|
||||
{ label: 'Alle Preisträger:innen', path: '/preistraeger' },
|
||||
{ label: 'Sieger & Aufsteiger 2025', path: '/preistraeger' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'teilnahme', label: 'Ihr Weg zum Preis', path: '/teilnahme',
|
||||
subLabel: 'IHR WEG ZUM PREIS',
|
||||
sub: [
|
||||
{ label: 'Teilnahmevoraussetzungen', path: '/teilnahme#voraussetzungen' },
|
||||
{ label: 'Kriterien & Bewertung', path: '/teilnahme#kriterien' },
|
||||
{ label: 'Fristen & Termine', path: '/teilnahme#fristen' },
|
||||
{ label: 'Bewerbungsformular', path: '/teilnahme#bewerben' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'bmp', label: 'Der BMP', path: '/der-bmp',
|
||||
subLabel: 'DER BMP',
|
||||
sub: [
|
||||
{ label: 'Was ist der Preis?', path: '/der-bmp#was-ist' },
|
||||
{ label: 'Geschichte & Werte', path: '/der-bmp#geschichte' },
|
||||
{ label: 'Vorteile der Auszeichnung', path: '/der-bmp#vorteile' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'netzwerk', label: 'Netzwerk', path: '/netzwerk',
|
||||
subLabel: 'NETZWERK',
|
||||
sub: [
|
||||
{ label: 'Jury-Mitglieder', path: '/netzwerk#jury' },
|
||||
{ label: 'Partner & Sponsoren', path: '/netzwerk#partner' },
|
||||
{ label: 'Schirmherrschaft', path: '/netzwerk#schirmherrschaft' },
|
||||
{ label: 'Mitglied werden', path: '/mitglied-werden' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'presse', label: 'Presse', path: '/presse',
|
||||
subLabel: 'PRESSE',
|
||||
sub: [
|
||||
{ label: 'Events & Gala', path: '/presse#events' },
|
||||
{ label: 'Downloads & Impressionen', path: '/presse#downloads' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const SECONDARY = [
|
||||
{ label:'Startseite', path:'/' },
|
||||
{ label:'Kontakt', path:'/kontakt' },
|
||||
{ label:'Impressum', path:'/impressum' },
|
||||
{ label:'Datenschutz', path:'/datenschutz' },
|
||||
];
|
||||
|
||||
/* ── Tokens ────────────────────────────────────────────── */
|
||||
const NAVY = '#111D55';
|
||||
@@ -90,22 +37,32 @@ const FF_DISPLAY = '"IBM Plex Sans", sans-serif';
|
||||
const FF_BODY = '"Inter", sans-serif';
|
||||
|
||||
/* ── SocialLink helper ─────────────────────────────────── */
|
||||
const SocialLink: React.FC<{ href:string; Icon:()=>React.ReactElement; label:string }> = ({ href, Icon, label }) => (
|
||||
<a href={href} target="_blank" rel="noopener noreferrer" aria-label={label}
|
||||
style={{ color:MUTED, display:'flex', textDecoration:'none', transition:'color .2s' }}
|
||||
onMouseEnter={e=>(e.currentTarget.style.color=GOLD)}
|
||||
onMouseLeave={e=>(e.currentTarget.style.color=MUTED)}
|
||||
><Icon /></a>
|
||||
);
|
||||
const SocialLink: React.FC<{ href:string; icon:SpaIconName; label:string }> = ({ href, icon, label }) => {
|
||||
const Icon = ICONS[icon] || LiIcon;
|
||||
return (
|
||||
<a href={href} target="_blank" rel="noopener noreferrer" aria-label={label}
|
||||
style={{ color:MUTED, display:'flex', textDecoration:'none', transition:'color .2s' }}
|
||||
onMouseEnter={e=>(e.currentTarget.style.color=GOLD)}
|
||||
onMouseLeave={e=>(e.currentTarget.style.color=MUTED)}
|
||||
><Icon /></a>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/* ── Component ──────────────────────────────────────────── */
|
||||
const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
const Layout: React.FC<LayoutProps> = ({ children, header = defaultHeaderData, footer = defaultFooterData }) => {
|
||||
const NAV = header.navItems;
|
||||
const SECONDARY = header.secondaryLinks;
|
||||
const SOCIAL = header.socialLinks;
|
||||
const logo = header.logo?.url || '/bmp-logo.png';
|
||||
const footerLogo = footer.logo?.url || '/bmp-logo.png';
|
||||
const headerCtas = header.ctas;
|
||||
const footerCtas = footer.ctas;
|
||||
const [open, setOpen] = useState(false); // desktop overlay
|
||||
const [activeKey, setActive] = useState<string | null>(null); // desktop hover subnav
|
||||
const [mobileOpen, setMobileOpen] = useState(false); // mobile sidebar overlay
|
||||
const [expandKey, setExpand] = useState<string | null>(null); // mobile accordion
|
||||
const [lang, setLang] = useState<'DE'|'EN'>('DE');
|
||||
const [lang, setLang] = useState(header.languages[0] || 'DE');
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [showLoader, setShowLoader] = useState(() => {
|
||||
if (typeof window === 'undefined') return false;
|
||||
@@ -226,8 +183,8 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
<div style={{ flex:1, display:'flex', alignItems:'center', padding:'0 24px', height:'100%', borderRight:`1px solid ${LINE}` }}>
|
||||
<Link to="/" style={{ display:'flex', alignItems:'center', textDecoration:'none' }}>
|
||||
<img
|
||||
src="/bmp-logo.png"
|
||||
alt="Der Bayerische Mittelstandspreis 2026"
|
||||
src={logo}
|
||||
alt={header.logo?.alt || header.overlayLogoAlt}
|
||||
style={{ height:38, width:'auto', display:'block', objectFit:'contain' }}
|
||||
/>
|
||||
</Link>
|
||||
@@ -239,7 +196,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
<button
|
||||
onClick={() => setOpen(v => !v)}
|
||||
aria-expanded={open}
|
||||
aria-label="Navigation öffnen"
|
||||
aria-label={header.openLabel}
|
||||
style={{ display:'flex', alignItems:'center', gap:14, background:'none', border:'none', cursor:'pointer', padding:'8px 16px' }}
|
||||
>
|
||||
<div style={{ width:20, height:15, position:'relative' }}>
|
||||
@@ -247,7 +204,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
<span style={{ position:'absolute', top:'50%', left:0, width:'100%', height:1.5, background:MUTED, marginTop:'-0.75px', transition:'opacity .3s', opacity: open ? 0 : 1 }} />
|
||||
<span style={{ position:'absolute', bottom:0, left:0, width:'100%', height:1.5, background:MUTED, transformOrigin:'center', transition:'transform .3s ease', transform: open ? 'translateY(-6.5px) rotate(-45deg)' : 'none' }} />
|
||||
</div>
|
||||
<span style={{ fontSize:9, fontWeight:600, textTransform:'uppercase', letterSpacing:'.22em', color:MUTED, lineHeight:1, fontFamily:FF_DISPLAY }}>Menü</span>
|
||||
<span style={{ fontSize:9, fontWeight:600, textTransform:'uppercase', letterSpacing:'.22em', color:MUTED, lineHeight:1, fontFamily:FF_DISPLAY }}>{header.menuLabel}</span>
|
||||
</button>
|
||||
<span style={{ fontSize:8, color:'rgba(5,26,135,0.5)', letterSpacing:2, userSelect:'none' }}>◆</span>
|
||||
</div>
|
||||
@@ -259,7 +216,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
</div>
|
||||
<div style={{ width:1, height:20, background:'rgba(239,191,4,0.3)' }} />
|
||||
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
|
||||
{(['DE','EN'] as const).map((l, i) => (
|
||||
{header.languages.map((l, i) => (
|
||||
<React.Fragment key={l}>
|
||||
{i===1 && <span style={{ color:'rgba(239,191,4,.35)', fontSize: 14 }}>|</span>}
|
||||
<button
|
||||
@@ -292,14 +249,14 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
<div style={{ flex:'1 1 50%', display:'flex', alignItems:'flex-end', padding:'0 80px 48px', position:'relative', minHeight:0, zIndex:1 }}>
|
||||
<div>
|
||||
<div style={{ fontSize:12, color:'rgba(255,255,255,.4)', letterSpacing:'.12em', textTransform:'uppercase', marginBottom:12, fontFamily:FF_DISPLAY }}>
|
||||
Bayerischer Mittelstandspreis
|
||||
{header.overlayKicker}
|
||||
</div>
|
||||
<div style={{ fontSize:'clamp(42px,6.5vw,88px)', fontWeight:900, color:'#fff', textTransform:'uppercase', lineHeight:.95, letterSpacing:'-.01em', fontFamily:FF_DISPLAY }}>
|
||||
EXZELLENZ HAT<br/>EINEN NAMEN.
|
||||
{header.overlayHeadline.split('\n').map((line, index) => <React.Fragment key={line + index}>{index > 0 && <br />}{line}</React.Fragment>)}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ position:'absolute', top:48, right:80 }}>
|
||||
<img src="/bmp-logo.png" alt="BMP Logo" style={{ height:40, width:'auto', display:'block', opacity:0.9 }} />
|
||||
<img src={logo} alt={header.overlayLogoAlt} style={{ height:40, width:'auto', display:'block', opacity:0.9 }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -359,7 +316,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
pointerEvents: activeKey===item.key ? 'all' : 'none',
|
||||
}}>
|
||||
<div style={{ fontSize:11, fontWeight:700, textTransform:'uppercase', letterSpacing:'.22em', color:'#009EE0', marginBottom:16, fontFamily:FF_DISPLAY }}>
|
||||
Überblick
|
||||
{header.overviewLabel}
|
||||
</div>
|
||||
<div style={{ fontSize: 16, fontWeight:700, textTransform:'uppercase', letterSpacing:'.1em', color:'#fff', marginBottom:20, fontFamily:FF_DISPLAY }}>
|
||||
{item.subLabel}
|
||||
@@ -385,7 +342,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
|
||||
<div style={{ display:'flex', alignItems:'center', gap:20, flexWrap:'wrap' }}>
|
||||
<div style={{ display:'flex', alignItems:'center', gap:8 }}>
|
||||
{(['DE','EN'] as const).map((l,i) => (
|
||||
{header.languages.map((l,i) => (
|
||||
<React.Fragment key={l}>
|
||||
{i===1 && <span style={{ color:'rgba(239,191,4,.35)', fontSize: 14 }}>|</span>}
|
||||
<button onClick={() => setLang(l)}
|
||||
@@ -412,20 +369,20 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
</div>
|
||||
|
||||
<div style={{ display:'flex', flexDirection:'column', gap:10 }}>
|
||||
<Link to="/teilnahme#bewerben" onClick={() => setOpen(false)}
|
||||
<Link to={headerCtas[0]?.path || '/teilnahme#bewerben'} onClick={() => setOpen(false)}
|
||||
style={{ display:'inline-block', background:'#EFBF04', color:'#101828', fontSize: 15, fontWeight:700, textTransform:'uppercase', letterSpacing:'.12em', padding:'11px 24px', textDecoration:'none', transition:'background .2s, box-shadow .2s', borderRadius:0, fontFamily:FF_DISPLAY }}
|
||||
onMouseEnter={e=>{ e.currentTarget.style.background='#FFD130'; e.currentTarget.style.boxShadow='0 0 18px rgba(239,191,4,0.65), 0 0 40px rgba(239,191,4,0.3)'; }}
|
||||
onMouseLeave={e=>{ e.currentTarget.style.background='#EFBF04'; e.currentTarget.style.boxShadow='none'; }}
|
||||
>Jetzt bewerben</Link>
|
||||
<Link to="/mitglied-werden" onClick={() => setOpen(false)}
|
||||
>{headerCtas[0]?.label || 'Jetzt bewerben'}</Link>
|
||||
<Link to={headerCtas[1]?.path || '/mitglied-werden'} onClick={() => setOpen(false)}
|
||||
style={{ display:'inline-block', background:'transparent', color:MUTED, fontSize: 15, fontWeight:700, textTransform:'uppercase', letterSpacing:'.12em', padding:'10px 24px', textDecoration:'none', transition:'border-color .2s, color .2s', borderRadius:0, fontFamily:FF_DISPLAY, border:'1.5px solid rgba(239,191,4,0.35)' }}
|
||||
onMouseEnter={e=>{ e.currentTarget.style.borderColor='#EFBF04'; e.currentTarget.style.color='#EFBF04'; }}
|
||||
onMouseLeave={e=>{ e.currentTarget.style.borderColor='rgba(239,191,4,0.35)'; e.currentTarget.style.color=MUTED; }}
|
||||
>Mitglied werden</Link>
|
||||
>{headerCtas[1]?.label || 'Mitglied werden'}</Link>
|
||||
|
||||
<div style={{ height:'1px', background:'rgba(239,191,4,0.12)', margin:'4px 0' }} />
|
||||
|
||||
<Link to="/formular-hochladen" onClick={() => setOpen(false)}
|
||||
<Link to={headerCtas[2]?.path || '/formular-hochladen'} onClick={() => setOpen(false)}
|
||||
style={{
|
||||
display:'flex', alignItems:'center', gap:10,
|
||||
background:'rgba(255,255,255,0.04)',
|
||||
@@ -449,7 +406,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
e.currentTarget.style.background='rgba(255,255,255,0.04)';
|
||||
}}
|
||||
>
|
||||
<Upload size={12} strokeWidth={2} /> Formular hochladen
|
||||
<Upload size={12} strokeWidth={2} /> {headerCtas[2]?.label || 'Formular hochladen'}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -478,14 +435,14 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
}}>
|
||||
{/* Logo */}
|
||||
<Link to="/" style={{ display:'flex', alignItems:'center', textDecoration:'none' }}>
|
||||
<img src="/bmp-logo.png" alt="Bayerischer Mittelstandspreis 2026" style={{ height:32, width:'auto', display:'block', objectFit:'contain' }} />
|
||||
<img src={logo} alt={header.logo?.alt || header.overlayLogoAlt} style={{ height:32, width:'auto', display:'block', objectFit:'contain' }} />
|
||||
</Link>
|
||||
|
||||
{/* Hamburger */}
|
||||
<button
|
||||
onClick={() => setMobileOpen(true)}
|
||||
aria-expanded={mobileOpen}
|
||||
aria-label="Navigation öffnen"
|
||||
aria-label={header.openLabel}
|
||||
style={{ display:'flex', alignItems:'center', justifyContent:'center', width:48, height:48, background:'none', border:'none', cursor:'pointer', padding:0, flexShrink:0 }}
|
||||
>
|
||||
<div style={{ width:24, position:'relative', display:'flex', flexDirection:'column', gap:5 }}>
|
||||
@@ -517,14 +474,14 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
textTransform:'uppercase', letterSpacing:'-.02em',
|
||||
color:'rgba(255,255,255,.04)',
|
||||
pointerEvents:'none', userSelect:'none', zIndex:0,
|
||||
}}>Exzellenz<br/>seit 2005</div>
|
||||
}}>{header.mobileWatermark.split('\n').map((line, index) => <React.Fragment key={line + index}>{index > 0 && <br />}{line}</React.Fragment>)}</div>
|
||||
|
||||
{/* Header row: logo + close */}
|
||||
<div style={{ position:'relative', zIndex:1, display:'flex', alignItems:'center', justifyContent:'space-between', padding:'0 8px 0 20px', height:60, borderBottom:`1px solid ${SEP}`, flexShrink:0 }}>
|
||||
<Link to="/" onClick={() => setMobileOpen(false)} style={{ display:'flex', alignItems:'center', textDecoration:'none' }}>
|
||||
<img src="/bmp-logo.png" alt="BMP Logo" style={{ height:32, width:'auto', display:'block', objectFit:'contain' }} />
|
||||
<img src={logo} alt={header.overlayLogoAlt} style={{ height:32, width:'auto', display:'block', objectFit:'contain' }} />
|
||||
</Link>
|
||||
<button onClick={() => setMobileOpen(false)} aria-label="Menü schließen"
|
||||
<button onClick={() => setMobileOpen(false)} aria-label={header.closeLabel}
|
||||
style={{ display:'flex', alignItems:'center', justifyContent:'center', width:48, height:48, background:'none', border:'none', cursor:'pointer', padding:0, flexShrink:0 }}>
|
||||
<div style={{ width:26, height:26, position:'relative', display:'flex', alignItems:'center', justifyContent:'center' }}>
|
||||
<span style={{ position:'absolute', width:22, height:2, background:MUTED, borderRadius:2, transform:'rotate(45deg)' }} />
|
||||
@@ -589,15 +546,15 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
|
||||
{/* CTAs */}
|
||||
<div style={{ padding:'24px 24px 8px', display:'flex', flexDirection:'column', gap:12 }}>
|
||||
<Link to="/teilnahme#bewerben" onClick={() => setMobileOpen(false)}
|
||||
<Link to={headerCtas[0]?.path || '/teilnahme#bewerben'} onClick={() => setMobileOpen(false)}
|
||||
style={{ display:'flex', alignItems:'center', justifyContent:'center', minHeight:52, background:GOLD, color:'#101828', fontSize:14, fontWeight:700, textTransform:'uppercase', letterSpacing:'.12em', textDecoration:'none', fontFamily:FF_DISPLAY }}
|
||||
>Jetzt bewerben</Link>
|
||||
<Link to="/mitglied-werden" onClick={() => setMobileOpen(false)}
|
||||
>{headerCtas[0]?.label || 'Jetzt bewerben'}</Link>
|
||||
<Link to={headerCtas[1]?.path || '/mitglied-werden'} onClick={() => setMobileOpen(false)}
|
||||
style={{ display:'flex', alignItems:'center', justifyContent:'center', minHeight:50, background:'transparent', color:'#fff', fontSize:14, fontWeight:700, textTransform:'uppercase', letterSpacing:'.12em', textDecoration:'none', fontFamily:FF_DISPLAY, border:`1.5px solid ${LINE}` }}
|
||||
>Mitglied werden</Link>
|
||||
<Link to="/formular-hochladen" onClick={() => setMobileOpen(false)}
|
||||
>{headerCtas[1]?.label || 'Mitglied werden'}</Link>
|
||||
<Link to={headerCtas[2]?.path || '/formular-hochladen'} onClick={() => setMobileOpen(false)}
|
||||
style={{ display:'flex', alignItems:'center', justifyContent:'center', gap:8, minHeight:46, background:'none', color:'rgba(255,255,255,0.5)', fontSize:11, fontWeight:700, textTransform:'uppercase', letterSpacing:'.14em', textDecoration:'none', fontFamily:FF_DISPLAY, border:'1px dashed rgba(255,255,255,0.2)' }}
|
||||
><Upload size={13} strokeWidth={2} /> Formular hochladen</Link>
|
||||
><Upload size={13} strokeWidth={2} /> {headerCtas[2]?.label || 'Formular hochladen'}</Link>
|
||||
</div>
|
||||
|
||||
{/* Footer zone: lang + social + secondary */}
|
||||
@@ -605,7 +562,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
{/* Lang + social row */}
|
||||
<div style={{ display:'flex', alignItems:'center', justifyContent:'space-between' }}>
|
||||
<div style={{ display:'flex', alignItems:'center', gap:10 }}>
|
||||
{(['DE','EN'] as const).map((l, i) => (
|
||||
{header.languages.map((l, i) => (
|
||||
<React.Fragment key={l}>
|
||||
{i === 1 && <span style={{ color:LINE, fontSize:13 }}>|</span>}
|
||||
<button onClick={() => setLang(l)}
|
||||
@@ -652,7 +609,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = '#EFBF04'; (e.currentTarget as HTMLElement).style.color = '#101828'; }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = '#111D55'; (e.currentTarget as HTMLElement).style.color = '#fff'; }}
|
||||
>
|
||||
Jetzt bewerben <ArrowRight size={13} />
|
||||
{headerCtas[0]?.label || 'Jetzt bewerben'} <ArrowRight size={13} />
|
||||
</Link>
|
||||
)}
|
||||
|
||||
@@ -693,7 +650,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
lineHeight: 1,
|
||||
whiteSpace: isMobile ? 'normal' : 'nowrap',
|
||||
}}>
|
||||
Der Preis des <span style={{ color: GOLD }}>Bayerischen</span> Mittelstands
|
||||
{footer.headlinePrefix} <span style={{ color: GOLD }}>{footer.headlineAccent}</span> {footer.headlineSuffix}
|
||||
</h2>
|
||||
|
||||
{/* Gold rule beneath headline */}
|
||||
@@ -711,7 +668,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.35em',
|
||||
color: 'rgba(239,191,4,0.55)',
|
||||
}}>Exzellenz seit 2005</span>
|
||||
}}>{footer.sinceLabel}</span>
|
||||
<div style={{ flex: 1, height: 1, background: 'rgba(239,191,4,0.18)' }} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -741,7 +698,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
userSelect: 'none',
|
||||
pointerEvents: 'none',
|
||||
zIndex: 0,
|
||||
}}>2026</div>
|
||||
}}>{footer.year}</div>
|
||||
)}
|
||||
|
||||
{/* ── LEFT COLUMN: copy + CTAs ── */}
|
||||
@@ -753,8 +710,8 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
}}>
|
||||
{/* Logo */}
|
||||
<img
|
||||
src="/bmp-logo.png"
|
||||
alt="Bayerischer Mittelstandspreis"
|
||||
src={footerLogo}
|
||||
alt={footer.logo?.alt || footer.logoAlt}
|
||||
style={{ width: 140, height: 'auto', marginBottom: 10, opacity: 0.9 }}
|
||||
/>
|
||||
|
||||
@@ -766,7 +723,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
margin: '0 0 16px 0',
|
||||
maxWidth: 300,
|
||||
}}>
|
||||
Die renommierteste Auszeichnung für herausragende Unternehmen des bayerischen Mittelstands.
|
||||
{footer.description}
|
||||
</p>
|
||||
|
||||
{/* CTA stack */}
|
||||
@@ -777,7 +734,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
gap: 8,
|
||||
}}>
|
||||
<Link
|
||||
to="/teilnahme#bewerben"
|
||||
to={footerCtas[0]?.path || '/teilnahme#bewerben'}
|
||||
style={{
|
||||
fontFamily: FF_DISPLAY,
|
||||
fontSize: 16,
|
||||
@@ -800,10 +757,10 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
e.currentTarget.style.background = GOLD;
|
||||
e.currentTarget.style.boxShadow = 'none';
|
||||
}}
|
||||
>Kostenlos bewerben</Link>
|
||||
>{footerCtas[0]?.label || 'Kostenlos bewerben'}</Link>
|
||||
|
||||
<Link
|
||||
to="/mitglied-werden"
|
||||
to={footerCtas[1]?.path || '/mitglied-werden'}
|
||||
style={{
|
||||
fontFamily: FF_DISPLAY,
|
||||
fontSize: 16,
|
||||
@@ -826,10 +783,10 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
e.currentTarget.style.borderColor = 'rgba(239,191,4,0.35)';
|
||||
e.currentTarget.style.color = MUTED;
|
||||
}}
|
||||
>Mitglied werden</Link>
|
||||
>{footerCtas[1]?.label || 'Mitglied werden'}</Link>
|
||||
|
||||
<Link
|
||||
to="/netzwerk#sponsoring"
|
||||
to={footerCtas[2]?.path || '/netzwerk#sponsoring'}
|
||||
style={{
|
||||
fontFamily: FF_DISPLAY,
|
||||
fontSize: 16,
|
||||
@@ -843,7 +800,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
}}
|
||||
onMouseEnter={e => { e.currentTarget.style.color = '#fff'; }}
|
||||
onMouseLeave={e => { e.currentTarget.style.color = 'rgba(255,255,255,0.4)'; }}
|
||||
>Sponsoring-Anfrage →</Link>
|
||||
>{footerCtas[2]?.label || 'Sponsoring-Anfrage →'}</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -868,135 +825,50 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
alignItems: 'flex-start',
|
||||
}}>
|
||||
|
||||
{/* BMP 2026 column */}
|
||||
<div style={{ minWidth: isMobile ? 'auto' : 160 }}>
|
||||
<p style={{
|
||||
fontFamily: FF_DISPLAY,
|
||||
fontSize: 9,
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.28em',
|
||||
color: 'rgba(255,255,255,0.3)',
|
||||
margin: '0 0 10px 0',
|
||||
}}>BMP 2026</p>
|
||||
<ul style={{ listStyle: 'none', margin: 0, padding: 0, display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
{[
|
||||
{ l: 'Startseite', p: '/' },
|
||||
{ l: 'Preisträger', p: '/preistraeger' },
|
||||
{ l: 'Teilnahme', p: '/teilnahme' },
|
||||
].map(item => (
|
||||
<li key={item.p}>
|
||||
<Link
|
||||
to={item.p}
|
||||
style={{
|
||||
fontFamily: FF_BODY,
|
||||
fontSize: 16,
|
||||
color: 'rgba(255,255,255,0.55)',
|
||||
textDecoration: 'none',
|
||||
transition: 'color 0.2s',
|
||||
display: 'inline-block',
|
||||
}}
|
||||
onMouseEnter={e => { e.currentTarget.style.color = '#fff'; }}
|
||||
onMouseLeave={e => { e.currentTarget.style.color = 'rgba(255,255,255,0.55)'; }}
|
||||
>{item.l}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Gold vertical separator – desktop only */}
|
||||
{!isMobile && (
|
||||
<div style={{
|
||||
width: 1,
|
||||
alignSelf: 'stretch',
|
||||
background: 'rgba(239,191,4,0.22)',
|
||||
margin: '0 40px',
|
||||
flexShrink: 0,
|
||||
}} />
|
||||
)}
|
||||
|
||||
{/* Engagement column */}
|
||||
<div style={{ minWidth: isMobile ? 'auto' : 160 }}>
|
||||
<p style={{
|
||||
fontFamily: FF_DISPLAY,
|
||||
fontSize: 9,
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.28em',
|
||||
color: 'rgba(255,255,255,0.3)',
|
||||
margin: '0 0 10px 0',
|
||||
}}>Engagement</p>
|
||||
<ul style={{ listStyle: 'none', margin: 0, padding: 0, display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
{[
|
||||
{ l: 'Netzwerk & Jury', p: '/netzwerk' },
|
||||
{ l: 'Presse', p: '/presse' },
|
||||
{ l: 'Kontakt', p: '/kontakt' },
|
||||
].map(item => (
|
||||
<li key={item.p}>
|
||||
<Link
|
||||
to={item.p}
|
||||
style={{
|
||||
fontFamily: FF_BODY,
|
||||
fontSize: 16,
|
||||
color: 'rgba(255,255,255,0.55)',
|
||||
textDecoration: 'none',
|
||||
transition: 'color 0.2s',
|
||||
display: 'inline-block',
|
||||
}}
|
||||
onMouseEnter={e => { e.currentTarget.style.color = '#fff'; }}
|
||||
onMouseLeave={e => { e.currentTarget.style.color = 'rgba(255,255,255,0.55)'; }}
|
||||
>{item.l}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Gold vertical separator – desktop only */}
|
||||
{!isMobile && (
|
||||
<div style={{
|
||||
width: 1,
|
||||
alignSelf: 'stretch',
|
||||
background: 'rgba(239,191,4,0.22)',
|
||||
margin: '0 40px',
|
||||
flexShrink: 0,
|
||||
}} />
|
||||
)}
|
||||
|
||||
{/* Legal column */}
|
||||
<div style={{ minWidth: isMobile ? 'auto' : 120 }}>
|
||||
<p style={{
|
||||
fontFamily: FF_DISPLAY,
|
||||
fontSize: 9,
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.28em',
|
||||
color: 'rgba(255,255,255,0.3)',
|
||||
margin: '0 0 10px 0',
|
||||
}}>Legal</p>
|
||||
<ul style={{ listStyle: 'none', margin: 0, padding: 0, display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
{[
|
||||
{ l: 'Datenschutz', p: '/datenschutz' },
|
||||
{ l: 'Impressum', p: '/impressum' },
|
||||
].map(item => (
|
||||
<li key={item.p}>
|
||||
<Link
|
||||
to={item.p}
|
||||
style={{
|
||||
fontFamily: FF_BODY,
|
||||
fontSize: 16,
|
||||
color: 'rgba(255,255,255,0.55)',
|
||||
textDecoration: 'none',
|
||||
transition: 'color 0.2s',
|
||||
display: 'inline-block',
|
||||
}}
|
||||
onMouseEnter={e => { e.currentTarget.style.color = '#fff'; }}
|
||||
onMouseLeave={e => { e.currentTarget.style.color = 'rgba(255,255,255,0.55)'; }}
|
||||
>{item.l}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{footer.linkColumns.map((column, index) => (
|
||||
<React.Fragment key={column.label + index}>
|
||||
<div style={{ minWidth: isMobile ? 'auto' : index === footer.linkColumns.length - 1 ? 120 : 160 }}>
|
||||
<p style={{
|
||||
fontFamily: FF_DISPLAY,
|
||||
fontSize: 9,
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.28em',
|
||||
color: 'rgba(255,255,255,0.3)',
|
||||
margin: '0 0 10px 0',
|
||||
}}>{column.label}</p>
|
||||
<ul style={{ listStyle: 'none', margin: 0, padding: 0, display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
{column.links.map(item => (
|
||||
<li key={item.path + item.label}>
|
||||
<Link
|
||||
to={item.path}
|
||||
style={{
|
||||
fontFamily: FF_BODY,
|
||||
fontSize: 16,
|
||||
color: 'rgba(255,255,255,0.55)',
|
||||
textDecoration: 'none',
|
||||
transition: 'color 0.2s',
|
||||
display: 'inline-block',
|
||||
}}
|
||||
onMouseEnter={e => { e.currentTarget.style.color = '#fff'; }}
|
||||
onMouseLeave={e => { e.currentTarget.style.color = 'rgba(255,255,255,0.55)'; }}
|
||||
>{item.label}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{!isMobile && index < footer.linkColumns.length - 1 && (
|
||||
<div style={{
|
||||
width: 1,
|
||||
alignSelf: 'stretch',
|
||||
background: 'rgba(239,191,4,0.22)',
|
||||
margin: '0 40px',
|
||||
flexShrink: 0,
|
||||
}} />
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1019,18 +891,18 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
letterSpacing: '0.32em',
|
||||
color: 'rgba(255,255,255,0.22)',
|
||||
flexShrink: 0,
|
||||
}}>Partner</span>
|
||||
}}>{footer.partnerLabel}</span>
|
||||
<div style={{ width: 1, height: 20, background: 'rgba(239,191,4,0.2)', flexShrink: 0 }} />
|
||||
<img
|
||||
src="/images/ewif-logo.png"
|
||||
alt="EWIF – Europäisches Wirtschaftsforum"
|
||||
style={{ height: 32, width: 'auto', opacity: 0.5, filter: 'brightness(2)', display: 'block' }}
|
||||
/>
|
||||
<img
|
||||
src="/images/partner-logos.png"
|
||||
alt="Hochschule für angewandtes Management · Gipfeldialog Altaussee"
|
||||
style={{ height: 32, width: 'auto', opacity: 0.5, filter: 'brightness(2)', display: 'block' }}
|
||||
/>
|
||||
{footer.partnerLogos.map((partner, index) => (
|
||||
partner.image?.url ? (
|
||||
<img
|
||||
key={`${partner.alt}-${index}`}
|
||||
src={partner.image.url}
|
||||
alt={partner.image.alt || partner.alt}
|
||||
style={{ height: 32, width: 'auto', opacity: 0.5, filter: 'brightness(2)', display: 'block' }}
|
||||
/>
|
||||
) : null
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* ── BOTTOM BAR ── */}
|
||||
@@ -1051,7 +923,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
color: 'rgba(255,255,255,0.3)',
|
||||
margin: 0,
|
||||
}}>
|
||||
© 2026 Bayerischer Mittelstandspreis · Alle Rechte vorbehalten
|
||||
{footer.copyright}
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: 20, alignItems: 'center' }}>
|
||||
{SOCIAL.map(s => <SocialLink key={s.label} {...s} />)}
|
||||
|
||||
Reference in New Issue
Block a user