"Mein Konto" und "Abmelden" starteten je nach Breite des Emojis (👤 vs 🚪) an leicht unterschiedlichen Stellen. Jetzt echtes Flex-Layout mit fester Icon-Spalte (1.2em, zentriert) — beide Labels stehen exakt untereinander. Co-Authored-By: Claude Sonnet 5 <[email protected]>
494 lines
20 KiB
Plaintext
494 lines
20 KiB
Plaintext
---
|
||
import "../styles/global.css";
|
||
import { categories } from "../data/categories";
|
||
import { defaultLocale, localeMeta, localePrefix, type Locale } from "../i18n/config";
|
||
import { useTranslations } from "../i18n/ui";
|
||
import LanguageSwitcher from "../components/LanguageSwitcher.astro";
|
||
|
||
interface Props {
|
||
title: string;
|
||
description?: string;
|
||
lang?: Locale;
|
||
/** Sprachneutraler Pfad der aktuellen Seite (ohne Sprach-Prefix), z.B. "/shop/". Für den Sprachumschalter. */
|
||
path?: string;
|
||
/** Seite existiert nur auf Deutsch (Rechtstexte) — siehe LanguageSwitcher. */
|
||
legalOnly?: boolean;
|
||
/** Interne Vorschau-/Demoseiten (z.B. /demo-abo/) sollen nie von Suchmaschinen indexiert
|
||
* werden — setzt zusätzlich zum Sitemap-Ausschluss noch ein robots-Meta. */
|
||
noindex?: boolean;
|
||
}
|
||
const {
|
||
title,
|
||
description = "Van's DIY & Bastelbedarf – handgemachte Häkelwerke, Plushies, Schmuck und Bastelzubehör mit Liebe gefertigt.",
|
||
lang = defaultLocale,
|
||
path = "/",
|
||
legalOnly = false,
|
||
noindex = false,
|
||
} = Astro.props;
|
||
|
||
const t = useTranslations(lang);
|
||
const p = (href: string) => `${localePrefix(lang)}${href}`;
|
||
---
|
||
<!doctype html>
|
||
<html lang={localeMeta[lang].htmlLang}>
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<!-- Eigenes Logo als Icon — überall (Browser-Tab, Lesezeichen, Installation als App/PWA auf
|
||
PC & Handy) statt eines automatisch generierten Buchstaben-Icons. -->
|
||
<link rel="icon" type="image/x-icon" href="/favicon.ico" sizes="any" />
|
||
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32" />
|
||
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16" />
|
||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||
<link rel="manifest" href="/manifest.webmanifest" />
|
||
<title>{title} · Van's DIY & Bastelbedarf</title>
|
||
<meta name="description" content={description} />
|
||
{noindex && <meta name="robots" content="noindex, nofollow" />}
|
||
<link rel="canonical" href={new URL(Astro.url.pathname, Astro.site ?? "https://vans-diy-bastelbedarf.de").toString()} />
|
||
<meta property="og:type" content="website" />
|
||
<meta property="og:site_name" content="Van's DIY & Bastelbedarf" />
|
||
<meta property="og:title" content={title} />
|
||
<meta property="og:description" content={description} />
|
||
<meta name="theme-color" content="#0a0910" />
|
||
<!-- TODO Phase 2: Fonts self-hosten statt Google-Fonts-CDN (Performance/DSGVO) -->
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,[email protected],300..700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||
<style>
|
||
:root { --font-head-override: "Fraunces", serif; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="topbar">🩵 {t.topbar}</div>
|
||
|
||
<header class="site-header">
|
||
<div class="container header-inner">
|
||
<a href={p("/")} class="brand">
|
||
<img src="/logo.png" alt="Van's DIY & Bastelbedarf Logo" width="48" height="48" />
|
||
<span>Van's DIY & Bastelbedarf</span>
|
||
</a>
|
||
|
||
<nav class="main-nav" aria-label="Hauptnavigation">
|
||
<a href={p("/shop/")}>{t.nav.shop}</a>
|
||
<a href={p("/sale/")}>{t.nav.sale}</a>
|
||
<a href={p("/ueber-die-kreative/")}>{t.nav.creative}</a>
|
||
<a href={p("/faq/")}>{t.nav.faq}</a>
|
||
<a href={p("/kontakt/")}>{t.nav.contact}</a>
|
||
</nav>
|
||
|
||
<div class="header-actions">
|
||
<button type="button" id="install-btn" class="install-btn" style="display:none;" title={t.common.installApp}>
|
||
<span class="install-icon" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M12 3a1 1 0 0 1 1 1v9.6l3-3a1 1 0 1 1 1.4 1.4l-4.7 4.7a1 1 0 0 1-1.4 0L6.6 12a1 1 0 1 1 1.4-1.4l3 3V4a1 1 0 0 1 1-1Zm-7 15a1 1 0 0 1 1-1h12a1 1 0 1 1 0 2H6a1 1 0 0 1-1-1Z"/></svg>
|
||
</span>
|
||
<span class="install-label">{t.common.installAppShort}</span>
|
||
</button>
|
||
<LanguageSwitcher lang={lang} path={path} legalOnly={legalOnly} />
|
||
{/* Zwei Zustände nebeneinander im HTML, per JS umgeschaltet (siehe Script unten) — ohne
|
||
eingebautes Login (Phase 1: nur clientseitig im localStorage, siehe scripts/account.ts)
|
||
weiß der Server beim Rendern nie, ob jemand "angemeldet" ist. Standardmäßig ist nur der
|
||
Anmelden-Link sichtbar; sobald JS geladen hat und ein lokales Konto findet, blendet es
|
||
auf das Konto-Menü um. */}
|
||
<div class="account-area" id="account-area">
|
||
<a href={p("/konto/")} class="account-btn" id="account-login-btn">
|
||
<span class="account-icon" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M12 12a5 5 0 1 0-5-5 5 5 0 0 0 5 5Zm0 2c-4.4 0-9 2.2-9 5v2h18v-2c0-2.8-4.6-5-9-5Z"/></svg>
|
||
</span>
|
||
<span class="account-label">{t.nav.login}</span>
|
||
</a>
|
||
<div class="account-menu" id="account-menu" style="display:none;">
|
||
<button type="button" class="account-menu-btn" id="account-menu-btn" aria-haspopup="true" aria-expanded="false">
|
||
<span class="account-avatar" id="account-avatar" aria-hidden="true"></span>
|
||
<span class="account-username" id="account-username-label"></span>
|
||
<span class="chevron" aria-hidden="true">▼</span>
|
||
</button>
|
||
<div class="account-dropdown" id="account-dropdown" role="menu">
|
||
<a href={p("/konto/angemeldet/")} role="menuitem"><span class="account-dropdown-icon" aria-hidden="true">👤</span>{t.accountMenu.myAccount}</a>
|
||
<button type="button" id="account-logout-btn" role="menuitem"><span class="account-dropdown-icon" aria-hidden="true">🚪</span>{t.accountMenu.logout}</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<a href={p("/warenkorb/")} class="icon-link cart-link" aria-label={t.nav.account}>
|
||
🧺 <span class="cart-count" id="cart-count">0</span>
|
||
</a>
|
||
<button class="hamburger" id="hamburger" aria-label="Menü" aria-expanded="false">☰</button>
|
||
</div>
|
||
</div>
|
||
<nav class="mobile-nav" id="mobile-nav" aria-label="Mobile Navigation">
|
||
<a href={p("/shop/")}>{t.nav.shop}</a>
|
||
<a href={p("/sale/")}>{t.nav.sale}</a>
|
||
<a href={p("/ueber-die-kreative/")}>{t.nav.creative}</a>
|
||
<a href={p("/faq/")}>{t.nav.faq}</a>
|
||
<a href={p("/kontakt/")}>{t.nav.contact}</a>
|
||
<a href={p("/konto/")}>{t.nav.account}</a>
|
||
<span class="mobile-nav-divider">{t.shop.categoryLabel}</span>
|
||
{categories.map((c) => <a href={p(`/shop/${c.slug}/`)}>{c.icon} {c.name[lang]}</a>)}
|
||
</nav>
|
||
</header>
|
||
|
||
<nav class="sub-nav" aria-label="Kategorien-Schnellzugriff">
|
||
<div class="container sub-nav-inner">
|
||
{categories.map((c) => (
|
||
<a href={p(`/shop/${c.slug}/`)}>{c.name[lang]}</a>
|
||
))}
|
||
</div>
|
||
</nav>
|
||
|
||
<main>
|
||
<slot />
|
||
</main>
|
||
|
||
<footer class="site-footer">
|
||
<div class="container footer-grid">
|
||
<div>
|
||
<a href={p("/")} class="brand">
|
||
<img src="/logo.png" alt="Van's DIY & Bastelbedarf Logo" width="36" height="36" />
|
||
<span>Van's DIY & Bastelbedarf</span>
|
||
</a>
|
||
<p class="small">{t.footer.tagline}</p>
|
||
</div>
|
||
<div>
|
||
<h3>{t.footer.shopHeading}</h3>
|
||
<ul class="footer-list">
|
||
{categories.map((c) => <li><a href={p(`/shop/${c.slug}/`)}>{c.name[lang]}</a></li>)}
|
||
</ul>
|
||
</div>
|
||
<div>
|
||
<h3>{t.footer.serviceHeading}</h3>
|
||
<ul class="footer-list">
|
||
<li><a href={p("/versand-zahlung/")}>{t.footer.shipping}</a></li>
|
||
<li><a href={p("/faq/")}>{t.nav.faq}</a></li>
|
||
<li><a href={p("/kontakt/")}>{t.nav.contact}</a></li>
|
||
<li><a href={p("/konto/")}>{t.footer.account}</a></li>
|
||
</ul>
|
||
</div>
|
||
<div>
|
||
<h3>{t.footer.legalHeading}</h3>
|
||
<ul class="footer-list">
|
||
<li><a href="/impressum/">Impressum</a></li>
|
||
<li><a href="/datenschutz/">Datenschutz</a></li>
|
||
<li><a href="/agb/">AGB</a></li>
|
||
<li><a href="/widerruf/">Widerrufsbelehrung</a></li>
|
||
<li><a href="/muster-widerrufsformular/">Muster-Widerrufsformular</a></li>
|
||
</ul>
|
||
{lang !== "de" && <p class="small legal-note-footer" title={t.footer.legalNoteTitle}>{t.footer.legalNote}</p>}
|
||
</div>
|
||
</div>
|
||
<div class="container">
|
||
<hr class="divider" />
|
||
<p class="small">© {new Date().getFullYear()} Van's DIY & Bastelbedarf · {t.footer.copyright}</p>
|
||
</div>
|
||
</footer>
|
||
|
||
<script>
|
||
import { cartCount } from "../scripts/cart";
|
||
import { getAccount, logout, anzeigeName } from "../scripts/account";
|
||
|
||
function updateCount() {
|
||
const el = document.getElementById("cart-count");
|
||
if (el) el.textContent = String(cartCount());
|
||
}
|
||
updateCount();
|
||
window.addEventListener("cart:changed", updateCount);
|
||
window.addEventListener("storage", updateCount);
|
||
|
||
// Konto-Bereich der Kopfzeile: "Anmelden"-Link vs. Konto-Menü, je nachdem ob auf diesem
|
||
// Gerät gerade ein (Demo-)Konto angemeldet ist (siehe scripts/account.ts). Läuft auf JEDER
|
||
// Seite, weil Layout.astro von allen Seiten eingebunden wird — daher überall konsistent.
|
||
const loginBtn = document.getElementById("account-login-btn");
|
||
const menu = document.getElementById("account-menu");
|
||
const menuBtn = document.getElementById("account-menu-btn");
|
||
const dropdown = document.getElementById("account-dropdown");
|
||
const avatar = document.getElementById("account-avatar");
|
||
const usernameLabel = document.getElementById("account-username-label");
|
||
const logoutBtn = document.getElementById("account-logout-btn");
|
||
|
||
function renderAccountArea() {
|
||
const a = getAccount();
|
||
if (loginBtn) loginBtn.style.display = a.loggedIn ? "none" : "";
|
||
if (menu) menu.style.display = a.loggedIn ? "flex" : "none";
|
||
if (!a.loggedIn) return;
|
||
if (usernameLabel) usernameLabel.textContent = anzeigeName(a) || "Konto";
|
||
if (avatar) {
|
||
if (a.photo) {
|
||
avatar.style.backgroundImage = `url(${a.photo})`;
|
||
avatar.textContent = "";
|
||
} else {
|
||
avatar.style.backgroundImage = "";
|
||
avatar.textContent = (anzeigeName(a) || "?").trim().charAt(0).toUpperCase();
|
||
}
|
||
}
|
||
}
|
||
renderAccountArea();
|
||
window.addEventListener("account:changed", renderAccountArea);
|
||
window.addEventListener("storage", renderAccountArea);
|
||
|
||
menuBtn?.addEventListener("click", (e) => {
|
||
e.stopPropagation();
|
||
const open = menu?.classList.toggle("open");
|
||
menuBtn.setAttribute("aria-expanded", open ? "true" : "false");
|
||
});
|
||
|
||
// Öffnet zusätzlich automatisch beim Drüberfahren mit der Maus (Desktop) — kein Klick mehr
|
||
// nötig. Kurze Schließverzögerung beim Verlassen, damit der Wechsel von Button zu Menü nicht
|
||
// sofort wieder zuklappt. Klick bleibt als Fallback für Touch-Geräte/Tastatur erhalten.
|
||
let accountCloseTimer: ReturnType<typeof setTimeout> | null = null;
|
||
menu?.addEventListener("mouseenter", () => {
|
||
if (accountCloseTimer) { clearTimeout(accountCloseTimer); accountCloseTimer = null; }
|
||
menu.classList.add("open");
|
||
menuBtn?.setAttribute("aria-expanded", "true");
|
||
});
|
||
menu?.addEventListener("mouseleave", () => {
|
||
accountCloseTimer = setTimeout(() => {
|
||
menu.classList.remove("open");
|
||
menuBtn?.setAttribute("aria-expanded", "false");
|
||
}, 200);
|
||
});
|
||
|
||
document.addEventListener("click", () => {
|
||
menu?.classList.remove("open");
|
||
menuBtn?.setAttribute("aria-expanded", "false");
|
||
});
|
||
document.addEventListener("keydown", (e) => {
|
||
if (e.key === "Escape") {
|
||
menu?.classList.remove("open");
|
||
menuBtn?.setAttribute("aria-expanded", "false");
|
||
}
|
||
});
|
||
logoutBtn?.addEventListener("click", () => {
|
||
logout();
|
||
menu?.classList.remove("open");
|
||
menuBtn?.setAttribute("aria-expanded", "false");
|
||
});
|
||
|
||
const hamburger = document.getElementById("hamburger");
|
||
const mobileNav = document.getElementById("mobile-nav");
|
||
hamburger?.addEventListener("click", () => {
|
||
const open = mobileNav?.classList.toggle("open");
|
||
hamburger.setAttribute("aria-expanded", String(!!open));
|
||
});
|
||
|
||
// "Auf dem Gerät installieren" — zeigt einen echten Button in der Kopfzeile, sobald der
|
||
// Browser eine Installation anbietet (Chrome/Edge auf PC & Handy), statt sich darauf zu
|
||
// verlassen, dass jemand das winzige Icon im Browser selbst findet. Läuft bereits als
|
||
// installierte App? Dann gibt es sowieso nichts mehr zu installieren — Button bleibt versteckt.
|
||
const installBtn = document.getElementById("install-btn");
|
||
const laeuftBereitsAlsApp = window.matchMedia("(display-mode: standalone)").matches;
|
||
let installPromptEvent = null;
|
||
if (!laeuftBereitsAlsApp) {
|
||
window.addEventListener("beforeinstallprompt", (e) => {
|
||
e.preventDefault();
|
||
installPromptEvent = e;
|
||
if (installBtn) installBtn.style.display = "flex";
|
||
});
|
||
}
|
||
installBtn?.addEventListener("click", async () => {
|
||
if (!installPromptEvent) return;
|
||
installBtn.style.display = "none";
|
||
installPromptEvent.prompt();
|
||
await installPromptEvent.userChoice;
|
||
installPromptEvent = null;
|
||
});
|
||
window.addEventListener("appinstalled", () => {
|
||
if (installBtn) installBtn.style.display = "none";
|
||
});
|
||
|
||
// Service Worker: rein "network-first" (versucht IMMER zuerst das echte Netzwerk, Cache
|
||
// dient nur als Rückfalllösung, wenn wirklich keine Verbindung besteht) — kein Risiko auf
|
||
// veraltete Inhalte im Adminbereich, macht die Seite aber gleichzeitig zu einer "echten" App
|
||
// (Installations-Voraussetzung in den meisten Browsern) und funktioniert notfalls auch kurz
|
||
// offline für bereits besuchte Seiten.
|
||
if ("serviceWorker" in navigator) {
|
||
window.addEventListener("load", () => {
|
||
navigator.serviceWorker.register("/sw.js").catch(() => {});
|
||
});
|
||
}
|
||
</script>
|
||
|
||
<style is:global>
|
||
body { font-family: var(--font-body); }
|
||
h1, h2, h3, h4 { font-family: var(--font-head-override, var(--font-head)); }
|
||
|
||
.topbar {
|
||
background: linear-gradient(90deg, var(--c-purple), var(--c-purple-deep));
|
||
color: var(--c-text);
|
||
text-align: center;
|
||
font-size: 0.9rem;
|
||
padding: 0.6rem 1rem;
|
||
letter-spacing: 0.01em;
|
||
}
|
||
|
||
.site-header {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 40;
|
||
background: rgba(10, 9, 16, 0.85);
|
||
backdrop-filter: blur(10px);
|
||
border-bottom: 1px solid rgba(244, 241, 247, 0.06);
|
||
}
|
||
.header-inner {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 1rem;
|
||
padding-block: 1rem;
|
||
}
|
||
.brand {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.6rem;
|
||
font-family: var(--font-head);
|
||
font-weight: 600;
|
||
font-size: 1.18rem;
|
||
}
|
||
.brand img { border-radius: 50%; }
|
||
|
||
.main-nav { display: flex; gap: 1.7rem; flex-shrink: 0; }
|
||
.main-nav a {
|
||
font-size: 1.02rem;
|
||
color: var(--c-text-muted);
|
||
white-space: nowrap;
|
||
transition: color 0.2s var(--ease);
|
||
}
|
||
.main-nav a:hover { color: var(--c-accent); }
|
||
|
||
.header-actions { display: flex; align-items: center; gap: 0.8rem; }
|
||
.icon-link { font-size: 1.25rem; position: relative; display: inline-flex; align-items: center; gap: 0.3rem; }
|
||
|
||
/* "Auf dem Gerät installieren"-Button — nur sichtbar, wenn der Browser das wirklich anbietet
|
||
(per JS über das beforeinstallprompt-Event ein-/ausgeblendet, siehe Script unten). Damit
|
||
muss niemand erst das kleine, leicht zu übersehende Installations-Icon im Browser selbst
|
||
suchen — der Button ist direkt in der Kopfzeile sichtbar. */
|
||
.install-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.45em;
|
||
padding: 0.5em 0.9em 0.5em 0.7em;
|
||
border-radius: 999px;
|
||
background: linear-gradient(135deg, rgba(155, 127, 214, 0.22), rgba(46, 27, 71, 0.4));
|
||
border: 1px solid rgba(155, 127, 214, 0.4);
|
||
color: var(--c-text);
|
||
font-size: 0.88rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: border-color 0.2s var(--ease), transform 0.2s var(--ease), box-shadow 0.2s var(--ease);
|
||
}
|
||
.install-btn:hover {
|
||
border-color: var(--c-purple-glow);
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 8px 20px -8px rgba(155, 127, 214, 0.5);
|
||
}
|
||
.install-icon { display: flex; }
|
||
@media (max-width: 640px) { .install-btn .install-label { display: none; } .install-btn { padding: 0.5em; } }
|
||
.cart-count {
|
||
background: var(--c-accent);
|
||
color: #08181c;
|
||
font-size: 0.7rem;
|
||
font-weight: 700;
|
||
border-radius: 999px;
|
||
min-width: 1.2em;
|
||
padding: 0 0.35em;
|
||
text-align: center;
|
||
}
|
||
.hamburger {
|
||
display: none;
|
||
background: none;
|
||
border: none;
|
||
color: var(--c-text);
|
||
font-size: 1.4rem;
|
||
cursor: pointer;
|
||
}
|
||
.mobile-nav {
|
||
display: none;
|
||
flex-direction: column;
|
||
padding: 0.5rem 1.5rem 1.2rem;
|
||
gap: 0.9rem;
|
||
border-top: 1px solid rgba(244,241,247,0.06);
|
||
}
|
||
.mobile-nav.open { display: flex; }
|
||
.mobile-nav-divider {
|
||
margin-top: 0.4rem;
|
||
padding-top: 0.9rem;
|
||
border-top: 1px solid rgba(244, 241, 247, 0.08);
|
||
font-size: 0.72rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
color: var(--c-accent);
|
||
}
|
||
|
||
@media (max-width: 1100px) {
|
||
.main-nav { display: none; }
|
||
.hamburger { display: inline-block; }
|
||
}
|
||
/* Auf schmalen Screens (Handy) reicht der Platz nicht mehr für Logo+Markenname+
|
||
Sprachumschalter-Text+Konto-Icon+Warenkorb+Menü-Button nebeneinander in voller Größe —
|
||
ohne diese Anpassung würde der Menü-Button (☰) rechts aus dem Bildschirm rausgeschoben
|
||
und wäre gar nicht mehr anklickbar. Zeigt auf schmalen Screens nur noch die Icons/Kürzel. */
|
||
@media (max-width: 480px) {
|
||
.brand img { width: 38px; height: 38px; }
|
||
.brand span { font-size: 0.98rem; }
|
||
.lang-switch-btn { padding: 0.35em 0.55em; }
|
||
.lang-switch-btn .lang-label,
|
||
.lang-switch-btn .chevron { display: none; }
|
||
}
|
||
|
||
/* Zweite Navigationsleiste: Schnellzugriff auf die 5 Hauptkategorien, direkt unter der
|
||
Hauptnav — gleicher Designstil (dunkler Anthrazit-Streifen, Akzent-Unterstrich bei Hover). */
|
||
.sub-nav {
|
||
background: var(--c-anthracite);
|
||
border-bottom: 1px solid rgba(244, 241, 247, 0.06);
|
||
}
|
||
.sub-nav-inner {
|
||
display: flex;
|
||
gap: clamp(1.1rem, 3vw, 2rem);
|
||
overflow-x: auto;
|
||
padding-block: 0.75rem;
|
||
scrollbar-width: none;
|
||
}
|
||
.sub-nav-inner::-webkit-scrollbar { display: none; }
|
||
.sub-nav-inner a {
|
||
position: relative;
|
||
font-size: 0.92rem;
|
||
font-weight: 600;
|
||
color: var(--c-text-muted);
|
||
white-space: nowrap;
|
||
padding-bottom: 3px;
|
||
transition: color 0.2s var(--ease);
|
||
}
|
||
.sub-nav-inner a::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: -1px;
|
||
height: 2px;
|
||
background: var(--c-accent);
|
||
transform: scaleX(0);
|
||
transform-origin: left;
|
||
transition: transform 0.2s var(--ease);
|
||
}
|
||
.sub-nav-inner a:hover { color: var(--c-accent); }
|
||
.sub-nav-inner a:hover::after { transform: scaleX(1); }
|
||
@media (max-width: 860px) { .sub-nav { display: none; } }
|
||
|
||
.site-footer {
|
||
background: var(--c-anthracite);
|
||
margin-top: 4rem;
|
||
padding-top: 3rem;
|
||
padding-bottom: 2rem;
|
||
}
|
||
.footer-grid {
|
||
display: grid;
|
||
grid-template-columns: 1.4fr 1fr 1fr 1fr;
|
||
gap: 2rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
.footer-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.55rem; }
|
||
.footer-list a { color: var(--c-text-muted); font-size: 0.9rem; }
|
||
.footer-list a:hover { color: var(--c-accent); }
|
||
.legal-note-footer { margin-top: 0.8rem; font-size: 0.78rem; line-height: 1.5; }
|
||
@media (max-width: 760px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
|
||
@media (max-width: 480px) { .footer-grid { grid-template-columns: 1fr; } }
|
||
</style>
|
||
</body>
|
||
</html>
|