2026-03-24 18:01:45 +00:00
|
|
|
---
|
|
|
|
|
import Navigation from "./Navigation.astro";
|
|
|
|
|
import ThemeIcon from "./ThemeIcon.astro";
|
2026-03-24 22:28:23 +00:00
|
|
|
import { getLangFromUrl, getTranslations } from "@/i18n";
|
2026-07-18 07:48:32 +00:00
|
|
|
import Divider from "@/components/Divider.astro";
|
2026-03-24 22:28:23 +00:00
|
|
|
|
|
|
|
|
const lang = getLangFromUrl(Astro.url);
|
|
|
|
|
const t = getTranslations(lang);
|
2026-03-24 18:01:45 +00:00
|
|
|
---
|
|
|
|
|
|
2026-09-17 04:05:55 +00:00
|
|
|
<div class="header-slot">
|
2026-03-24 22:28:23 +00:00
|
|
|
<header class="site-header">
|
|
|
|
|
<div class="theme-icon-wrap">
|
|
|
|
|
<ThemeIcon />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<nav class="header-nav">
|
2026-03-28 20:42:16 +00:00
|
|
|
<div class="banner">
|
|
|
|
|
<a
|
|
|
|
|
href={`/${lang}/`}
|
|
|
|
|
data-astro-reload
|
|
|
|
|
class="banner-link"
|
|
|
|
|
aria-label="banner"
|
|
|
|
|
>
|
|
|
|
|
<span class="banner-logo"></span>
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
2026-03-24 18:01:45 +00:00
|
|
|
<Navigation />
|
|
|
|
|
</nav>
|
2026-07-18 07:48:32 +00:00
|
|
|
<div class="divider">
|
|
|
|
|
<Divider />
|
|
|
|
|
</div>
|
2026-03-24 18:01:45 +00:00
|
|
|
</header>
|
2026-09-17 04:05:55 +00:00
|
|
|
</div>
|
2026-03-24 18:01:45 +00:00
|
|
|
|
|
|
|
|
<style>
|
2026-09-17 04:05:55 +00:00
|
|
|
.site-header {
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 30;
|
|
|
|
|
padding-top: 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-28 20:42:16 +00:00
|
|
|
.banner-link {
|
2026-09-17 04:05:55 +00:00
|
|
|
display: block;
|
2026-03-28 20:42:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.banner-logo {
|
|
|
|
|
display: block;
|
2026-09-17 04:05:55 +00:00
|
|
|
height: 55px;
|
2026-03-28 20:42:16 +00:00
|
|
|
background-color: #1f2328;
|
|
|
|
|
-webkit-mask: url("/images/banner-black.svg") no-repeat left / contain;
|
|
|
|
|
mask: url("/images/banner-black.svg") no-repeat left / contain;
|
2026-09-17 04:05:55 +00:00
|
|
|
transform-origin: left center;
|
2026-03-28 20:42:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:global(html.dark .banner-logo) {
|
|
|
|
|
background-color: #e6e6e6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-nav .banner a {
|
2026-03-25 15:34:38 +00:00
|
|
|
color: inherit;
|
|
|
|
|
text-decoration: none;
|
2026-03-25 18:51:38 +00:00
|
|
|
display: block;
|
2026-03-25 15:34:38 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-24 22:28:23 +00:00
|
|
|
.theme-icon-wrap {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
right: 0;
|
2026-03-25 18:51:38 +00:00
|
|
|
z-index: 2;
|
2026-03-24 22:28:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-nav {
|
2026-03-24 18:01:45 +00:00
|
|
|
display: flex;
|
2026-03-24 22:28:23 +00:00
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 1rem;
|
2026-09-17 04:05:55 +00:00
|
|
|
max-height: 12rem;
|
2026-03-24 22:28:23 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-28 20:42:16 +00:00
|
|
|
.header-nav .banner {
|
2026-03-24 22:28:23 +00:00
|
|
|
margin: 0;
|
|
|
|
|
font-style: italic;
|
2026-03-25 18:51:38 +00:00
|
|
|
line-height: 1.1;
|
2026-03-28 20:42:16 +00:00
|
|
|
max-width: calc(100% - 7rem);
|
2026-03-25 18:51:38 +00:00
|
|
|
font-size: clamp(1.6rem, 6vw, 2.5rem);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-18 07:48:32 +00:00
|
|
|
.divider {
|
|
|
|
|
margin-top: 0.2rem;
|
|
|
|
|
margin-bottom: 2rem;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-17 04:05:55 +00:00
|
|
|
@media (min-width: 769px) {
|
|
|
|
|
.header-slot {
|
|
|
|
|
/* Keep document height independent of scroll-driven header height. */
|
|
|
|
|
display: flow-root;
|
|
|
|
|
position: sticky;
|
|
|
|
|
top: 0;
|
|
|
|
|
z-index: 30;
|
|
|
|
|
height: var(--header-slot-height, auto);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.site-header {
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
width: var(--header-width, 100%);
|
|
|
|
|
max-width: none;
|
|
|
|
|
margin-left: var(--header-offset, 0);
|
|
|
|
|
margin-right: var(--header-offset, 0);
|
|
|
|
|
padding: 0.5rem var(--header-inline-padding, 0) 0;
|
|
|
|
|
background-color: color-mix(
|
|
|
|
|
in srgb,
|
|
|
|
|
var(--background-color) var(--header-surface-weight, 0%),
|
|
|
|
|
transparent
|
|
|
|
|
);
|
|
|
|
|
backdrop-filter: blur(var(--header-blur, 0));
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
transition: transform 0.3s ease;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-17 04:12:43 +00:00
|
|
|
.header-slot.is-scroll-hidden .site-header,
|
|
|
|
|
.header-slot.is-footer-hidden .site-header {
|
2026-09-17 04:05:55 +00:00
|
|
|
transform: translateY(calc(-100% - 0.5rem));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:global(html.dark) .site-header {
|
|
|
|
|
background-color: color-mix(
|
|
|
|
|
in srgb,
|
|
|
|
|
#1e1e1e var(--header-surface-weight, 0%),
|
|
|
|
|
transparent
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.theme-icon-wrap {
|
|
|
|
|
top: var(--theme-top, 0);
|
|
|
|
|
right: var(--theme-right, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-nav {
|
|
|
|
|
gap: var(--header-gap, 1rem);
|
|
|
|
|
height: var(--header-nav-height, auto);
|
|
|
|
|
max-height: var(--header-nav-height, 12rem);
|
|
|
|
|
transform: translateY(var(--header-nav-y, 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-nav .banner {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
width: var(--banner-width, auto);
|
|
|
|
|
max-width: var(--banner-width, calc(100% - 7rem));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.banner-logo {
|
|
|
|
|
height: var(--logo-height, 55px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.site-header :global(.site-nav) {
|
|
|
|
|
width: var(--site-nav-width, auto);
|
|
|
|
|
margin: var(--site-nav-margin, 1rem) 0;
|
|
|
|
|
padding-bottom: var(--site-nav-padding, 1rem);
|
|
|
|
|
transform: translate(
|
|
|
|
|
var(--site-nav-x, 0),
|
|
|
|
|
var(--site-nav-y, 0)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.site-header :global(.site-nav-desktop) {
|
|
|
|
|
gap: var(--site-nav-gap, 1.5rem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.site-header :global(.site-nav a) {
|
|
|
|
|
font-size: var(--site-nav-font-size, 1.2rem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.site-header > .divider {
|
|
|
|
|
width: var(--divider-width, 100%);
|
|
|
|
|
margin-left: var(--divider-offset, 0);
|
|
|
|
|
margin-right: var(--divider-offset, 0);
|
|
|
|
|
margin-top: var(--divider-margin-top, 0.2rem);
|
|
|
|
|
margin-bottom: var(--divider-margin-bottom, 2rem);
|
|
|
|
|
transform: translateY(var(--divider-y, 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.site-header > .divider :global(.divider) {
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin-bottom: var(--divider-inner-margin-bottom, 1rem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 18:51:38 +00:00
|
|
|
@media (max-width: 768px) {
|
2026-09-17 04:05:55 +00:00
|
|
|
.header-slot {
|
|
|
|
|
display: contents;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 18:51:38 +00:00
|
|
|
.site-header {
|
2026-09-17 04:05:55 +00:00
|
|
|
position: relative;
|
2026-03-25 18:51:38 +00:00
|
|
|
padding-top: 0.25rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-nav h1 {
|
|
|
|
|
max-width: calc(100% - 3.5rem);
|
|
|
|
|
font-size: clamp(1.25rem, 7vw, 2rem);
|
|
|
|
|
}
|
2026-03-24 18:01:45 +00:00
|
|
|
}
|
2026-09-17 04:05:55 +00:00
|
|
|
|
2026-03-24 18:01:45 +00:00
|
|
|
</style>
|
2026-09-17 04:05:55 +00:00
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
const desktopHeaderQuery = window.matchMedia("(min-width: 769px)");
|
|
|
|
|
const HEADER_SCROLL_DISTANCE = 120;
|
2026-09-17 04:12:43 +00:00
|
|
|
const DIRECTION_TRIGGER_DISTANCE = 6;
|
2026-09-17 04:05:55 +00:00
|
|
|
const headerStyleProperties = [
|
|
|
|
|
"--header-width",
|
|
|
|
|
"--header-offset",
|
|
|
|
|
"--header-inline-padding",
|
|
|
|
|
"--header-surface-weight",
|
|
|
|
|
"--header-blur",
|
|
|
|
|
"--theme-top",
|
|
|
|
|
"--theme-right",
|
|
|
|
|
"--header-gap",
|
|
|
|
|
"--header-nav-height",
|
|
|
|
|
"--header-nav-y",
|
|
|
|
|
"--banner-width",
|
|
|
|
|
"--logo-height",
|
|
|
|
|
"--site-nav-width",
|
|
|
|
|
"--site-nav-margin",
|
|
|
|
|
"--site-nav-padding",
|
|
|
|
|
"--site-nav-x",
|
|
|
|
|
"--site-nav-y",
|
|
|
|
|
"--site-nav-gap",
|
|
|
|
|
"--site-nav-font-size",
|
|
|
|
|
"--divider-margin-top",
|
|
|
|
|
"--divider-margin-bottom",
|
|
|
|
|
"--divider-inner-margin-bottom",
|
|
|
|
|
"--divider-width",
|
|
|
|
|
"--divider-offset",
|
|
|
|
|
"--divider-y",
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
type HeaderMetrics = {
|
|
|
|
|
header: HTMLElement;
|
|
|
|
|
expandedWidth: number;
|
|
|
|
|
expandedNavHeight: number;
|
|
|
|
|
expandedBannerWidth: number;
|
|
|
|
|
expandedSiteNavWidth: number;
|
|
|
|
|
rootFontSize: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let headerFrame = 0;
|
|
|
|
|
let headerMetrics: HeaderMetrics | null = null;
|
|
|
|
|
let footerDividerObserver: IntersectionObserver | null = null;
|
2026-09-17 04:12:43 +00:00
|
|
|
let lastScrollY = window.scrollY;
|
|
|
|
|
let scrollDirection = 0;
|
|
|
|
|
let directionDistance = 0;
|
2026-09-17 04:05:55 +00:00
|
|
|
|
|
|
|
|
const lerp = (start: number, end: number, progress: number) =>
|
|
|
|
|
start + (end - start) * progress;
|
|
|
|
|
|
|
|
|
|
function clearHeaderStyles(header: HTMLElement) {
|
|
|
|
|
headerStyleProperties.forEach((property) => {
|
|
|
|
|
header.style.removeProperty(property);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function measureHeader(header: HTMLElement): HeaderMetrics | null {
|
|
|
|
|
clearHeaderStyles(header);
|
|
|
|
|
const slot = header.parentElement;
|
|
|
|
|
if (!slot) return null;
|
|
|
|
|
slot.style.removeProperty("--header-slot-height");
|
|
|
|
|
|
|
|
|
|
const headerNav = header.querySelector<HTMLElement>(".header-nav");
|
|
|
|
|
const banner = header.querySelector<HTMLElement>(".banner");
|
|
|
|
|
const siteNav = header.querySelector<HTMLElement>(".site-nav");
|
|
|
|
|
|
|
|
|
|
if (!headerNav || !banner || !siteNav) return null;
|
|
|
|
|
|
|
|
|
|
// Measure the expanded footprint, including its bottom spacing, once.
|
|
|
|
|
// Only the contents animate; the following page content never reflows.
|
|
|
|
|
slot.style.setProperty(
|
|
|
|
|
"--header-slot-height",
|
|
|
|
|
`${slot.getBoundingClientRect().height}px`,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
header,
|
|
|
|
|
expandedWidth: header.getBoundingClientRect().width,
|
|
|
|
|
expandedNavHeight: headerNav.getBoundingClientRect().height,
|
|
|
|
|
expandedBannerWidth: banner.getBoundingClientRect().width,
|
|
|
|
|
expandedSiteNavWidth: siteNav.getBoundingClientRect().width,
|
|
|
|
|
rootFontSize: Number.parseFloat(
|
|
|
|
|
getComputedStyle(document.documentElement).fontSize,
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateHeaderState() {
|
|
|
|
|
const header = document.querySelector<HTMLElement>(".site-header");
|
|
|
|
|
if (!header) return;
|
|
|
|
|
|
2026-09-17 04:12:43 +00:00
|
|
|
updateScrollVisibility(header.parentElement, window.scrollY);
|
|
|
|
|
|
2026-09-17 04:05:55 +00:00
|
|
|
if (!desktopHeaderQuery.matches) {
|
|
|
|
|
clearHeaderStyles(header);
|
|
|
|
|
header.parentElement?.style.removeProperty("--header-slot-height");
|
|
|
|
|
headerMetrics = null;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!headerMetrics || headerMetrics.header !== header) {
|
|
|
|
|
headerMetrics = measureHeader(header);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!headerMetrics) return;
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
expandedWidth,
|
|
|
|
|
expandedNavHeight,
|
|
|
|
|
expandedBannerWidth,
|
|
|
|
|
expandedSiteNavWidth,
|
|
|
|
|
rootFontSize,
|
|
|
|
|
} = headerMetrics;
|
|
|
|
|
const rawProgress = Math.min(
|
|
|
|
|
Math.max(window.scrollY / HEADER_SCROLL_DISTANCE, 0),
|
|
|
|
|
1,
|
|
|
|
|
);
|
|
|
|
|
const progress = rawProgress;
|
|
|
|
|
const viewportWidth = document.documentElement.clientWidth;
|
|
|
|
|
const compactLogoWidth = Math.min(
|
|
|
|
|
Math.max(9 * rootFontSize, viewportWidth * 0.22),
|
|
|
|
|
12 * rootFontSize,
|
|
|
|
|
);
|
|
|
|
|
const headerWidth = lerp(expandedWidth, viewportWidth, progress);
|
|
|
|
|
const headerOffset = -(headerWidth - expandedWidth) / 2;
|
|
|
|
|
const inlinePadding = lerp(0, rootFontSize, progress);
|
|
|
|
|
const compactSiteNavWidth = Math.max(
|
|
|
|
|
viewportWidth -
|
|
|
|
|
2 * rootFontSize -
|
|
|
|
|
compactLogoWidth -
|
|
|
|
|
7.5 * rootFontSize,
|
|
|
|
|
0,
|
|
|
|
|
);
|
|
|
|
|
const compactNavGap = Math.min(
|
|
|
|
|
Math.max(0.75 * rootFontSize, viewportWidth * 0.02),
|
|
|
|
|
1.5 * rootFontSize,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const values: Record<string, string> = {
|
|
|
|
|
"--header-width": `${headerWidth}px`,
|
|
|
|
|
"--header-offset": `${headerOffset}px`,
|
|
|
|
|
"--header-inline-padding": `${inlinePadding}px`,
|
|
|
|
|
"--header-surface-weight": `${lerp(0, 94, progress)}%`,
|
|
|
|
|
"--header-blur": `${lerp(0, 10, progress)}px`,
|
|
|
|
|
"--theme-top": `${lerp(0, 0.85 * rootFontSize, progress)}px`,
|
|
|
|
|
"--theme-right": `${inlinePadding}px`,
|
|
|
|
|
"--header-gap": `${lerp(rootFontSize, 0, progress)}px`,
|
|
|
|
|
"--header-nav-height": `${lerp(expandedNavHeight, 3 * rootFontSize, progress)}px`,
|
|
|
|
|
"--header-nav-y": `${lerp(0, 0.5 * rootFontSize, progress)}px`,
|
|
|
|
|
"--banner-width": `${lerp(expandedBannerWidth, compactLogoWidth, progress)}px`,
|
|
|
|
|
"--logo-height": `${lerp(55, 36, progress)}px`,
|
|
|
|
|
"--site-nav-width": `${lerp(expandedSiteNavWidth, compactSiteNavWidth, progress)}px`,
|
|
|
|
|
"--site-nav-margin": `${lerp(rootFontSize, 0, progress)}px`,
|
|
|
|
|
"--site-nav-padding": `${lerp(rootFontSize, 0, progress)}px`,
|
|
|
|
|
"--site-nav-x": `${lerp(0, compactLogoWidth + 1.5 * rootFontSize, progress)}px`,
|
|
|
|
|
"--site-nav-y": `${lerp(0, -36, progress)}px`,
|
|
|
|
|
"--site-nav-gap": `${lerp(1.5 * rootFontSize, compactNavGap, progress)}px`,
|
|
|
|
|
"--site-nav-font-size": `${lerp(1.2 * rootFontSize, 1.05 * rootFontSize, progress)}px`,
|
|
|
|
|
"--divider-margin-top": `${lerp(0.2 * rootFontSize, 0.45 * rootFontSize, progress)}px`,
|
|
|
|
|
"--divider-margin-bottom": `${lerp(2 * rootFontSize, 0, progress)}px`,
|
|
|
|
|
"--divider-inner-margin-bottom": `${lerp(rootFontSize, 0, progress)}px`,
|
|
|
|
|
"--divider-width": `${headerWidth}px`,
|
|
|
|
|
"--divider-offset": `${-inlinePadding}px`,
|
|
|
|
|
"--divider-y": `${lerp(0, -0.1 * rootFontSize, progress)}px`,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Object.entries(values).forEach(([property, value]) => {
|
|
|
|
|
header.style.setProperty(property, value);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-17 04:12:43 +00:00
|
|
|
function updateScrollVisibility(
|
|
|
|
|
slot: HTMLElement | null,
|
|
|
|
|
currentScrollY: number,
|
|
|
|
|
) {
|
|
|
|
|
if (!slot) return;
|
|
|
|
|
|
|
|
|
|
const delta = currentScrollY - lastScrollY;
|
|
|
|
|
lastScrollY = currentScrollY;
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!desktopHeaderQuery.matches ||
|
|
|
|
|
currentScrollY <= HEADER_SCROLL_DISTANCE
|
|
|
|
|
) {
|
|
|
|
|
slot.classList.remove("is-scroll-hidden");
|
|
|
|
|
scrollDirection = 0;
|
|
|
|
|
directionDistance = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Math.abs(delta) < 0.5) return;
|
|
|
|
|
|
|
|
|
|
const nextDirection = Math.sign(delta);
|
|
|
|
|
if (nextDirection !== scrollDirection) {
|
|
|
|
|
scrollDirection = nextDirection;
|
|
|
|
|
directionDistance = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
directionDistance += Math.abs(delta);
|
|
|
|
|
if (directionDistance < DIRECTION_TRIGGER_DISTANCE) return;
|
|
|
|
|
|
|
|
|
|
slot.classList.toggle("is-scroll-hidden", scrollDirection > 0);
|
|
|
|
|
directionDistance = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-17 04:05:55 +00:00
|
|
|
function requestHeaderUpdate() {
|
|
|
|
|
if (headerFrame) return;
|
|
|
|
|
|
|
|
|
|
headerFrame = window.requestAnimationFrame(() => {
|
|
|
|
|
headerFrame = 0;
|
|
|
|
|
updateHeaderState();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function bindFooterDividerObserver() {
|
|
|
|
|
footerDividerObserver?.disconnect();
|
|
|
|
|
footerDividerObserver = null;
|
|
|
|
|
|
|
|
|
|
const slot = document.querySelector<HTMLElement>(".header-slot");
|
|
|
|
|
const footerDivider = document.querySelector<HTMLElement>(
|
|
|
|
|
"[data-footer-divider]",
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!slot) return;
|
|
|
|
|
|
2026-09-17 04:12:43 +00:00
|
|
|
slot.classList.remove("is-footer-hidden");
|
2026-09-17 04:05:55 +00:00
|
|
|
|
|
|
|
|
if (!desktopHeaderQuery.matches || !footerDivider) return;
|
|
|
|
|
|
|
|
|
|
footerDividerObserver = new IntersectionObserver(([entry]) => {
|
2026-09-17 04:12:43 +00:00
|
|
|
slot.classList.toggle("is-footer-hidden", entry.isIntersecting);
|
2026-09-17 04:05:55 +00:00
|
|
|
});
|
|
|
|
|
footerDividerObserver.observe(footerDivider);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.addEventListener("scroll", requestHeaderUpdate, { passive: true });
|
|
|
|
|
window.addEventListener("resize", () => {
|
|
|
|
|
headerMetrics = null;
|
|
|
|
|
requestHeaderUpdate();
|
|
|
|
|
});
|
|
|
|
|
desktopHeaderQuery.addEventListener("change", () => {
|
|
|
|
|
requestHeaderUpdate();
|
|
|
|
|
bindFooterDividerObserver();
|
|
|
|
|
});
|
|
|
|
|
document.addEventListener("astro:page-load", () => {
|
|
|
|
|
headerMetrics = null;
|
2026-09-17 04:12:43 +00:00
|
|
|
lastScrollY = window.scrollY;
|
|
|
|
|
scrollDirection = 0;
|
|
|
|
|
directionDistance = 0;
|
|
|
|
|
document
|
|
|
|
|
.querySelector<HTMLElement>(".header-slot")
|
|
|
|
|
?.classList.remove("is-scroll-hidden");
|
2026-09-17 04:05:55 +00:00
|
|
|
updateHeaderState();
|
|
|
|
|
bindFooterDividerObserver();
|
|
|
|
|
});
|
|
|
|
|
document.fonts.ready.then(() => {
|
|
|
|
|
headerMetrics = null;
|
|
|
|
|
requestHeaderUpdate();
|
|
|
|
|
});
|
|
|
|
|
updateHeaderState();
|
|
|
|
|
bindFooterDividerObserver();
|
|
|
|
|
</script>
|