Component library
Back to catalog

Expanding glass header

A dark glass capsule floating over the hero that grows into its own mega panel: the open section fills with the accent, its chevron rolls, and the panel height glides between sections. Panels hold columns of graded-icon rows, an optional lead row and up to three photo cards. Tucks away on scroll down, returns on scroll up; below the computed width it becomes a sheet of accordions inside the same capsule.

headernavigationmega menuglassdarkfloatinghide on scrolllocal servicepremium

Open preview in a new tab

Use this component

Three ways to get Expanding glass header into one of your own projects. All pre-filled with this component's name and library page.

Or, for the design alone

Placement prompt

What to do, where it goes and what must not change. Paste it into Claude Code inside the destination project.

Read it first
Add Expanding glass header from my Astro Component Library to this Astro project.

Library page, for reference only:
https://astro.baysixmedia.com/components/expanding-glass-header/

Place it in:
[ENTER PAGE, FILE, OR ROUTE]

Position:
[ENTER WHERE IT SHOULD APPEAR]

First read the component source, README, documented props, dependencies, and required assets from the library page. Copy the component and every required companion asset locally into this project. Do not import files from the deployed library at runtime.

Adapt its demo content, links, colors, typography, and images to this project through documented props where possible. Preserve its responsive behavior, accessibility, interaction logic, reduced-motion behavior, and support for multiple instances. Avoid changing unrelated project code.

Test it at desktop, tablet, and mobile widths. Run the project's available check and build commands. Report the files changed and any differences from the library version. Do not commit or deploy.

--- VERIFIED IN THE COMPONENT LIBRARY ---
Enrolled for verification but never run.
No verification run has been recorded.
No checks have passed yet.
This record pre-dates source fingerprinting, so it is not tied to a specific revision.

--- NOT PROVEN ---
- ClientRouter: event-tested only. Handlers respond to dispatched lifecycle events; this has NOT been certified against a real <ClientRouter /> transition.

--- WHAT THIS DOES NOT CERTIFY ---
Verification was performed on the unmodified component, in an isolated fixture, in the
library. It says nothing about this project's CSS, routes, layout or data, and nothing
about the component once you adapt it. A verified component is a good starting point,
not a guarantee about the installation you are about to build.

--- TEST IN THIS PROJECT AFTER INSTALLING OR CUSTOMISING ---
- Horizontal overflow at 1440, 1280, 1024, 834, 390 and 320px.
- Keyboard operation end to end, including visible focus.
- The no-JavaScript render.
- prefers-reduced-motion: reduce.
- Two instances on one page, if this project renders more than one.
- If this project uses <ClientRouter />: teardown and reinitialisation across a real navigation.

Component reference

A fragment for a request you write yourself, e.g. “I want to add [paste] to Section 3 of my homepage.” It names the component, links its page, and fixes what must not change — the design build and its effects — while leaving branding and content open. It does not carry the code.

Read it first
“Expanding glass header” from my Astro Component Library:
https://astro.baysixmedia.com/components/expanding-glass-header/

Use the existing library component as the implementation source. Preserve its design structure, layout, proportions, spacing system, responsive behavior, visual effects, animations, transitions, hover states, interactions, and accessibility.

The copy, colors, typography, images, and other content may be adapted to match the destination website. Do not redesign, simplify, or reinterpret the component’s underlying design build or effects.

src/components/library/ExpandingGlassHeader.astro

Installing this one is more than pasting a section

  • installOne required file, plus supporting files worth taking:
    • src/components/ExpandingGlassHeader.astrocomponentrequired
    • src/components/ExpandingGlassHeader.mdreadmesupporting
    Use Copy header kit above to get them all, each one labelled with the path it belongs at.
  • assetsCopy these across too — they are referenced by URL, so nothing in the source above points at them: public/expanding-glass-header/drill-decking.webp, public/expanding-glass-header/modern-house.webp, public/expanding-glass-header/white-house-lawn.webp. Leave them behind and those URLs resolve to nothing on the other end. What each component does without them is in its README.
---
/**
 * ExpandingGlassHeader (candidate, role `navigation`)
 *
 * A dark glass capsule floating at the top of the page that *becomes* its own dropdown. Hover or
 * press a section and the capsule itself grows downward into a solid panel — no separate flyout —
 * the chosen trigger fills with the accent, its chevron rolls from down to up, and the panel's
 * height glides between sections as the pointer moves along the bar. Panels hold columns of rows
 * (an optional graded icon tile, a title, one line under it), an optional lead "overview" row for
 * the section's own page, and up to three feature cards with a photo. The primary button's label
 * rolls up and is replaced by itself on hover. Scrolling down tucks the capsule away; scrolling up
 * brings it back.
 *
 * Below the width at which one line holds brand, sections and button (computed from the labels, so
 * the bar never wraps), the sections move into a sheet inside the same capsule: the capsule grows to
 * the viewport, each section is an accordion, and the button follows the list.
 *
 * The phone rides in the bar: a 44px tap-to-call button, or the number itself beside the button from the
 * width at which it fits; on the smallest phones the call button takes the button's place. A runtime
 * check backs the estimate: if a site face sets wider than planned, the number folds back to the
 * button, then the sections fold into the sheet — the bar never overlaps.
 *
 * Without JavaScript every section with a page is a plain link, and the menu is an anchor that opens
 * the sheet with every accordion expanded.
 *
 * Truth rules. Only supplied links render. A section with no rows is a plain link. Feature cards
 * render only with a title and a safe href; a photo only with a src. The call line and call button
 * render only from a real number.
 *
 * Reads, never declares: --egh-accent, --egh-accent-ink, --egh-panel, --egh-card, --egh-ink,
 * --egh-muted, --egh-family, --egh-mono, --egh-max, --egh-top.
 */
export type ExpandingGlassHeaderIcon =
	| "flame"
	| "snowflake"
	| "fan"
	| "wrench"
	| "shield"
	| "calendar"
	| "home"
	| "pin"
	| "star"
	| "users"
	| "clipboard"
	| "chat"
	| "drop"
	| "bolt"
	| "leaf"
	| "document";

export interface ExpandingGlassHeaderRow {
	label: string;
	href: string;
	description?: string;
	icon?: ExpandingGlassHeaderIcon;
	external?: boolean;
}

export interface ExpandingGlassHeaderGroup {
	heading?: string;
	links: readonly ExpandingGlassHeaderRow[];
}

export interface ExpandingGlassHeaderFeature {
	tag?: string;
	title: string;
	href: string;
	image?: { src: string; alt: string } | null;
}

export interface ExpandingGlassHeaderItem {
	label: string;
	href?: string;
	/** Wording for the lead row that links to the section's own page (`href`); the row renders whenever href does. */
	overview?: { title?: string; description?: string } | null;
	groups?: readonly ExpandingGlassHeaderGroup[];
	featureHeading?: string;
	features?: readonly ExpandingGlassHeaderFeature[];
}

interface Props {
	brandName?: string;
	brandHref?: string;
	logo?: { src: string; alt: string; width?: number; height?: number } | null;
	items?: readonly ExpandingGlassHeaderItem[];
	currentPath?: string;
	cta?: { label: string; href: string } | null;
	phone?: { number: string; label?: string } | null;
	/** Pull the next section up under the header so the capsule floats over it (a hero). */
	overlap?: boolean;
	/** Tuck the capsule away on scroll down and bring it back on scroll up. */
	hideOnScroll?: boolean;
	/** "glass" floats translucent over a dark hero; "solid" is an opaque capsule that reads on any page. */
	surface?: "glass" | "solid";
	navLabel?: string;
	menuLabel?: string;
	closeLabel?: string;
	fontStylesheet?: string;
	class?: string;
	id?: string;
}

const {
	brandName = "",
	brandHref,
	logo = null,
	items = [],
	currentPath,
	cta = null,
	phone = null,
	overlap = false,
	hideOnScroll = true,
	surface = "glass",
	navLabel = "Primary",
	menuLabel = "Menu",
	closeLabel = "Close menu",
	fontStylesheet = "https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600&family=Geist+Mono:wght@500&display=swap",
	class: className,
	id,
} = Astro.props;

const safeHref = (href: string | undefined | null) => {
	const h = (href ?? "").trim();
	return /^(https?:\/\/|mailto:|tel:|#|\/(?!\/))/i.test(h) ? h : null;
};
const norm = (p: string) => p.replace(/\/+$/, "") || "/";
const isCurrent = (href: string | null) => Boolean(currentPath && href && norm(currentPath) === norm(href));

const ICONS: Record<ExpandingGlassHeaderIcon, string> = {
	flame: "M12 3c.6 3 3.9 4.9 3.9 9a3.9 3.9 0 0 1-7.8 0c0-1.7.8-2.9 1.9-3.9.2 1.4.9 2.3 1.9 2.7C11.5 8.7 11 6 12 3Z M7.5 13.5A4.5 4.5 0 0 0 12 21a4.5 4.5 0 0 0 4.5-7.5",
	snowflake: "M12 3v18 M4.2 7.5l15.6 9 M4.2 16.5l15.6-9 M9.5 4.5 12 6.5l2.5-2 M9.5 19.5 12 17.5l2.5 2",
	fan: "M12 12c-1.5-3.5-1-7.5 2-8.5 2.5-.8 4 1.5 3 3.5-.9 1.8-3 3.4-5 5Z M12 12c3.7-.5 7.1 1.7 6.5 4.8-.5 2.6-3.2 2.8-4.5 1-1.2-1.6-1.6-4.1-2-5.8Z M12 12c-2.2 3-5.9 4.6-8.1 2.4-1.9-1.8-.6-4.2 1.6-4.3 2-.1 4.4.9 6.5 1.9Z M12 12h.01",
	wrench: "M14.5 5.5a4 4 0 0 0 4.9 4.9L10 19.8a2.1 2.1 0 0 1-3-3l9.4-9.4a4 4 0 0 0-1.9-1.9Z M14.5 5.5 17 3l1.5 1.5L21 3v0l-1.5 2.5L21 7l-2.5 2.5",
	shield: "M12 3 5 5.8v5.4c0 4.3 2.9 8.1 7 9.8 4.1-1.7 7-5.5 7-9.8V5.8L12 3Z M9 12l2 2 4-4",
	calendar: "M5 6h14v14H5z M5 10h14 M9 3.5V7 M15 3.5V7 M9 14h2 M13 14h2 M9 17h2",
	home: "M4 11 12 4l8 7 M6.5 9.5V20h11V9.5 M10 20v-5h4v5",
	pin: "M12 21s-6.5-5.8-6.5-11a6.5 6.5 0 0 1 13 0c0 5.2-6.5 11-6.5 11Z M12 12.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z",
	star: "m12 3.8 2.5 5.1 5.6.8-4 4 .9 5.5-5-2.6-5 2.6.9-5.5-4-4 5.6-.8L12 3.8Z",
	users: "M9 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7Z M2.5 20c.6-3.4 3.2-5.5 6.5-5.5s5.9 2.1 6.5 5.5 M16 4.3a3.5 3.5 0 0 1 0 6.4 M18 14.8c2 .7 3.2 2.4 3.5 5.2",
	clipboard: "M8 5H6v16h12V5h-2 M9 3.5h6v3H9z M9 11h6 M9 15h6 M9 18.5h3",
	chat: "M4 5h16v11H9l-5 4V5Z M8 9.5h8 M8 12.5h5",
	drop: "M12 3.5s6 6.3 6 10.5a6 6 0 0 1-12 0c0-4.2 6-10.5 6-10.5Z M9 14.5a3 3 0 0 0 3 3",
	bolt: "M13 3 5 13.5h6L10.5 21 19 10h-6l0-7Z",
	leaf: "M5 19c0-8 5-13.5 15-14-.4 9.5-5.5 14.5-13 14.5 M5 19c2.5-4 5.5-6.8 9-8.5",
	document: "M6 3h8.5L19 7.5V21H6V3Z M14 3v5h5 M9 12h7 M9 15.5h7 M9 19h4",
};

/* Sections: a section with rows is a trigger; one without is a plain link. */
const sections = items
	.map((item, i) => {
		const label = (item?.label ?? "").trim();
		const href = safeHref(item?.href);
		const groups = (item?.groups ?? [])
			.map((g) => {
				const rows = (g?.links ?? [])
					.map((r) => ({
						label: (r?.label ?? "").trim(),
						href: safeHref(r?.href),
						description: (r?.description ?? "").trim(),
						icon: r?.icon && ICONS[r.icon] ? r.icon : null,
						external: Boolean(r?.external),
					}))
					.filter((r): r is typeof r & { href: string } => Boolean(r.label && r.href));
				const tiled = rows.some((r) => r.icon);
				/* The tiles step from dark to light down each column. */
				return { heading: (g?.heading ?? "").trim(), tiled, rows: rows.map((r, j) => ({ ...r, shade: rows.length > 1 ? Math.round((j / (rows.length - 1)) * 100) : 0 })) };
			})
			.filter((g) => g.rows.length > 0);
		const features = (item?.features ?? [])
			.map((f) => ({ tag: (f?.tag ?? "").trim(), title: (f?.title ?? "").trim(), href: safeHref(f?.href), image: f?.image?.src ? { src: f.image.src, alt: f.image.alt ?? "" } : null }))
			.filter((f): f is typeof f & { href: string } => Boolean(f.title && f.href))
			.slice(0, 3);
		/* The section's own page always leads its panel (a trigger is a button, not a link); overview only words it. */
		const overview = href && (groups.length || features.length) ? { title: (item?.overview?.title ?? "").trim() || label, description: (item?.overview?.description ?? "").trim() } : null;
		const hasPanel = groups.length > 0 || features.length > 0;
		const panelHrefs = [href, ...groups.flatMap((g) => g.rows.map((r) => r.href)), ...features.map((f) => f.href)];
		return {
			key: `${id ?? "egh"}-s${i}`,
			label,
			href,
			groups,
			features,
			featureHeading: (item?.featureHeading ?? "").trim(),
			overview,
			hasPanel,
			current: panelHrefs.some((h) => isCurrent(h)),
		};
	})
	.filter((s) => s.label && (s.hasPanel || s.href));

const action = cta && cta.label?.trim() && safeHref(cta.href) ? { label: cta.label.trim(), href: safeHref(cta.href)! } : null;
const digits = (phone?.number ?? "").replace(/[^\d+]/g, "");
const call = phone && phone.number?.trim() && digits.replace(/\D/g, "").length >= 7 ? { number: phone.number.trim(), label: (phone.label ?? "Call").trim(), href: `tel:${digits}` } : null;
const name = brandName.trim();
const home = safeHref(brandHref);

/*
 * The one-line budget, in px at the default type (16px Geist sections; a 21/18/15.5px wordmark by the
 * name's length), calibrated on the rendered bar: 8.4px per section character, 22px padding, 18px for a
 * chevron, 8px between sections. The sections centre in the capsule while there is room and give up the
 * centre before they give up a line, so the need is the plain sum. 5% margin for a wider site face.
 */
const nameSize = name.length <= 16 ? "l" : name.length <= 26 ? "m" : "s";
const perChar = { l: 10.6, m: 9.1, s: 7.9 }[nameSize];
const brandW = logo?.src ? Math.min(180, Math.round(((logo.width ?? 160) / (logo.height ?? 40)) * 26)) + 18 : Math.max(60, Math.min(name.length, nameSize === "s" ? 22 : 99) * perChar) + 18;
const navAt = (perLabel: number, pad: number, chev: number, gap: number) => sections.reduce((w, s) => w + s.label.length * perLabel + pad + (s.hasPanel ? chev : 0), 0) + Math.max(0, sections.length - 1) * gap;
const ctaW = action ? action.label.length * 8.3 + 52 : 0;
/* Compact density (15px sections, tighter padding) only when the regular bar would not fit even at 1440. */
const regular = navAt(8.4, 22, 18, 8);
const compact = navAt(7.9, 16, 16, 2);
const density = (brandW + regular + ctaW + 57 + (call ? 54 : 0)) * 1.05 > 1139 && (brandW + compact + ctaW + 57 + (call ? 54 : 0)) * 1.05 <= 1139 ? "compact" : "regular";
const navW = density === "compact" ? compact : regular;
/*
 * The phone costs a 44px call button; the fuller call line ("Call (650) 555-0148") is used instead only
 * when it also fits at the width the sections go inline — the sections never give way to it.
 */
const base = brandW + navW + ctaW + 32 + 25;
const need = (base + (call ? 54 : 0)) * 1.05;
/* Capsule content width at each allowed step: viewport − 40px frame − 25px capsule padding, capped at the 1164px capsule. */
const STEPS = [960, 1024, 1120, 1200, 1280, 1440] as const;
const inner = (w: number) => Math.min(w - 40, 1164) - 25;
const inlineAt = sections.length ? (STEPS.find((w) => inner(w) >= need) ?? "never") : 0;
/* The call line (glyph + number) replaces the call button from the first step at which it also fits. */
const lineW = call ? call.number.length * 7.6 + 36 : 0;
const lineAt = !call ? null : inlineAt === 0 ? 0 : inlineAt === "never" ? null : (STEPS.find((w) => w >= inlineAt && inner(w) >= (base + lineW) * 1.05) ?? null);
const hasSheet = sections.length > 0;
const sheetId = `${id ?? "egh"}-sheet`;
---

{(name || logo?.src || sections.length > 0) && (
	<header
		class:list={["egh", className]}
		id={id}
		data-inline={String(inlineAt)}
		data-name={nameSize}
		data-density={density}
		data-overlap={overlap ? "" : undefined}
		data-hide={hideOnScroll ? "" : undefined}
		data-surface={surface === "solid" ? "solid" : "glass"}
		data-call={call ? "" : undefined}
		data-line={lineAt === null ? undefined : String(lineAt)}
	>
		{fontStylesheet && <link rel="stylesheet" href={fontStylesheet} />}
		<div class="egh-frame">
			<div class="egh-shell">
				<span class="egh-stroke egh-stroke--top" aria-hidden="true"></span>
				<span class="egh-stroke egh-stroke--bottom" aria-hidden="true"></span>

				<div class="egh-bar">
					{home ? (
						<a class="egh-brand" href={home}>
							{logo?.src ? <img class="egh-logo" src={logo.src} alt={logo.alt || name} width={logo.width} height={logo.height} /> : <span class="egh-wordmark">{name}</span>}
						</a>
					) : (
						<span class="egh-brand">
							{logo?.src ? <img class="egh-logo" src={logo.src} alt={logo.alt || name} width={logo.width} height={logo.height} /> : <span class="egh-wordmark">{name}</span>}
						</span>
					)}

					{sections.length > 0 && (
						<nav class="egh-nav" aria-label={navLabel}>
							<ul role="list">
								{sections.map((s) => (
									<li>
										{s.hasPanel && s.href && (
											<a class="egh-trigger egh-nojs" href={s.href} aria-current={isCurrent(s.href) ? "page" : undefined} data-current={s.current ? "" : undefined}>
												<span>{s.label}</span>
											</a>
										)}
										{s.hasPanel ? (
											<button class:list={["egh-trigger", { "egh-js": Boolean(s.href) }]} type="button" aria-expanded="false" aria-controls={`${s.key}-panel`} data-section={s.key} data-current={s.current ? "" : undefined}>
												<span>{s.label}</span>
												<span class="egh-chev" aria-hidden="true">
													<span class="egh-chev-track">
														<svg viewBox="0 0 12 12" width="12" height="12"><path d="M3 7.5 6 4.5l3 3" /></svg>
														<svg viewBox="0 0 12 12" width="12" height="12"><path d="M3 4.5 6 7.5l3-3" /></svg>
													</span>
												</span>
											</button>
										) : (
											<a class="egh-trigger" href={s.href} aria-current={isCurrent(s.href) ? "page" : undefined}>
												<span>{s.label}</span>
											</a>
										)}
									</li>
								))}
							</ul>
						</nav>
					)}

					<div class="egh-actions">
						{call && (
							<a class="egh-call-line" href={call.href}>
								<svg aria-hidden="true" viewBox="0 0 20 20" width="16" height="16"><path d="M6.2 2.8 8 6.6 6.3 8.2c.9 2 2.5 3.6 4.5 4.5l1.6-1.7 3.8 1.8-.6 3.2c-.1.5-.5.8-1 .8C8.4 16.6 3.4 11.6 2.2 5.4c-.1-.5.3-.9.8-1Z" /></svg>
								<span class="egh-sr">{call.label} </span>
								<span class="egh-call-number">{call.number}</span>
							</a>
						)}
						{call && (
							<a class="egh-call-icon" href={call.href} aria-label={`${call.label} ${call.number}`}>
								<svg aria-hidden="true" viewBox="0 0 20 20" width="18" height="18"><path d="M6.2 2.8 8 6.6 6.3 8.2c.9 2 2.5 3.6 4.5 4.5l1.6-1.7 3.8 1.8-.6 3.2c-.1.5-.5.8-1 .8C8.4 16.6 3.4 11.6 2.2 5.4c-.1-.5.3-.9.8-1Z" /></svg>
							</a>
						)}
						{action && (
							<a class="egh-cta" href={action.href}>
								<span class="egh-roll">
									<span>{action.label}</span>
									<span aria-hidden="true">{action.label}</span>
								</span>
							</a>
						)}
						{hasSheet && (
							<>
								<span class="egh-divider" aria-hidden="true"></span>
								<a class="egh-menu egh-nojs" href={`#${sheetId}`} aria-label={menuLabel}>
									<span class="egh-burger" aria-hidden="true"><span></span><span></span><span></span></span>
								</a>
								<button class="egh-menu egh-js" type="button" aria-expanded="false" aria-controls={sheetId} data-open-label={closeLabel} data-closed-label={menuLabel} aria-label={menuLabel}>
									<span class="egh-burger" aria-hidden="true"><span></span><span></span><span></span></span>
								</button>
							</>
						)}
					</div>
				</div>

				{/* Desktop panels share one stage; its height follows the open panel. */}
				{sections.some((s) => s.hasPanel) && (
					<div class="egh-stage">
						{sections
							.filter((s) => s.hasPanel)
							.map((s) => (
								<div class="egh-panel" id={`${s.key}-panel`} data-section={s.key} role="group" aria-label={s.label}>
									<div class="egh-panel-grid" style={`--g:${s.groups.length || 0};--f:${s.features.length}`} data-f={String(s.features.length)} data-g={String(s.groups.length)}>
										{(s.overview || s.groups.length > 0) && (
											<div class="egh-groups">
												{s.overview && (
													<a class="egh-lead" href={s.href!} aria-current={isCurrent(s.href) ? "page" : undefined}>
														<span class="egh-lead-tile" aria-hidden="true">
															<svg viewBox="0 0 24 24" width="26" height="26"><path d="M4 12h16 M12 4v16 M6.3 6.3l11.4 11.4 M17.7 6.3 6.3 17.7" /><circle cx="12" cy="12" r="4.2" /></svg>
														</span>
														<span class="egh-row-text">
															<span class="egh-row-title">
																{s.overview.title}
																<svg class="egh-lead-arrow" aria-hidden="true" viewBox="0 0 16 16" width="14" height="14"><path d="M3 8h9.5 M8.5 4l4 4-4 4" /></svg>
															</span>
															{s.overview.description && <span class="egh-row-desc">{s.overview.description}</span>}
														</span>
													</a>
												)}
												<div class="egh-columns">
													{s.groups.map((g) => (
														<div class="egh-group">
															{g.heading && <p class="egh-heading">{g.heading}</p>}
															<ul role="list" class:list={["egh-rows", { "egh-rows--tiled": g.tiled }]}>
																{g.rows.map((r) => (
																	<li>
																		<a
																			class="egh-row"
																			href={r.href}
																			aria-current={isCurrent(r.href) ? "page" : undefined}
																			target={r.external ? "_blank" : undefined}
																			rel={r.external ? "noopener noreferrer" : undefined}
																		>
																			{g.tiled && (
																				<span class="egh-tile" style={`--t:${r.shade}%`} aria-hidden="true">
																					{r.icon && (
																						<svg viewBox="0 0 24 24" width="24" height="24">
																							<path d={ICONS[r.icon]} />
																						</svg>
																					)}
																				</span>
																			)}
																			<span class="egh-row-text">
																				<span class="egh-row-title">{r.label}</span>
																				{r.description && <span class="egh-row-desc">{r.description}</span>}
																			</span>
																		</a>
																	</li>
																))}
															</ul>
														</div>
													))}
												</div>
											</div>
										)}
										{s.features.length > 0 && (
											<div class="egh-features">
												{s.featureHeading && <p class="egh-heading">{s.featureHeading}</p>}
												<ul role="list" class="egh-cards">
													{s.features.map((f) => (
														<li>
															<a class="egh-card" href={f.href} aria-current={isCurrent(f.href) ? "page" : undefined}>
																<span class="egh-card-media">
																	{f.image ? <img src={f.image.src} alt={f.image.alt} loading="lazy" decoding="async" /> : <span class="egh-card-blank" aria-hidden="true"></span>}
																	{f.tag && <span class="egh-card-tag">{f.tag}</span>}
																</span>
																<span class="egh-card-title">{f.title}</span>
																<svg class="egh-card-arrow" aria-hidden="true" viewBox="0 0 16 16" width="16" height="16"><path d="M3 8h9.5 M8.5 4l4 4-4 4" /></svg>
															</a>
														</li>
													))}
												</ul>
											</div>
										)}
									</div>
								</div>
							))}
					</div>
				)}

				{/* Small screens: the same sections as accordions in a sheet inside the capsule. */}
				{hasSheet && (
					<div class="egh-drawer" id={sheetId}>
						<nav class="egh-sheet" aria-label={navLabel}>
							<ul role="list" class="egh-sheet-list">
								{sections.map((s) => (
									<li class="egh-acc">
										{s.hasPanel ? (
											<>
												<button class="egh-acc-trigger" type="button" aria-expanded="false" aria-controls={`${s.key}-acc`} data-current={s.current ? "" : undefined}>
													<span>{s.label}</span>
													<span class="egh-chev" aria-hidden="true">
														<span class="egh-chev-track">
															<svg viewBox="0 0 12 12" width="12" height="12"><path d="M3 7.5 6 4.5l3 3" /></svg>
															<svg viewBox="0 0 12 12" width="12" height="12"><path d="M3 4.5 6 7.5l3-3" /></svg>
														</span>
													</span>
												</button>
												<div class="egh-acc-region" id={`${s.key}-acc`}>
													<div class="egh-acc-inner">
														{s.overview && (
															<a class="egh-lead" href={s.href!} aria-current={isCurrent(s.href) ? "page" : undefined}>
																<span class="egh-lead-tile" aria-hidden="true">
																	<svg viewBox="0 0 24 24" width="24" height="24"><path d="M4 12h16 M12 4v16 M6.3 6.3l11.4 11.4 M17.7 6.3 6.3 17.7" /><circle cx="12" cy="12" r="4.2" /></svg>
																</span>
																<span class="egh-row-text">
																	<span class="egh-row-title">{s.overview.title}</span>
																	{s.overview.description && <span class="egh-row-desc">{s.overview.description}</span>}
																</span>
															</a>
														)}
														{s.groups.map((g) => (
															<div class="egh-group">
																{g.heading && <p class="egh-heading">{g.heading}</p>}
																<ul role="list" class:list={["egh-rows", { "egh-rows--tiled": g.tiled }]}>
																	{g.rows.map((r) => (
																		<li>
																			<a
																				class="egh-row"
																				href={r.href}
																				aria-current={isCurrent(r.href) ? "page" : undefined}
																				target={r.external ? "_blank" : undefined}
																				rel={r.external ? "noopener noreferrer" : undefined}
																			>
																				{g.tiled && (
																					<span class="egh-tile" style={`--t:${r.shade}%`} aria-hidden="true">
																						{r.icon && (
																							<svg viewBox="0 0 24 24" width="22" height="22">
																								<path d={ICONS[r.icon]} />
																							</svg>
																						)}
																					</span>
																				)}
																				<span class="egh-row-text">
																					<span class="egh-row-title">{r.label}</span>
																					{r.description && <span class="egh-row-desc">{r.description}</span>}
																				</span>
																			</a>
																		</li>
																	))}
																</ul>
															</div>
														))}
														{s.features.length > 0 && (
															<div class="egh-group">
																{s.featureHeading && <p class="egh-heading">{s.featureHeading}</p>}
																<ul role="list" class="egh-rows">
																	{s.features.map((f) => (
																		<li>
																			<a class="egh-row egh-row--feature" href={f.href}>
																				<span class="egh-row-text">
																					{f.tag && <span class="egh-row-tag">{f.tag}</span>}
																					<span class="egh-row-title">{f.title}</span>
																				</span>
																			</a>
																		</li>
																	))}
																</ul>
															</div>
														)}
													</div>
												</div>
											</>
										) : (
											<a class="egh-acc-trigger" href={s.href} aria-current={isCurrent(s.href) ? "page" : undefined}>
												<span>{s.label}</span>
											</a>
										)}
									</li>
								))}
							</ul>
							{(action || call) && (
								<div class="egh-sheet-foot">
									{action && (
										<a class="egh-cta egh-cta--block" href={action.href}>
											<span class="egh-roll">
												<span>{action.label}</span>
												<span aria-hidden="true">{action.label}</span>
											</span>
										</a>
									)}
									{call && (
										<a class="egh-call" href={call.href}>
											<span class="egh-call-label">{call.label}</span>
											<span>{call.number}</span>
										</a>
									)}
								</div>
							)}
						</nav>
					</div>
				)}
			</div>
		</div>
	</header>
)}

<style>
	.egh {
		--egh-a: var(--egh-accent, #d98f5b);
		--egh-a-ink: var(--egh-accent-ink, #121213);
		--egh-solid: var(--egh-panel, #1d1d1f);
		--egh-raised: var(--egh-card, #29292c);
		--egh-text: var(--egh-ink, #ffffff);
		--egh-soft: var(--egh-muted, #9ba6b4);
		--egh-font: var(--egh-family, "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif);
		--egh-font-mono: var(--egh-mono, "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, monospace);
		--egh-cap: var(--egh-max, 1164px);
		--egh-pad-top: var(--egh-top, 30px);
		--egh-pad-x: 20px;
		--egh-bar-h: 54px;
		--egh-ease: cubic-bezier(0.32, 0.72, 0, 1);
		--egh-stage-h: 0px;
		--egh-drawer-h: 0px;
		position: sticky;
		top: 0;
		z-index: 60;
		height: calc(var(--egh-pad-top) + var(--egh-bar-h));
		font-family: var(--egh-font);
		color: var(--egh-text);
		transition: transform 420ms var(--egh-ease);
	}
	.egh[data-overlap] {
		margin-bottom: calc(-1 * (var(--egh-pad-top) + var(--egh-bar-h)));
	}
	.egh.is-tucked {
		transform: translateY(calc(-100% - 12px));
	}
	.egh-frame {
		position: relative;
		height: 100%;
		padding: var(--egh-pad-top) var(--egh-pad-x) 0;
		box-sizing: border-box;
	}

	/* The capsule: glass at rest, solid once it opens. It grows downward over the page. */
	.egh-shell {
		position: absolute;
		top: var(--egh-pad-top);
		left: var(--egh-pad-x);
		right: var(--egh-pad-x);
		max-width: var(--egh-cap);
		margin-inline: auto;
		border-radius: 12px;
		/* Smoked glass: dark enough that white labels hold over a bright photo, clear enough to show the hero. */
		background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0) 70%), rgba(22, 22, 26, 0.52);
		-webkit-backdrop-filter: blur(14px) saturate(1.3);
		backdrop-filter: blur(14px) saturate(1.3);
		isolation: isolate;
		transition:
			background-color 380ms var(--egh-ease),
			box-shadow 380ms var(--egh-ease);
	}
	/* Solid: opaque from the start, for a page whose first section is light. */
	.egh[data-surface="solid"] .egh-shell {
		background: var(--egh-solid);
		box-shadow: 0 10px 30px -14px rgba(0, 0, 0, 0.45);
	}
	.egh.is-open .egh-shell {
		background: var(--egh-solid);
		box-shadow: 0 24px 60px -24px rgba(0, 0, 0, 0.65);
	}
	/* Two hairlines, each fading out toward the other edge: light at the top, a paler line at the bottom. */
	.egh-stroke {
		position: absolute;
		inset: 0;
		border-radius: inherit;
		border: 1px solid;
		pointer-events: none;
		z-index: 2;
	}
	.egh-stroke--top {
		border-color: rgba(255, 255, 255, 0.34);
		-webkit-mask-image: linear-gradient(0deg, transparent 10%, #000 100%);
		mask-image: linear-gradient(0deg, transparent 10%, #000 100%);
	}
	.egh-stroke--bottom {
		border-color: rgba(255, 255, 255, 0.12);
		-webkit-mask-image: linear-gradient(180deg, transparent 30%, #000 100%);
		mask-image: linear-gradient(180deg, transparent 30%, #000 100%);
	}

	/* Bar: brand · sections (centred) · button. */
	.egh-bar {
		position: relative;
		display: grid;
		grid-template-columns: minmax(0, 1fr) auto;
		align-items: center;
		gap: 12px;
		height: var(--egh-bar-h);
		padding: 7px;
		padding-left: 18px;
		box-sizing: border-box;
	}
	.egh-brand {
		display: inline-flex;
		align-items: center;
		justify-self: start;
		min-height: 40px;
		min-width: 0;
		color: var(--egh-text);
		text-decoration: none;
	}
	.egh-wordmark {
		font-size: 21px;
		font-weight: 500;
		letter-spacing: -0.035em;
		line-height: 1;
		white-space: nowrap;
	}
	.egh[data-name="m"] .egh-wordmark {
		font-size: 18px;
	}
	.egh[data-name="s"] .egh-wordmark {
		font-size: 15.5px;
		white-space: normal;
		line-height: 1.1;
	}
	.egh-logo {
		display: block;
		height: 26px;
		width: auto;
		max-width: 180px;
		object-fit: contain;
	}

	.egh-nav {
		display: none;
	}
	.egh[data-density="compact"] .egh-nav ul {
		gap: 2px;
	}
	.egh[data-density="compact"] .egh-trigger {
		padding-inline: 8px;
		font-size: 15px;
	}
	.egh-nav ul {
		display: flex;
		align-items: center;
		gap: 8px;
		margin: 0;
		padding: 0;
		list-style: none;
	}
	.egh-trigger {
		display: inline-flex;
		align-items: center;
		gap: 6px;
		height: 34px;
		padding: 6px 11px;
		border: 0;
		border-radius: 8px;
		background: transparent;
		color: var(--egh-text);
		font: inherit;
		font-size: 16px;
		line-height: 22px;
		letter-spacing: -0.02em;
		text-decoration: none;
		white-space: nowrap;
		cursor: pointer;
		transition:
			background-color 300ms var(--egh-ease),
			color 300ms var(--egh-ease);
	}
	a.egh-trigger:hover {
		background: rgba(255, 255, 255, 0.08);
	}
	.egh-trigger[aria-expanded="true"] {
		background: var(--egh-a);
		color: var(--egh-a-ink);
	}
	/* The section holding the current page: a small accent dot under its label. */
	.egh-trigger {
		position: relative;
	}
	.egh-trigger[aria-current="page"]::after,
	.egh-trigger[data-current]::after {
		content: "";
		position: absolute;
		left: 50%;
		bottom: -3px;
		width: 4px;
		height: 4px;
		margin-left: -2px;
		border-radius: 50%;
		background: var(--egh-a);
		transition: opacity 200ms linear;
	}
	.egh-trigger[aria-expanded="true"]::after {
		opacity: 0;
	}

	/* Chevron roll: the up chevron waits above the clip and slides in as the down one leaves. */
	.egh-chev {
		display: inline-block;
		width: 12px;
		height: 12px;
		overflow: hidden;
		flex: none;
	}
	.egh-chev-track {
		display: flex;
		flex-direction: column;
		transform: translateY(-12px);
		transition: transform 380ms var(--egh-ease);
	}
	.egh-chev svg {
		display: block;
		flex: none;
		fill: none;
		stroke: currentColor;
		stroke-width: 1.3;
		stroke-linecap: round;
		stroke-linejoin: round;
		opacity: 0.9;
	}
	[aria-expanded="true"] > .egh-chev .egh-chev-track {
		transform: translateY(0);
	}

	.egh-actions {
		display: flex;
		align-items: center;
		justify-content: flex-end;
		justify-self: end;
		gap: 10px;
		min-width: 0;
	}

	/* Phone: a quiet call line once the sections are inline; a tap-to-call button below that. */
	.egh-sr {
		position: absolute;
		width: 1px;
		height: 1px;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
	}
	.egh-call-line {
		display: none;
		align-items: center;
		gap: 8px;
		min-height: 44px;
		padding: 0 8px;
		color: var(--egh-text);
		font-size: 15px;
		letter-spacing: -0.01em;
		text-decoration: none;
		white-space: nowrap;
		font-variant-numeric: tabular-nums;
	}
	.egh-call-line svg {
		flex: none;
		fill: none;
		stroke: var(--egh-a);
		stroke-width: 1.6;
		stroke-linejoin: round;
	}
	.egh-call-line:hover .egh-call-number {
		text-decoration: underline;
		text-underline-offset: 4px;
	}
	.egh-call-icon {
		display: inline-grid;
		place-items: center;
		width: 44px;
		height: 44px;
		margin-block: -2px;
		border-radius: 8px;
		color: var(--egh-text);
		transition: background-color 220ms linear;
	}
	.egh-call-icon:hover {
		background: rgba(255, 255, 255, 0.1);
	}
	.egh-call-icon svg {
		fill: none;
		stroke: currentColor;
		stroke-width: 1.5;
		stroke-linejoin: round;
	}

	/* Primary button: the label rolls up and is replaced by itself. */
	.egh-cta {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		height: 40px;
		padding: 0 26px;
		border-radius: 8px;
		background: #ffffff;
		color: #111112;
		font-size: 16px;
		letter-spacing: -0.02em;
		text-decoration: none;
		white-space: nowrap;
		transition: background-color 380ms var(--egh-ease);
	}
	.egh-roll {
		display: flex;
		flex-direction: column;
		gap: 8px;
		height: 22px;
		line-height: 22px;
		overflow: hidden;
	}
	.egh-roll > span {
		display: block;
		transition: transform 420ms var(--egh-ease);
	}
	.egh-cta:hover {
		background: #f2f2f2;
	}
	.egh-cta:hover .egh-roll > span {
		transform: translateY(-30px);
	}

	.egh-divider {
		width: 1px;
		height: 36px;
		background: rgba(255, 255, 255, 0.16);
	}
	.egh-menu {
		position: relative;
		display: inline-grid;
		place-items: center;
		width: 44px;
		height: 44px;
		margin-block: -2px;
		padding: 0;
		border: 0;
		border-radius: 8px;
		background: transparent;
		color: var(--egh-text);
		cursor: pointer;
	}
	.egh-burger {
		position: relative;
		width: 20px;
		height: 14px;
	}
	.egh-burger span {
		position: absolute;
		left: 0;
		width: 20px;
		height: 1.5px;
		border-radius: 1px;
		background: currentColor;
		transition:
			transform 360ms var(--egh-ease),
			opacity 200ms linear,
			top 360ms var(--egh-ease);
	}
	.egh-burger span:nth-child(1) {
		top: 0;
	}
	.egh-burger span:nth-child(2) {
		top: 6.25px;
	}
	.egh-burger span:nth-child(3) {
		top: 12.5px;
	}
	.egh-menu[aria-expanded="true"] .egh-burger span:nth-child(1) {
		top: 6.25px;
		transform: rotate(45deg) scaleX(1.12);
	}
	.egh-menu[aria-expanded="true"] .egh-burger span:nth-child(2) {
		opacity: 0;
	}
	.egh-menu[aria-expanded="true"] .egh-burger span:nth-child(3) {
		top: 6.25px;
		transform: rotate(-45deg) scaleX(1.12);
	}

	/* Stage: the panels overlap in place; its height follows the open one, so switching sections glides. */
	.egh-stage {
		display: none;
		position: relative;
		height: var(--egh-stage-h);
		overflow: hidden;
		transition: height 440ms var(--egh-ease);
	}
	.egh-stage::before {
		content: "";
		position: absolute;
		inset: 0 0 auto;
		height: 1px;
		background: #2f2f32;
		opacity: 0;
		transition: opacity 200ms linear;
	}
	.egh.is-open .egh-stage::before {
		opacity: 1;
	}
	.egh-panel {
		position: absolute;
		inset: 0 0 auto;
		max-height: calc(100dvh - var(--egh-pad-top) - var(--egh-bar-h) - 24px);
		overflow-y: auto;
		overscroll-behavior: contain;
		scrollbar-width: thin;
		scrollbar-color: #3a3a3f transparent;
		padding: 20px 18px 22px;
		box-sizing: border-box;
		visibility: hidden;
		opacity: 0;
		transform: translateY(-6px);
		transition:
			opacity 200ms linear,
			transform 300ms var(--egh-ease),
			visibility 0s linear 200ms;
	}
	.egh-panel.is-active {
		visibility: visible;
		opacity: 1;
		transform: none;
		transition:
			opacity 260ms linear 90ms,
			transform 420ms var(--egh-ease) 60ms,
			visibility 0s;
	}

	.egh-panel-grid {
		display: grid;
		grid-template-columns: minmax(0, 1fr);
		gap: 24px;
	}
	.egh-panel-grid[data-f="1"] {
		grid-template-columns: minmax(0, 1fr) minmax(240px, 290px);
	}
	.egh-panel-grid[data-f="2"] {
		grid-template-columns: minmax(0, 1fr) minmax(0, 588px);
	}
	.egh-panel-grid[data-f="3"] {
		grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.9fr);
	}
	.egh-panel-grid[data-g="0"] {
		grid-template-columns: minmax(0, 1fr);
	}
	.egh-groups {
		display: grid;
		gap: 22px;
		align-content: start;
	}
	.egh-columns {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
		gap: 24px;
	}
	.egh-heading {
		margin: 0 0 20px;
		color: var(--egh-soft);
		font-family: var(--egh-font-mono);
		font-size: 12px;
		font-weight: 500;
		line-height: 1.1;
		letter-spacing: 0.02em;
		text-transform: uppercase;
	}
	.egh-rows {
		display: grid;
		gap: 20px;
		margin: 0;
		padding: 0;
		list-style: none;
	}
	.egh-rows--tiled {
		gap: 20px;
	}
	.egh-row,
	.egh-lead {
		position: relative;
		display: flex;
		align-items: center;
		gap: 15px;
		color: var(--egh-text);
		text-decoration: none;
		isolation: isolate;
	}
	/* Hover wash: a raised pill that reaches 8px past the row on every side. */
	.egh-row::before {
		content: "";
		position: absolute;
		inset: -8px;
		z-index: -1;
		border-radius: 8px;
		background: var(--egh-raised);
		opacity: 0;
		transition: opacity 220ms linear;
	}
	.egh-row:hover::before,
	.egh-row:focus-visible::before {
		opacity: 1;
	}
	.egh-row-text {
		display: grid;
		gap: 4px;
		min-width: 0;
	}
	.egh-row-title {
		display: inline-flex;
		align-items: center;
		gap: 8px;
		font-size: 16px;
		line-height: 22px;
		letter-spacing: -0.02em;
	}
	.egh-row-desc {
		color: var(--egh-soft);
		font-size: 14px;
		line-height: 21px;
	}
	.egh-row-tag {
		color: var(--egh-soft);
		font-family: var(--egh-font-mono);
		font-size: 11px;
		letter-spacing: 0.02em;
		text-transform: uppercase;
	}
	.egh-row[aria-current="page"] .egh-row-title,
	.egh-lead[aria-current="page"] .egh-row-title {
		color: var(--egh-a);
	}
	.egh-tile {
		display: grid;
		place-items: center;
		flex: none;
		width: 58px;
		height: 58px;
		border-radius: 6px;
		background:
			linear-gradient(180deg, rgba(255, 255, 255, 0.07), transparent 60%),
			color-mix(in srgb, color-mix(in srgb, var(--egh-a) 12%, #6b7684) var(--t, 0%), color-mix(in srgb, var(--egh-a) 16%, #3a4049));
		box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
		color: #ffffff;
	}
	.egh-tile svg,
	.egh-lead-tile svg {
		fill: none;
		stroke: currentColor;
		stroke-width: 1.4;
		stroke-linecap: round;
		stroke-linejoin: round;
	}

	/* Lead row: the section's own page, raised across the columns, with the accent tile. */
	.egh-lead {
		padding: 14px;
		border-radius: 6px;
		background: var(--egh-raised);
		transition: background-color 220ms linear;
	}
	.egh-lead:hover {
		background: #313135;
	}
	/* Title only: a slimmer lead row. */
	.egh-lead:not(:has(.egh-row-desc)) {
		padding: 10px 14px 10px 10px;
	}
	.egh-lead:not(:has(.egh-row-desc)) .egh-lead-tile {
		width: 40px;
		height: 40px;
	}
	.egh-lead-tile {
		display: grid;
		place-items: center;
		flex: none;
		width: 58px;
		height: 58px;
		border-radius: 6px;
		background: var(--egh-a);
		color: #ffffff;
	}
	.egh-lead-arrow {
		fill: none;
		stroke: currentColor;
		stroke-width: 1.4;
		stroke-linecap: round;
		stroke-linejoin: round;
		opacity: 0.6;
		transition:
			transform 320ms var(--egh-ease),
			opacity 220ms linear;
	}
	.egh-lead:hover .egh-lead-arrow {
		opacity: 1;
		transform: translateX(3px);
	}

	/* Feature cards: photo, optional tag, title, arrow. */
	.egh-cards {
		display: grid;
		grid-auto-flow: column;
		grid-auto-columns: minmax(0, 1fr);
		gap: 16px;
		margin: 0;
		padding: 0;
		list-style: none;
	}
	.egh-card {
		position: relative;
		display: flex;
		flex-direction: column;
		height: 100%;
		padding: 4px;
		border-radius: 6px;
		background: var(--egh-raised);
		color: var(--egh-text);
		text-decoration: none;
		box-sizing: border-box;
	}
	.egh-card-media {
		position: relative;
		display: block;
		aspect-ratio: 16 / 9;
		border-radius: 2px;
		overflow: hidden;
		background: #111112;
	}
	.egh-card-media img,
	.egh-card-blank {
		display: block;
		width: 100%;
		height: 100%;
		object-fit: cover;
		transition: transform 600ms var(--egh-ease);
	}
	.egh-card-blank {
		background:
			radial-gradient(120% 90% at 0% 50%, color-mix(in srgb, var(--egh-a) 55%, transparent), transparent 60%),
			#111112;
	}
	.egh-card:hover .egh-card-media img,
	.egh-card:hover .egh-card-blank {
		transform: scale(1.05);
	}
	.egh-card-tag {
		position: absolute;
		top: 12px;
		left: 12px;
		padding: 6px 8px;
		border-radius: 8px;
		background: rgba(255, 255, 255, 0.8);
		-webkit-backdrop-filter: blur(6px);
		backdrop-filter: blur(6px);
		color: #111112;
		font-family: var(--egh-font-mono);
		font-size: 11px;
		font-weight: 500;
		line-height: 14px;
		letter-spacing: 0.02em;
		text-transform: uppercase;
	}
	.egh-card-title {
		display: block;
		padding: 40px 40px 12px 8px;
		margin-top: auto;
		font-size: 16px;
		line-height: 22.4px;
		letter-spacing: -0.02em;
	}
	.egh-card-arrow {
		position: absolute;
		right: 14px;
		bottom: 16px;
		fill: none;
		stroke: currentColor;
		stroke-width: 1.4;
		stroke-linecap: round;
		stroke-linejoin: round;
		opacity: 0.5;
		transition:
			opacity 220ms linear,
			transform 320ms var(--egh-ease);
	}
	.egh-card:hover .egh-card-arrow,
	.egh-card:focus-visible .egh-card-arrow {
		opacity: 1;
		transform: translateX(2px);
	}

	/* Small screens: the capsule grows into a sheet, clipped to the viewport, scrolling inside. */
	.egh-drawer {
		position: relative;
		height: var(--egh-drawer-h);
		overflow: hidden;
		transition: height 460ms var(--egh-ease);
	}
	.egh-sheet {
		max-height: calc(100dvh - var(--egh-pad-top) - var(--egh-bar-h) - 16px);
		overflow-y: auto;
		overscroll-behavior: contain;
		scrollbar-width: thin;
		scrollbar-color: #3a3a3f transparent;
		padding: 18px 16px 20px;
		box-sizing: border-box;
		visibility: hidden;
		transition: visibility 0s linear 460ms;
	}
	.egh.is-sheet .egh-sheet {
		visibility: visible;
		transition: none;
	}
	.egh-sheet-list {
		display: grid;
		gap: 4px;
		margin: 0;
		padding: 0;
		list-style: none;
	}
	.egh-acc-trigger {
		display: inline-flex;
		align-items: center;
		gap: 8px;
		min-height: 44px;
		padding: 6px 11px;
		border: 0;
		border-radius: 8px;
		background: transparent;
		color: var(--egh-text);
		font: inherit;
		font-size: 16px;
		letter-spacing: -0.02em;
		text-decoration: none;
		cursor: pointer;
		transition:
			background-color 300ms var(--egh-ease),
			color 300ms var(--egh-ease);
	}
	.egh-acc-trigger[aria-expanded="true"] {
		background: var(--egh-a);
		color: var(--egh-a-ink);
	}
	.egh-acc-trigger[aria-current="page"]::after,
	.egh-acc-trigger[data-current]:not([aria-expanded="true"])::after {
		content: "";
		width: 5px;
		height: 5px;
		border-radius: 50%;
		background: var(--egh-a);
	}
	.egh-acc-region {
		display: grid;
		grid-template-rows: 0fr;
		visibility: hidden;
		transition:
			grid-template-rows 420ms var(--egh-ease),
			visibility 0s linear 420ms;
	}
	.egh-acc-region.is-open {
		grid-template-rows: 1fr;
		visibility: visible;
		transition: grid-template-rows 420ms var(--egh-ease);
	}
	.egh-acc-inner {
		display: grid;
		gap: 26px;
		min-height: 0;
		overflow: hidden;
		padding: 0 0 0 0;
	}
	.egh-acc-region.is-open .egh-acc-inner {
		padding: 16px 8px 18px;
	}
	.egh-acc-inner .egh-heading {
		margin-bottom: 18px;
	}
	.egh-acc-inner .egh-lead {
		margin-inline: -8px;
	}
	.egh-sheet-foot {
		display: grid;
		gap: 14px;
		margin-top: 18px;
		padding-top: 18px;
		border-top: 1px solid #2f2f32;
	}
	.egh-cta--block {
		width: 100%;
		height: 48px;
		box-sizing: border-box;
	}
	.egh-call {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		gap: 8px;
		min-height: 44px;
		color: var(--egh-text);
		font-size: 16px;
		font-variant-numeric: tabular-nums;
		text-decoration: none;
	}
	.egh-call-label {
		color: var(--egh-soft);
		font-size: 14px;
	}

	/* Focus: a visible accent ring on every control. */
	.egh-brand:focus-visible,
	.egh-trigger:focus-visible,
	.egh-cta:focus-visible,
	.egh-menu:focus-visible,
	.egh-acc-trigger:focus-visible,
	.egh-lead:focus-visible,
	.egh-card:focus-visible,
	.egh-call:focus-visible,
	.egh-call-line:focus-visible,
	.egh-call-icon:focus-visible {
		outline: 2px solid var(--egh-a);
		outline-offset: 2px;
	}
	.egh-row:focus-visible {
		outline: none;
	}
	.egh-row:focus-visible::before {
		box-shadow: 0 0 0 2px var(--egh-a);
	}

	/* Narrow phones: the bar keeps brand and menu; the button lives at the foot of the sheet. */
	@media (max-width: 809.98px) {
		.egh {
			--egh-pad-top: var(--egh-top, 16px);
			--egh-pad-x: 12px;
		}
		.egh-bar {
			padding-left: 14px;
		}
	}
	/* Smallest phones: with a number, the call button takes the button's place (the button leads the sheet foot). */
	@media (max-width: 419.98px) {
		.egh[data-call] .egh-actions > .egh-cta {
			display: none;
		}
		.egh:not([data-call]) .egh-actions > .egh-cta {
			height: 38px;
			padding: 0 16px;
			font-size: 15px;
		}
		.egh-actions {
			gap: 6px;
		}
	}
	/* Phones: a long name may take two lines rather than run under the menu. */
	@media (max-width: 479.98px) {
		.egh-wordmark {
			font-size: 17px;
			white-space: normal;
			line-height: 1.1;
		}
		.egh[data-name="m"] .egh-wordmark,
		.egh[data-name="s"] .egh-wordmark {
			font-size: 15.5px;
		}
	}

	/* Inline sections from the computed width; below it the sheet. */
	@media (min-width: 960px) {
		.egh[data-inline="960"] .egh-nav,
		.egh[data-inline="960"] .egh-stage {
			display: block;
		}
		.egh[data-inline="960"] .egh-bar {
			grid-template-columns: 1fr auto 1fr;
			gap: 16px;
		}
		.egh[data-inline="960"] .egh-divider,
		.egh[data-inline="960"] .egh-menu,
		.egh[data-inline="960"] .egh-drawer,
		.egh[data-line="960"] .egh-call-icon {
			display: none;
		}
		.egh[data-line="960"] .egh-call-line {
			display: inline-flex;
		}
	}
	@media (min-width: 1024px) {
		.egh[data-inline="1024"] .egh-nav,
		.egh[data-inline="1024"] .egh-stage {
			display: block;
		}
		.egh[data-inline="1024"] .egh-bar {
			grid-template-columns: 1fr auto 1fr;
			gap: 16px;
		}
		.egh[data-inline="1024"] .egh-divider,
		.egh[data-inline="1024"] .egh-menu,
		.egh[data-inline="1024"] .egh-drawer,
		.egh[data-line="1024"] .egh-call-icon {
			display: none;
		}
		.egh[data-line="1024"] .egh-call-line {
			display: inline-flex;
		}
	}
	@media (min-width: 1120px) {
		.egh[data-inline="1120"] .egh-nav,
		.egh[data-inline="1120"] .egh-stage {
			display: block;
		}
		.egh[data-inline="1120"] .egh-bar {
			grid-template-columns: 1fr auto 1fr;
			gap: 16px;
		}
		.egh[data-inline="1120"] .egh-divider,
		.egh[data-inline="1120"] .egh-menu,
		.egh[data-inline="1120"] .egh-drawer,
		.egh[data-line="1120"] .egh-call-icon {
			display: none;
		}
		.egh[data-line="1120"] .egh-call-line {
			display: inline-flex;
		}
	}
	@media (min-width: 1200px) {
		.egh[data-inline="1200"] .egh-nav,
		.egh[data-inline="1200"] .egh-stage {
			display: block;
		}
		.egh[data-inline="1200"] .egh-bar {
			grid-template-columns: 1fr auto 1fr;
			gap: 16px;
		}
		.egh[data-inline="1200"] .egh-divider,
		.egh[data-inline="1200"] .egh-menu,
		.egh[data-inline="1200"] .egh-drawer,
		.egh[data-line="1200"] .egh-call-icon {
			display: none;
		}
		.egh[data-line="1200"] .egh-call-line {
			display: inline-flex;
		}
	}
	@media (min-width: 1280px) {
		.egh[data-inline="1280"] .egh-nav,
		.egh[data-inline="1280"] .egh-stage {
			display: block;
		}
		.egh[data-inline="1280"] .egh-bar {
			grid-template-columns: 1fr auto 1fr;
			gap: 16px;
		}
		.egh[data-inline="1280"] .egh-divider,
		.egh[data-inline="1280"] .egh-menu,
		.egh[data-inline="1280"] .egh-drawer,
		.egh[data-line="1280"] .egh-call-icon {
			display: none;
		}
		.egh[data-line="1280"] .egh-call-line {
			display: inline-flex;
		}
	}
	@media (min-width: 1440px) {
		.egh[data-inline="1440"] .egh-nav,
		.egh[data-inline="1440"] .egh-stage {
			display: block;
		}
		.egh[data-inline="1440"] .egh-bar {
			grid-template-columns: 1fr auto 1fr;
			gap: 16px;
		}
		.egh[data-inline="1440"] .egh-divider,
		.egh[data-inline="1440"] .egh-menu,
		.egh[data-inline="1440"] .egh-drawer,
		.egh[data-line="1440"] .egh-call-icon {
			display: none;
		}
		.egh[data-line="1440"] .egh-call-line {
			display: inline-flex;
		}
	}
	/* No sections at all: just brand and button. */
	.egh[data-inline="0"] .egh-divider,
	.egh[data-inline="0"] .egh-menu,
	.egh[data-line="0"] .egh-call-icon {
		display: none;
	}
	.egh[data-line="0"] .egh-call-line {
		display: inline-flex;
	}

	/* Runtime guard (set by the script when a wider face would crowd the bar): number first, then sections. */
	.egh.is-tight-call .egh-call-line {
		display: none;
	}
	.egh.is-tight-call[data-call] .egh-call-icon {
		display: inline-grid;
	}
	.egh.is-crowded .egh-bar {
		grid-template-columns: minmax(0, 1fr) auto;
		gap: 12px;
	}
	.egh.is-crowded .egh-nav,
	.egh.is-crowded .egh-stage,
	.egh.is-crowded .egh-call-line {
		display: none;
	}
	.egh.is-crowded .egh-divider,
	.egh.is-crowded .egh-drawer {
		display: block;
	}
	.egh.is-crowded .egh-menu.egh-js,
	.egh.is-crowded[data-call] .egh-call-icon {
		display: inline-grid;
	}

	/* Progressive enhancement: the no-JS twins give way once the script runs. */
	.egh:not(.is-enhanced) .egh-js,
	.egh.is-enhanced .egh-nojs {
		display: none !important;
	}
	.egh:not(.is-enhanced) .egh-drawer:target {
		height: auto;
	}
	.egh:not(.is-enhanced):has(.egh-drawer:target) .egh-shell {
		background: var(--egh-solid);
	}
	.egh:not(.is-enhanced) .egh-drawer:target .egh-sheet {
		visibility: visible;
		transition: none;
	}
	.egh:not(.is-enhanced) .egh-acc-region {
		grid-template-rows: 1fr;
		visibility: visible;
	}
	.egh:not(.is-enhanced) .egh-acc-inner {
		padding: 12px 8px 18px;
	}
	.egh:not(.is-enhanced) .egh-acc-trigger .egh-chev {
		display: none;
	}

	@media (prefers-reduced-motion: reduce) {
		.egh,
		.egh *,
		.egh *::before {
			transition-duration: 0s !important;
			transition-delay: 0s !important;
		}
		.egh-cta:hover .egh-roll > span {
			transform: none;
		}
		.egh-card:hover .egh-card-media img,
		.egh-card:hover .egh-card-blank {
			transform: none;
		}
	}
</style>

<script>
	/**
	 * One controller per header, one boot per page view. Survives <ClientRouter />: boots on first load
	 * and on astro:page-load, init is idempotent, every listener hangs off an AbortController aborted on
	 * astro:before-swap, and the body scroll lock is counted at module scope.
	 */
	let locks = 0;
	let savedOverflow = "";
	let savedPadding = "";
	function lockScroll() {
		if (locks++ > 0) return;
		savedOverflow = document.body.style.overflow;
		savedPadding = document.body.style.paddingRight;
		const gap = window.innerWidth - document.documentElement.clientWidth;
		if (gap > 0) document.body.style.paddingRight = `${gap}px`;
		document.body.style.overflow = "hidden";
	}
	function releaseScroll() {
		if (locks === 0 || --locks > 0) return;
		document.body.style.overflow = savedOverflow;
		document.body.style.paddingRight = savedPadding;
	}

	const FOCUSABLE = "a[href], button:not([disabled])";

	function init(root: HTMLElement) {
		if (root.dataset.eghReady) return;
		root.dataset.eghReady = "1";
		root.classList.add("is-enhanced");
		const controller = new AbortController();
		const { signal } = controller;
		const shell = root.querySelector<HTMLElement>(".egh-shell")!;
		const stage = root.querySelector<HTMLElement>(".egh-stage");
		const triggers = [...root.querySelectorAll<HTMLButtonElement>("button.egh-trigger")];
		const barLinks = [...root.querySelectorAll<HTMLElement>(".egh-nav .egh-trigger:not(.egh-nojs), .egh-actions > .egh-cta")];
		const menu = root.querySelector<HTMLButtonElement>("button.egh-menu");
		const drawer = root.querySelector<HTMLElement>(".egh-drawer");
		const sheet = root.querySelector<HTMLElement>(".egh-sheet");
		const reduced = window.matchMedia("(prefers-reduced-motion: reduce)");
		const finePointer = window.matchMedia("(hover: hover) and (pointer: fine)");

		/* ---------- Desktop panels ---------- */
		let active: HTMLButtonElement | null = null;
		let closeTimer = 0;
		/* A click right after a hover opened the panel confirms it rather than closing it. */
		let hoverOpenedAt = 0;
		const panelOf = (t: HTMLButtonElement) => root.querySelector<HTMLElement>(`#${CSS.escape(t.getAttribute("aria-controls") ?? "")}`);
		const measure = () => {
			if (!stage) return;
			const panel = active ? panelOf(active) : null;
			stage.style.setProperty("--egh-stage-h", panel ? `${panel.offsetHeight}px` : "0px");
		};
		const resize = new ResizeObserver(() => measure());

		function open(t: HTMLButtonElement | null) {
			window.clearTimeout(closeTimer);
			if (t === active) return;
			if (active) {
				active.setAttribute("aria-expanded", "false");
				const p = panelOf(active);
				p?.classList.remove("is-active");
				if (p) resize.unobserve(p);
			}
			active = t;
			if (t) {
				t.setAttribute("aria-expanded", "true");
				const p = panelOf(t);
				p?.classList.add("is-active");
				if (p) resize.observe(p);
			}
			root.classList.toggle("is-open", Boolean(t) || sheetOpen);
			measure();
			if (t) reveal();
		}
		const close = () => open(null);
		const scheduleClose = () => {
			window.clearTimeout(closeTimer);
			closeTimer = window.setTimeout(close, 160);
		};

		/* Hover intent: from rest the pointer must dwell 110ms on a section (a pass across the bar opens nothing); once open, sections switch at once. */
		let intentTimer = 0;
		const hoverOpen = (t: HTMLButtonElement) => {
			open(t);
			hoverOpenedAt = performance.now();
		};
		for (const t of triggers) {
			t.addEventListener(
				"pointerenter",
				(e) => {
					if (e.pointerType !== "mouse" || !finePointer.matches || active === t) return;
					window.clearTimeout(intentTimer);
					if (active) hoverOpen(t);
					else intentTimer = window.setTimeout(() => hoverOpen(t), 110);
				},
				{ signal },
			);
			t.addEventListener("pointerleave", () => window.clearTimeout(intentTimer), { signal });
			t.addEventListener(
				"click",
				() => {
					if (active === t && performance.now() - hoverOpenedAt < 600) return;
					open(active === t ? null : t);
				},
				{ signal },
			);
			t.addEventListener(
				"keydown",
				(e) => {
					const panel = panelOf(t);
					const first = panel?.querySelector<HTMLElement>(FOCUSABLE);
					if (e.key === "ArrowDown") {
						e.preventDefault();
						open(t);
						first?.focus();
					} else if (e.key === "Tab" && !e.shiftKey && active === t && first) {
						/* The panel sits after the bar in the DOM: step into it from its own trigger. */
						e.preventDefault();
						first.focus();
					}
				},
				{ signal },
			);
		}
		/* Plain links on the bar close any open panel when hovered. */
		for (const a of root.querySelectorAll<HTMLElement>("a.egh-trigger, .egh-actions > .egh-cta")) {
			a.addEventListener(
				"pointerenter",
				(e) => {
					if (e.pointerType === "mouse") scheduleClose();
				},
				{ signal },
			);
		}
		shell.addEventListener(
			"pointerleave",
			(e) => {
				if (e.pointerType === "mouse" && active) scheduleClose();
			},
			{ signal },
		);
		shell.addEventListener("pointerenter", () => window.clearTimeout(closeTimer), { signal });

		/* Inside a panel: Tab past the last link continues along the bar; Shift+Tab before the first returns to the trigger. */
		stage?.addEventListener(
			"keydown",
			(e) => {
				if (!active || e.key !== "Tab") return;
				const panel = panelOf(active);
				const items = panel ? [...panel.querySelectorAll<HTMLElement>(FOCUSABLE)] : [];
				const i = items.indexOf(document.activeElement as HTMLElement);
				if (!e.shiftKey && i === items.length - 1) {
					e.preventDefault();
					const next = barLinks[barLinks.indexOf(active) + 1];
					close();
					next?.focus();
				} else if (e.shiftKey && i === 0) {
					e.preventDefault();
					active.focus();
				}
			},
			{ signal },
		);

		/* ---------- Small-screen sheet ---------- */
		let sheetOpen = false;
		const sizeDrawer = () => {
			if (!drawer || !sheet) return;
			drawer.style.setProperty("--egh-drawer-h", sheetOpen ? `${sheet.offsetHeight}px` : "0px");
		};
		const sheetResize = new ResizeObserver(() => sizeDrawer());
		if (sheet) sheetResize.observe(sheet);

		function setSheet(next: boolean, restoreFocus = true) {
			if (!menu || next === sheetOpen) return;
			sheetOpen = next;
			menu.setAttribute("aria-expanded", String(next));
			menu.setAttribute("aria-label", (next ? menu.dataset.openLabel : menu.dataset.closedLabel) ?? "");
			root.classList.toggle("is-sheet", next);
			root.classList.toggle("is-open", next || Boolean(active));
			if (next) {
				lockScroll();
				reveal();
			} else {
				releaseScroll();
				if (restoreFocus) menu.focus();
			}
			sizeDrawer();
		}
		menu?.addEventListener("click", () => setSheet(!sheetOpen), { signal });

		for (const b of root.querySelectorAll<HTMLButtonElement>("button.egh-acc-trigger")) {
			b.addEventListener(
				"click",
				() => {
					const region = root.querySelector<HTMLElement>(`#${CSS.escape(b.getAttribute("aria-controls") ?? "")}`);
					const next = b.getAttribute("aria-expanded") !== "true";
					/* One section open at a time, as on the bar. */
					for (const other of root.querySelectorAll<HTMLButtonElement>("button.egh-acc-trigger[aria-expanded='true']")) {
						if (other === b) continue;
						other.setAttribute("aria-expanded", "false");
						root.querySelector(`#${CSS.escape(other.getAttribute("aria-controls") ?? "")}`)?.classList.remove("is-open");
					}
					b.setAttribute("aria-expanded", String(next));
					region?.classList.toggle("is-open", next);
				},
				{ signal },
			);
		}
		/* Following a link in the sheet closes it (same-page anchors and client-routed pages alike). */
		sheet?.addEventListener(
			"click",
			(e) => {
				if ((e.target as HTMLElement).closest("a[href]")) setSheet(false, false);
			},
			{ signal },
		);

		/* ---------- Global: Escape, outside click, focus leaving, breakpoint ---------- */
		document.addEventListener(
			"keydown",
			(e) => {
				if (e.key !== "Escape") return;
				if (active) {
					const t = active;
					close();
					t.focus();
				} else if (sheetOpen) setSheet(false);
			},
			{ signal },
		);
		document.addEventListener(
			"pointerdown",
			(e) => {
				if (root.contains(e.target as Node)) return;
				if (active) close();
				if (sheetOpen) setSheet(false, false);
			},
			{ signal },
		);
		root.addEventListener(
			"focusout",
			(e) => {
				/* Only focus moving somewhere else closes it. A click on a non-focusable part of the panel
				   (padding, a heading) blurs to nothing and must not; clicks outside close via pointerdown. */
				const to = e.relatedTarget as Node | null;
				if (!to || root.contains(to)) return;
				if (active) close();
				if (sheetOpen) setSheet(false, false);
			},
			{ signal },
		);
		/*
		 * The frontmatter plans the bar from estimated label widths. Check the real one: if the sections
		 * would touch the brand or the actions, fold the number back to its button, then the sections into
		 * the sheet. Measured with the planned layout restored first, so a wider window can undo it.
		 */
		const nav = root.querySelector<HTMLElement>(".egh-nav");
		const brand = root.querySelector<HTMLElement>(".egh-brand");
		const actions = root.querySelector<HTMLElement>(".egh-actions");
		const touching = () => {
			if (!nav || !brand || !actions || nav.offsetParent === null) return false;
			const n = nav.getBoundingClientRect();
			return brand.getBoundingClientRect().right + 8 > n.left || n.right + 8 > actions.getBoundingClientRect().left;
		};
		const fit = () => {
			root.classList.remove("is-crowded", "is-tight-call");
			if (!touching()) return;
			root.classList.add("is-tight-call");
			if (touching()) root.classList.add("is-crowded");
		};
		fit();
		document.fonts?.ready.then(() => {
			if (!signal.aborted) fit();
		});

		const onResize = () => {
			fit();
			const inline = Boolean(nav && nav.offsetParent !== null);
			if (inline && sheetOpen) setSheet(false, false);
			if (!inline && active) close();
			measure();
		};
		window.addEventListener("resize", onResize, { passive: true, signal });

		/* ---------- Tuck away on scroll down, back on scroll up ---------- */
		let lastY = window.scrollY;
		let ticking = false;
		function reveal() {
			root.classList.remove("is-tucked");
		}
		const onScroll = () => {
			ticking = false;
			const y = window.scrollY;
			const dy = y - lastY;
			if (Math.abs(dy) < 6) return;
			lastY = y;
			/* Never tucked while in use: a panel or the sheet open, or a keyboard user focused inside it. */
			if (root.dataset.hide === undefined || reduced.matches || active || sheetOpen || root.querySelector(":focus-visible")) return reveal();
			root.classList.toggle("is-tucked", dy > 0 && y > root.offsetHeight + 40);
		};
		window.addEventListener(
			"scroll",
			() => {
				if (!ticking) {
					ticking = true;
					requestAnimationFrame(onScroll);
				}
			},
			{ passive: true, signal },
		);
		root.addEventListener(
			"focusin",
			() => {
				if (root.querySelector(":focus-visible")) reveal();
			},
			{ signal },
		);

		document.addEventListener(
			"astro:before-swap",
			() => {
				if (sheetOpen) setSheet(false, false);
				controller.abort();
				resize.disconnect();
				sheetResize.disconnect();
				window.clearTimeout(closeTimer);
				window.clearTimeout(intentTimer);
				delete root.dataset.eghReady;
			},
			{ once: true, signal },
		);
	}

	function boot() {
		for (const root of document.querySelectorAll<HTMLElement>(".egh")) init(root);
	}
	if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", boot, { once: true });
	else boot();
	document.addEventListener("astro:page-load", boot);
</script>