432 lines
25 KiB
PHP
432 lines
25 KiB
PHP
<?php
|
|
/**
|
|
* Header Template
|
|
*
|
|
* Variables (set by each page):
|
|
* $pageTitle - Page title
|
|
* $pageDescription - Meta description
|
|
* $currentLang - ISO 639-1 code (e.g. 'de', 'en')
|
|
* $currentPage - Current page slug (for navigation highlighting)
|
|
* $basePath - Relative path to root (e.g. '' or '../')
|
|
*
|
|
* Configuration (from config.php):
|
|
* SITE_LANG_DEFAULT - Primary language code (pages at root)
|
|
* $SITE_LANGUAGES - Array of lang code => native name
|
|
* $NAV_ITEMS - (optional) Array of lang => [nav items]
|
|
* $PAGE_MAPPING - (optional) Array of page => [lang => path]
|
|
*/
|
|
|
|
require_once __DIR__ . '/access-guard.php';
|
|
require_once __DIR__ . '/page-path.php';
|
|
if (isset($_GET['rj_agent_preview']) && is_file(__DIR__ . '/ai/agent/preview.php')) {
|
|
require_once __DIR__ . '/ai/agent/preview.php'; // owner-only preview of an AI change set
|
|
raijuAgentPreviewBoot();
|
|
}
|
|
raijuAccessEnforceMaintenance();
|
|
if (!empty($contentPage)) {
|
|
raijuAccessEnforceCurrentTemplatePage((string)$contentPage);
|
|
}
|
|
|
|
// Auto-load site config if not already loaded
|
|
$_configPath = __DIR__ . '/../admin/config.php';
|
|
if (!defined('SITE_LANG_DEFAULT') && file_exists($_configPath)) {
|
|
require_once $_configPath;
|
|
}
|
|
|
|
$basePath = $basePath ?? '';
|
|
$currentLang = $currentLang ?? (defined('SITE_LANG_DEFAULT') ? SITE_LANG_DEFAULT : 'en');
|
|
$isHomepage = ($currentPage ?? '') === 'home';
|
|
$defaultLang = defined('SITE_LANG_DEFAULT') ? SITE_LANG_DEFAULT : 'en';
|
|
|
|
// ============================================================
|
|
// PAGE MAPPING & NAVIGATION
|
|
// ============================================================
|
|
// Load shared nav config (also sets $SITE_LANGUAGES fallback).
|
|
// Customize includes/nav-config.php for your site's pages.
|
|
|
|
if (!isset($PAGE_MAPPING) || !isset($NAV_ITEMS)) {
|
|
$_navConfigPath = __DIR__ . '/nav-config.php';
|
|
if (!file_exists($_navConfigPath)) {
|
|
$_navConfigPath = __DIR__ . '/nav-config.default.php';
|
|
}
|
|
if (file_exists($_navConfigPath)) {
|
|
include_once $_navConfigPath;
|
|
}
|
|
}
|
|
|
|
// Final fallback if $SITE_LANGUAGES still not set
|
|
if (!isset($SITE_LANGUAGES)) {
|
|
$SITE_LANGUAGES = [$defaultLang => $defaultLang];
|
|
}
|
|
|
|
// Fallback if still not set
|
|
if (!isset($PAGE_MAPPING)) {
|
|
$PAGE_MAPPING = ['home' => []];
|
|
foreach ($SITE_LANGUAGES as $code => $name) {
|
|
$PAGE_MAPPING['home'][$code] = ($code === $defaultLang) ? '.' : $code . '/';
|
|
}
|
|
}
|
|
|
|
// Determine links for language switching
|
|
$currentPageKey = $currentPage ?? 'home';
|
|
$langLinks = [];
|
|
$_contentPath = __DIR__ . '/../content/pages/';
|
|
foreach ($SITE_LANGUAGES as $code => $name) {
|
|
if (isset($PAGE_MAPPING[$currentPageKey][$code])) {
|
|
$langLinks[$code] = $basePath . $PAGE_MAPPING[$currentPageKey][$code];
|
|
} elseif ($currentPageKey !== 'home' && is_file($_contentPath . raijuPageContentKey($code, $currentPageKey) . '.json')) {
|
|
// Dynamic fallback: same slug exists in target language
|
|
$langLinks[$code] = $basePath . ltrim(raijuPageUrlPath($code, $currentPageKey, $defaultLang), '/');
|
|
} else {
|
|
// Final fallback: home page of that language
|
|
$langLinks[$code] = $basePath . (($code === $defaultLang) ? '.' : $code . '/');
|
|
}
|
|
}
|
|
|
|
// Fallback for $NAV_ITEMS
|
|
if (!isset($NAV_ITEMS)) {
|
|
$NAV_ITEMS = [];
|
|
foreach ($SITE_LANGUAGES as $code => $name) {
|
|
$NAV_ITEMS[$code] = [
|
|
['href' => ($code === $defaultLang) ? '.' : $code . '/', 'label' => 'Home', 'page' => 'home'],
|
|
];
|
|
}
|
|
}
|
|
|
|
require_once __DIR__ . '/menu-helpers.php';
|
|
require_once __DIR__ . '/asset-helpers.php';
|
|
require_once __DIR__ . '/version.php';
|
|
require_once __DIR__ . '/seo-helper.php';
|
|
require_once __DIR__ . '/analytics-helper.php';
|
|
|
|
$_raijuDevLogin = session_status() === PHP_SESSION_ACTIVE && !empty($_SESSION['raiju_dev_login']);
|
|
$_allNavItems = $NAV_ITEMS[$currentLang] ?? $NAV_ITEMS[$defaultLang] ?? [];
|
|
$navItems = getMenuItems('header', $currentLang, $basePath ?? '', $_allNavItems);
|
|
$_headerMenuType = getMenuType('header');
|
|
$_homeHref = ($currentLang === $defaultLang) ? '.' : $currentLang . '/';
|
|
$_buildNavHref = function (array $item) use ($basePath, $isHomepage, $_headerMenuType, $_homeHref) {
|
|
$href = (string)($item['href'] ?? '#');
|
|
if ($_headerMenuType === 'one-page' && str_starts_with($href, '#') && $href !== '#') {
|
|
return ($isHomepage ? '' : $basePath . $_homeHref) . $href;
|
|
}
|
|
return $basePath . $href;
|
|
};
|
|
$_navLinkAttrs = function (array $item, bool $isActive) use ($_headerMenuType) {
|
|
$href = (string)($item['href'] ?? '');
|
|
$classes = [];
|
|
if ($isActive) $classes[] = 'active';
|
|
$attrs = '';
|
|
if ($_headerMenuType === 'one-page' && str_starts_with($href, '#') && $href !== '#') {
|
|
$attrs .= ' data-nav-hash="' . htmlspecialchars(substr($href, 1), ENT_QUOTES, 'UTF-8') . '"';
|
|
}
|
|
if ($classes) {
|
|
$attrs .= ' class="' . htmlspecialchars(implode(' ', $classes), ENT_QUOTES, 'UTF-8') . '"';
|
|
}
|
|
return $attrs;
|
|
};
|
|
|
|
// Load site settings (used for favicon, theme colors, editor button style)
|
|
$_settingsPath = __DIR__ . '/../content/settings.json';
|
|
$_settings = [];
|
|
$_favicon = ltrim(defined('RAIJU_DEFAULT_FAVICON') ? RAIJU_DEFAULT_FAVICON : '/assets/images/favicon.svg', '/');
|
|
$_faviconPng = '';
|
|
if (file_exists($_settingsPath)) {
|
|
$_settings = json_decode(file_get_contents($_settingsPath), true) ?: [];
|
|
if (!empty($_settings['favicon'])) $_favicon = ltrim($_settings['favicon'], '/');
|
|
if (!empty($_settings['favicon_png'])) $_faviconPng = ltrim($_settings['favicon_png'], '/');
|
|
}
|
|
$_rememberPublicTheme = !isset($_settings['privacy']['rememberPublicTheme']) || !empty($_settings['privacy']['rememberPublicTheme']);
|
|
$_publicThemeDefault = $_settings['theme']['publicDefault'] ?? 'system';
|
|
if (!in_array($_publicThemeDefault, ['light', 'dark', 'system'], true)) {
|
|
$_publicThemeDefault = 'system';
|
|
}
|
|
$_editorFlat = isset($_settings['theme']['buttonGlow']) && !$_settings['theme']['buttonGlow'];
|
|
$_seoContext = raijuSeoContext([
|
|
'contentPage' => $contentPage ?? null,
|
|
'currentLang' => $currentLang,
|
|
'currentPage' => $currentPage ?? '',
|
|
'pageTitle' => $pageTitle ?? '',
|
|
'pageDescription' => $pageDescription ?? '',
|
|
'data' => isset($data) && is_array($data) ? $data : null,
|
|
]);
|
|
$_seoHealth = raijuSeoHealth($_seoContext);
|
|
// Site-wide search engine block: also as a header, so files and proxies agree with the meta tag.
|
|
if (raijuSeoSiteBlocked() && !headers_sent()) header('X-Robots-Tag: ' . $_seoContext['robots']);
|
|
$_seoJsonLd = raijuSeoJsonLd($_seoContext, $langLinks);
|
|
raijuAnalyticsTrack($contentPage ?? null, $currentLang, $currentPage ?? null);
|
|
|
|
require_once __DIR__ . '/email-obfuscator.php';
|
|
raijuStartEmailObfuscation();
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="<?php echo htmlspecialchars($currentLang); ?>"<?php if ($_editorFlat) echo ' class="editor-flat"'; ?>>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="description" content="<?php echo htmlspecialchars($_seoContext['description']); ?>">
|
|
<meta name="robots" content="<?php echo htmlspecialchars($_seoContext['robots']); ?>">
|
|
<meta name="generator" content="raiju <?php echo htmlspecialchars(raijuVersion(), ENT_QUOTES, 'UTF-8'); ?>">
|
|
<?php if (!empty($_seoContext['canonical'])): ?>
|
|
<link rel="canonical" href="<?php echo htmlspecialchars($_seoContext['canonical']); ?>">
|
|
<?php endif; ?>
|
|
<?php foreach ($SITE_LANGUAGES as $_altCode => $_altName): ?>
|
|
<?php if (isset($langLinks[$_altCode])): ?>
|
|
<link rel="alternate" hreflang="<?php echo htmlspecialchars($_altCode); ?>" href="<?php echo htmlspecialchars(raijuSeoPageUrl($_altCode, $currentPageKey)); ?>">
|
|
<?php endif; ?>
|
|
<?php endforeach; ?>
|
|
<?php if (isset($langLinks[$defaultLang])): ?>
|
|
<link rel="alternate" hreflang="x-default" href="<?php echo htmlspecialchars(raijuSeoPageUrl($defaultLang, $currentPageKey)); ?>">
|
|
<?php endif; ?>
|
|
<?php $_faviconType = pathinfo($_favicon, PATHINFO_EXTENSION) === 'svg' ? 'image/svg+xml' : 'image/png'; ?>
|
|
<link rel="icon" href="<?php echo $basePath . htmlspecialchars($_favicon); ?>" type="<?php echo $_faviconType; ?>">
|
|
<?php if ($_faviconPng): ?>
|
|
<link rel="alternate icon" href="<?php echo $basePath . htmlspecialchars($_faviconPng); ?>" type="image/png">
|
|
<link rel="apple-touch-icon" href="<?php echo $basePath . htmlspecialchars($_faviconPng); ?>">
|
|
<?php else: ?>
|
|
<link rel="apple-touch-icon" href="<?php echo $basePath . htmlspecialchars($_favicon); ?>">
|
|
<?php endif; ?>
|
|
|
|
<!-- Optional: uncomment to load custom fonts -->
|
|
<!-- <link rel="stylesheet" href="<?php echo $basePath; ?>css/fonts.css"> -->
|
|
|
|
<!-- Open Graph -->
|
|
<meta property="og:title" content="<?php echo htmlspecialchars($_seoContext['ogTitle']); ?>">
|
|
<meta property="og:description" content="<?php echo htmlspecialchars($_seoContext['ogDescription']); ?>">
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:locale" content="<?php echo htmlspecialchars($currentLang); ?>">
|
|
<meta property="og:url" content="<?php echo htmlspecialchars($_seoContext['canonical'] ?: raijuSeoCurrentUrl()); ?>">
|
|
<?php if (!empty($_seoContext['ogImage'])): ?>
|
|
<meta property="og:image" content="<?php echo htmlspecialchars($_seoContext['ogImage']); ?>">
|
|
<?php if (!empty($_seoContext['ogImageWidth'])): ?>
|
|
<meta property="og:image:width" content="<?php echo htmlspecialchars((string)$_seoContext['ogImageWidth']); ?>">
|
|
<?php endif; ?>
|
|
<?php if (!empty($_seoContext['ogImageHeight'])): ?>
|
|
<meta property="og:image:height" content="<?php echo htmlspecialchars((string)$_seoContext['ogImageHeight']); ?>">
|
|
<?php endif; ?>
|
|
<?php if (!empty($_seoContext['ogImageAlt'])): ?>
|
|
<meta property="og:image:alt" content="<?php echo htmlspecialchars($_seoContext['ogImageAlt']); ?>">
|
|
<?php endif; ?>
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:image" content="<?php echo htmlspecialchars($_seoContext['ogImage']); ?>">
|
|
<?php else: ?>
|
|
<meta name="twitter:card" content="summary">
|
|
<?php endif; ?>
|
|
<meta name="twitter:title" content="<?php echo htmlspecialchars($_seoContext['ogTitle']); ?>">
|
|
<meta name="twitter:description" content="<?php echo htmlspecialchars($_seoContext['ogDescription']); ?>">
|
|
|
|
<title><?php echo htmlspecialchars($_seoContext['title'] ?: 'Website'); ?></title>
|
|
<script type="application/ld+json"><?php echo json_encode($_seoJsonLd, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP); ?></script>
|
|
|
|
<?php echo raijuCoreStyles($basePath); ?>
|
|
<?php if (file_exists(__DIR__ . '/../css/website.css')): ?>
|
|
<link rel="stylesheet" href="<?php echo $basePath; ?>css/website.css">
|
|
<?php endif; ?>
|
|
<?php if (file_exists(__DIR__ . '/../css/fonts.css') && file_exists(__DIR__ . '/../assets/fonts/')): ?>
|
|
<link rel="stylesheet" href="<?php echo $basePath; ?>css/fonts.css">
|
|
<?php endif; ?>
|
|
<?php if (!empty($pageExternalStyles)): ?>
|
|
<?php foreach ($pageExternalStyles as $_extStyle): ?>
|
|
<link rel="stylesheet" href="<?php echo htmlspecialchars($_extStyle); ?>">
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
<?php if (isset($pageStylesheet) && file_exists(__DIR__ . '/../' . $pageStylesheet)): ?>
|
|
<link rel="stylesheet" href="<?php echo $basePath . htmlspecialchars($pageStylesheet); ?>">
|
|
<?php endif; ?>
|
|
|
|
<?php
|
|
// Build editor theme variables from settings (injected in footer.php AFTER inline-editor.css)
|
|
$_editorVars = [];
|
|
if (!empty($_settings['theme']['primaryColor'])) {
|
|
$pc = $_settings['theme']['primaryColor'];
|
|
$_editorVars[] = '--editor-primary:' . htmlspecialchars($pc);
|
|
$r = hexdec(substr($pc, 1, 2)); $g = hexdec(substr($pc, 3, 2)); $b = hexdec(substr($pc, 5, 2));
|
|
$pcHover = '#' . sprintf('%02x%02x%02x', max(0,$r-15), max(0,$g-15), max(0,$b-15));
|
|
$pcLight = '#' . sprintf('%02x%02x%02x', min(255,$r+30), min(255,$g+30), min(255,$b+30));
|
|
$pcLightHover = '#' . sprintf('%02x%02x%02x', min(255,$r+50), min(255,$g+50), min(255,$b+50));
|
|
$_editorVars[] = '--editor-primary-hover:' . $pcHover;
|
|
if (!empty($_settings['theme']['accentColor'])) {
|
|
$_editorVars[] = '--editor-primary-light:' . htmlspecialchars($_settings['theme']['accentColor']);
|
|
} else {
|
|
$_editorVars[] = '--editor-primary-light:#' . sprintf('%02x%02x%02x', min(255,$r+40), min(255,$g+40), min(255,$b+40));
|
|
}
|
|
// Button background: gradient (glow) or flat color
|
|
$buttonGlow = $_settings['theme']['buttonGlow'] ?? true;
|
|
if ($buttonGlow) {
|
|
$_editorVars[] = '--editor-btn-bg:radial-gradient(ellipse at 50% 0%, ' . $pcLight . ' 0%, ' . htmlspecialchars($pc) . ' 70%)';
|
|
$_editorVars[] = '--editor-btn-bg-hover:radial-gradient(ellipse at 50% 0%, ' . $pcLightHover . ' 0%, ' . htmlspecialchars($pc) . ' 70%)';
|
|
} else {
|
|
$_editorVars[] = '--editor-btn-bg:' . htmlspecialchars($pc);
|
|
$_editorVars[] = '--editor-btn-bg-hover:' . $pcHover;
|
|
}
|
|
}
|
|
if (isset($_settings['theme']['buttonRadius'])) {
|
|
$_editorVars[] = '--editor-btn-radius:' . intval($_settings['theme']['buttonRadius']) . 'px';
|
|
}
|
|
?>
|
|
|
|
<!-- Prevent FOUC: apply public theme preference before paint -->
|
|
<script>
|
|
(function(){var remember=<?php echo $_rememberPublicTheme ? 'true' : 'false'; ?>;var fallback=<?php echo json_encode($_publicThemeDefault); ?>;try{var t=remember?localStorage.getItem('site-theme'):null;if(t==='system')t=null;if(t!=='dark'&&t!=='light')t=(fallback==='dark'||fallback==='light')?fallback:(window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light');document.documentElement.setAttribute('data-theme',t);}catch(e){document.documentElement.setAttribute('data-theme',(fallback==='dark'||fallback==='light')?fallback:'light');}})();
|
|
</script>
|
|
</head>
|
|
<body class="<?php echo $isHomepage ? 'page-home' : 'page-subpage'; ?><?php echo isset($pageClass) ? ' ' . $pageClass : ''; ?><?php echo $_raijuDevLogin ? ' has-dev-login' : ''; ?>"<?php echo $_raijuDevLogin ? ' data-dev-login="true"' : ''; ?>>
|
|
<?php if (!empty($GLOBALS['raijuAgentPreview']['active'])) echo raijuAgentPreviewBanner(); ?>
|
|
<a class="skip-link" href="#main-content">Skip to main content</a>
|
|
|
|
<!-- Fixed Header -->
|
|
<header class="site-header" id="siteHeader">
|
|
<div class="header-inner">
|
|
<!-- Logo -->
|
|
<?php
|
|
$_headerLogo = $_settings['logo'] ?? $_settings['branding']['logo'] ?? '';
|
|
$_headerLogo = ltrim($_headerLogo, '/');
|
|
$_headerLogoDark = ltrim($_settings['branding']['logoDark'] ?? '', '/');
|
|
// Treat a logo that points at the favicon as "no separate logo set"
|
|
if ($_headerLogo === $_favicon) $_headerLogo = '';
|
|
$_siteName = $_settings['branding']['name'] ?? (defined('SITE_NAME') ? SITE_NAME : '');
|
|
$_logoDisplay = $_settings['branding']['logoDisplay'] ?? 'both';
|
|
$_logoSize = $_settings['branding']['logoSize'] ?? 'medium';
|
|
if (!in_array($_logoSize, ['small', 'medium', 'large'], true)) $_logoSize = 'medium';
|
|
$_showFavicon = !$_headerLogo && $_logoDisplay !== 'text';
|
|
$_showText = $_siteName && !$_headerLogo && $_logoDisplay !== 'favicon';
|
|
?>
|
|
<a href="<?php echo $basePath; ?>." class="site-logo site-logo--size-<?php echo htmlspecialchars($_logoSize); ?>" aria-label="Home">
|
|
<?php
|
|
if ($_headerLogo):
|
|
$_headerLogoDarkSrc = $_headerLogoDark ?: $_headerLogo;
|
|
$_hasDarkLogo = ($_headerLogoDark && $_headerLogoDark !== $_headerLogo);
|
|
?>
|
|
<img class="site-logo-img site-logo-img--light" src="<?php echo $basePath . htmlspecialchars($_headerLogo); ?>" alt="<?php echo htmlspecialchars($_siteName); ?>">
|
|
<?php if ($_hasDarkLogo): ?>
|
|
<img class="site-logo-img site-logo-img--dark" src="<?php echo $basePath . htmlspecialchars($_headerLogoDarkSrc); ?>" alt="<?php echo htmlspecialchars($_siteName); ?>" aria-hidden="true">
|
|
<?php endif; ?>
|
|
<?php elseif ($_showFavicon): ?>
|
|
<?php echo raijuIconOrImg($basePath . $_favicon, $_siteName, ['width' => 32, 'height' => 32, 'class' => 'site-logo-img site-logo-icon']); ?>
|
|
<?php endif; ?>
|
|
<?php if ($_showText): ?>
|
|
<span class="site-logo-text" title="<?php echo htmlspecialchars($_siteName); ?>"><?php echo htmlspecialchars($_siteName); ?></span>
|
|
<?php endif; ?>
|
|
</a>
|
|
|
|
<!-- Desktop Navigation -->
|
|
<nav class="nav-main" aria-label="Primary navigation">
|
|
<ul class="nav-list">
|
|
<?php foreach ($navItems as $item):
|
|
$hasChildren = !empty($item['children']);
|
|
$isHashItem = $_headerMenuType === 'one-page' && str_starts_with((string)($item['href'] ?? ''), '#');
|
|
$isActive = !$isHashItem && (($currentPage ?? '') === ($item['page'] ?? ''));
|
|
// Parent is active if any child matches
|
|
if ($hasChildren && !$isActive) {
|
|
foreach ($item['children'] as $_child) {
|
|
$_childHash = $_headerMenuType === 'one-page' && str_starts_with((string)($_child['href'] ?? ''), '#');
|
|
if (!$_childHash && ($currentPage ?? '') === ($_child['page'] ?? '')) { $isActive = true; break; }
|
|
}
|
|
}
|
|
?>
|
|
<li<?php echo $hasChildren ? ' class="nav-item--has-children"' : ''; ?>>
|
|
<a href="<?php echo htmlspecialchars($_buildNavHref($item)); ?>"<?php echo $_navLinkAttrs($item, $isActive); ?>>
|
|
<?php echo htmlspecialchars($item['label']); ?>
|
|
<?php if ($hasChildren): ?><svg class="nav-chevron" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true" focusable="false"><polyline points="6 9 12 15 18 9"/></svg><?php endif; ?>
|
|
</a>
|
|
<?php if ($hasChildren): ?>
|
|
<ul class="nav-dropdown">
|
|
<?php foreach ($item['children'] as $child):
|
|
$_childHash = $_headerMenuType === 'one-page' && str_starts_with((string)($child['href'] ?? ''), '#');
|
|
$_childActive = !$_childHash && (($currentPage ?? '') === ($child['page'] ?? ''));
|
|
?>
|
|
<li>
|
|
<a href="<?php echo htmlspecialchars($_buildNavHref($child)); ?>"<?php echo $_navLinkAttrs($child, $_childActive); ?>>
|
|
<?php echo htmlspecialchars($child['label']); ?>
|
|
</a>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
|
|
<!-- Language Selector -->
|
|
<?php if (count($SITE_LANGUAGES) > 1): ?>
|
|
<div class="nav-lang" aria-label="Language selection">
|
|
<?php $langCodes = array_keys($SITE_LANGUAGES); ?>
|
|
<?php foreach ($langCodes as $i => $code): ?>
|
|
<?php if ($i > 0): ?><span class="lang-separator">|</span><?php endif; ?>
|
|
<a href="<?php echo $langLinks[$code]; ?>" class="lang-link<?php echo ($code === $currentLang) ? ' active' : ''; ?>"<?php echo ($code === $currentLang) ? ' aria-current="true"' : ''; ?>><?php echo strtoupper($code); ?></a>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<!-- Theme Toggle -->
|
|
<button class="theme-toggle" id="themeToggle" aria-label="Toggle theme" title="Toggle theme">
|
|
<svg class="theme-icon theme-icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
|
|
<svg class="theme-icon theme-icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg>
|
|
</button>
|
|
</nav>
|
|
|
|
<!-- Hamburger Menu Button -->
|
|
<button class="hamburger" id="hamburger" aria-label="Open menu" aria-expanded="false" aria-controls="mobileNavOverlay">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Mobile Navigation Overlay -->
|
|
<div class="mobile-nav-overlay" id="mobileNavOverlay" aria-hidden="true">
|
|
<nav class="mobile-nav" aria-label="Mobile navigation">
|
|
<ul class="mobile-nav-list">
|
|
<?php foreach ($navItems as $item):
|
|
$hasChildren = !empty($item['children']);
|
|
$isHashItem = $_headerMenuType === 'one-page' && str_starts_with((string)($item['href'] ?? ''), '#');
|
|
$isActive = !$isHashItem && (($currentPage ?? '') === ($item['page'] ?? ''));
|
|
if ($hasChildren && !$isActive) {
|
|
foreach ($item['children'] as $_child) {
|
|
$_childHash = $_headerMenuType === 'one-page' && str_starts_with((string)($_child['href'] ?? ''), '#');
|
|
if (!$_childHash && ($currentPage ?? '') === ($_child['page'] ?? '')) { $isActive = true; break; }
|
|
}
|
|
}
|
|
?>
|
|
<li<?php echo $hasChildren ? ' class="mobile-nav-item--parent"' : ''; ?>>
|
|
<a href="<?php echo htmlspecialchars($_buildNavHref($item)); ?>"<?php echo $_navLinkAttrs($item, $isActive); ?>>
|
|
<?php echo htmlspecialchars($item['label']); ?>
|
|
</a>
|
|
<?php if ($hasChildren): ?>
|
|
<ul class="mobile-nav-children">
|
|
<?php foreach ($item['children'] as $child):
|
|
$_childHash = $_headerMenuType === 'one-page' && str_starts_with((string)($child['href'] ?? ''), '#');
|
|
$_childActive = !$_childHash && (($currentPage ?? '') === ($child['page'] ?? ''));
|
|
?>
|
|
<li>
|
|
<a href="<?php echo htmlspecialchars($_buildNavHref($child)); ?>"<?php echo $_navLinkAttrs($child, $_childActive); ?>>
|
|
<?php echo htmlspecialchars($child['label']); ?>
|
|
</a>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
|
|
<?php if (count($SITE_LANGUAGES) > 1): ?>
|
|
<div class="mobile-nav-lang">
|
|
<?php foreach ($langCodes as $i => $code): ?>
|
|
<?php if ($i > 0): ?><span>|</span><?php endif; ?>
|
|
<a href="<?php echo $langLinks[$code]; ?>"<?php echo ($code === $currentLang) ? ' class="active" aria-current="true"' : ''; ?>><?php echo strtoupper($code); ?></a>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="mobile-theme-toggle">
|
|
<button class="theme-toggle-mobile" data-theme-choice="light" aria-label="Light theme">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
|
|
</button>
|
|
<button class="theme-toggle-mobile" data-theme-choice="dark" aria-label="Dark theme">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg>
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
</div>
|