/* ==========================================================================
   Gemial Digital v2 — Shell
   --------------------------------------------------------------------------
   Header, mobile panel, footer. Layer two of the three in REDESIGN-PLAN.md §5.

   SCOPE RULE, and it is load-bearing from M5. This sheet goes site-wide,
   above roughly twenty Elementor pages. It therefore contains:

     - NO resets
     - NO element selectors that are not inside a .gdh / .gdf / .gd-panel
     - NO custom properties (those are tokens.css)

   Everything reads the --shell-* role tokens, never a raw palette value, so
   the light variant on legacy pages is a token swap and not a second
   stylesheet. Change a colour here and you have broken one of the two
   variants; change the token and both follow.

   Trap §7.3 becomes load-bearing at M5 too: Blocksy sets .menu to
   display:flex, and wp_nav_menu outputs <ul class="menu">. Every ul and li in
   this file therefore declares display explicitly rather than relying on a
   default the parent theme is quietly overriding.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Skip link
   -------------------------------------------------------------------------- */

.gd-skip-link {
	position: absolute;
	inset-inline-start: 1rem;
	inset-block-start: -100%;
	z-index: 200;
	padding: .75rem 1.25rem;
	font-family: var(--f-body);
	font-size: 14px;
	font-weight: 600;
	background: var(--shell-btn-bg);
	color: var(--shell-btn-text);
	border-radius: var(--r-sm);
	text-decoration: none;
}

.gd-skip-link:focus {
	inset-block-start: 1rem;
}

/* --------------------------------------------------------------------------
   Header
   -------------------------------------------------------------------------- */

.gdh {
	position: sticky;
	inset-block-start: 0;
	z-index: 100;
	border-block-end: 1px solid var(--shell-line);
}

/*
 * backdrop-filter lives on the pseudo-element, never on .gdh itself.
 * CLAUDE.md §7.5: it creates a containing block, which would make the fixed
 * mobile panel size itself against the 78px header instead of the viewport.
 */
.gdh::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background: var(--shell-bg-veil);
	backdrop-filter: blur(14px) saturate(150%);
	-webkit-backdrop-filter: blur(14px) saturate(150%);
}

.gdh__in {
	display: flex;
	align-items: center;
	gap: 2rem;
	min-height: var(--header-h);
	width: 100%;
	max-width: calc(var(--container) + var(--gutter) * 2);
	margin-inline: auto;
	padding-inline: var(--gutter);
	transition: min-height var(--dur) var(--ease);
}

.gdh.is-scrolled .gdh__in {
	min-height: var(--header-h-scrolled);
}

/*
 * Scrolled elevation. One rule, both variants: the dark one resolves
 * --shell-elev-scrolled to none and leaves its hairline alone, the light one
 * promotes the hairline and adds a soft shadow. The reasoning and the measured
 * contrast figures are in tokens.css beside the values.
 *
 * On .gdh rather than .gdh__in, because the border and the backdrop live here
 * and a shadow on the inner element would be clipped by the header's own box.
 */
.gdh.is-scrolled {
	border-block-end-color: var(--shell-line-scrolled);
	box-shadow: var(--shell-elev-scrolled);
	transition: box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.gdh__brand {
	display: inline-flex;
	align-items: center;
	gap: .6rem;
	text-decoration: none;
	flex: none;
}

/*
 * Lockup height is the constraint, and it is measured rather than chosen.
 * REDESIGN-PLAN.md §10: DIGITAL smears below 30px and 40px is where the
 * lockup is comfortable. 44px at rest, 40px scrolled. Neither state is
 * allowed below 40, which is why --header-h grew to 84/72.
 *
 * width:auto with an aspect-ratio rather than a fixed width, so the 213.08:64
 * artwork cannot be squashed by a stray width rule.
 */
.gdh__logo {
	height: 44px;
	width: auto;
	aspect-ratio: 213.08 / 64;
	transition: height var(--dur) var(--ease);
}

.gdh.is-scrolled .gdh__logo {
	height: 40px;
}

/*
 * Below 420px the lockup plus a 42px burger starts to crowd. 40px is the
 * floor, so the logo holds and the gutter gives way instead.
 */
@media (max-width: 420px) {
	.gdh__in {
		gap: 1rem;
	}
}

.gdh__nav {
	margin-inline-start: auto;
}

.gdh__nav ul,
.gd-panel ul {
	display: flex;
	list-style: none;
	margin: 0;
	padding: 0;
}

.gdh__nav li,
.gd-panel li {
	display: block;
}

.gdh__nav ul {
	gap: 2rem;
	align-items: center;
}

.gdh__nav a {
	position: relative;
	display: block;
	padding-block: .4rem;
	font-family: var(--f-body);
	font-size: 13.5px;
	color: var(--shell-text);
	text-decoration: none;
	transition: color var(--dur-fast) var(--ease);
}

.gdh__nav a::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	inset-block-end: 0;
	height: 1px;
	background: var(--shell-accent);
	transform: scaleX(0);
	transform-origin: 100% 50%;
	transition: transform var(--dur) var(--ease);
}

.gdh__nav a:hover,
.gdh__nav .current-menu-item > a {
	color: var(--shell-text-hi);
}

.gdh__nav a:hover::after {
	transform: scaleX(1);
	transform-origin: 0 50%;
}

.gdh__act {
	display: flex;
	align-items: center;
	gap: .75rem;
	flex: none;
}

.gdh__burger {
	display: none;
	width: 42px;
	height: 42px;
	place-items: center;
	background: var(--shell-surface-2);
	border: 1px solid var(--shell-line-hi);
	border-radius: var(--r-sm);
	cursor: pointer;
}

.gdh__burger-box {
	display: grid;
	gap: 4px;
	width: 17px;
}

.gdh__burger-box span {
	display: block;
	height: 2px;
	background: var(--shell-accent);
	border-radius: 2px;
	transition: transform var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}

.gdh__burger[aria-expanded="true"] .gdh__burger-box span:nth-child(1) {
	transform: translateY(6px) rotate(45deg);
}

.gdh__burger[aria-expanded="true"] .gdh__burger-box span:nth-child(2) {
	opacity: 0;
}

.gdh__burger[aria-expanded="true"] .gdh__burger-box span:nth-child(3) {
	transform: translateY(-6px) rotate(-45deg);
}

/* Reading progress. Purely decorative, driven by --gd-p from the rAF loop in
   shell.js, so it degrades to a bar that never moves rather than to a broken
   layout.

   V2 PAGES ONLY, and the rule staying in this site-wide sheet is deliberate:
   header-gd2.php prints the element only on a v2 page, so a legacy page never
   has anything for this rule to match. The switch is the markup, in one place,
   rather than a body.gd2-legacy selector here AND a condition in the script. */
.gdh__prog {
	position: absolute;
	inset-inline: 0;
	inset-block-end: -1px;
	height: 2px;
	background: var(--shell-accent);
	transform: scaleX(var(--gd-p, 0));
	transform-origin: 0 50%;
}

/* --------------------------------------------------------------------------
   Reduced motion
   --------------------------------------------------------------------------
   THE CONDENSE IS A STATE CHANGE, THE TRANSITION IS THE ANIMATION, and only
   the second one is a motion preference. Before M5 the scroll loop sat below
   motion.js's reduced-motion early return, so a visitor with the preference on
   simply never got a condensing header. Moving the loop into shell.js gives it
   to everyone, and this turns the tween off rather than the behaviour, which is
   the tier rule in REDESIGN-PLAN.md §6 applied to the shell.

   NOT REDUNDANT WITH base.css, which is the obvious objection. Its blanket
   reduced-motion rule is scoped :where(body.gd2-page) and wins here with
   !important -- measured, a v2 header reads transition-duration 1e-05s under
   the preference. A legacy page carries body.gd2-legacy, so that rule matches
   nothing at all there and this sheet is the only one that reaches it.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	.gdh,
	.gdh__in,
	.gdh__logo,
	.gdh__burger-box span {
		transition: none;
	}
}

/*
 * The auto margin that pushes the actions right lives INSIDE the breakpoint,
 * on an element that is visible at that width. CLAUDE.md §7.4: it used to sit
 * on .gdh__nav, which is display:none below 980px, and auto margins vanish
 * with the element -- which is why the hamburger sat centred.
 */
/*
 * A URL carrying #gd-panel -- shared, bookmarked, or arrived at by Back --
 * must not open a mobile panel on a desktop where the full nav is already
 * visible. :target is (0,1,0) like .gd-panel itself, so this needs to be a
 * rule of its own rather than left to source order.
 */
@media (min-width: 981px) {
	.gd-panel,
	.gd-panel:target {
		display: none;
	}
}

@media (max-width: 980px) {
	.gdh__nav,
	.gdh__cta {
		display: none;
	}

	.gdh__act {
		margin-inline-start: auto;
	}

	.gdh__burger {
		display: grid;
	}
}

/* --------------------------------------------------------------------------
   Mobile panel
   -------------------------------------------------------------------------- */

/*
 * A FULL-VIEWPORT TAKEOVER, AND IT IS A BUG FIX RATHER THAN A PREFERENCE.
 *
 * This was inset: var(--header-h) 0 0 0 at z-index 99, which assumes the
 * header is at the top of the viewport. It is not: inc/site-notice.php prints
 * the redesign banner above it, in flow, at z-index 2000. Measured on
 * production at 390px with the menu open -- banner 0-122px, header 122-207px,
 * panel starting at 84px behind both. The first menu item, Home, was **behind
 * the banner and unclickable on the live homepage**, and had been since the
 * banner returned at 1.6.6.
 *
 * The offset cannot be fixed by arithmetic. The banner's height depends on how
 * its text wraps, and the gap between the viewport top and the header's bottom
 * changes as the sticky header detaches on scroll. CSS cannot track that
 * without anchor positioning, and JavaScript measuring it would put the
 * no-script path straight back where step 4 took it out of.
 *
 * So the panel stops trying to sit below the header and covers everything,
 * above the banner, carrying its own close control. Nothing can overlap it
 * because nothing is above 2001, and the geometry no longer depends on what
 * else the page decides to print at the top.
 */
.gd-panel {
	position: fixed;
	inset: 0;
	z-index: 2001;

	/* Closed is the resting state. The open states are two rules below. */
	display: none;
	flex-direction: column;
	justify-content: space-between;
	gap: 2rem;

	/* Block start clears the close control in the corner: .75rem + 42px + air. */
	padding: 4.75rem var(--gutter) 2.5rem;
	background: var(--shell-bg);
	border-block-start: 1px solid var(--shell-line);
	overflow-y: auto;
	overscroll-behavior: contain;
}

/*
 * THE DISCLOSURE, AND IT IS CSS RATHER THAN JAVASCRIPT.
 *
 * The panel used to be closed by the "hidden" attribute, which only shell.js
 * could remove -- so a phone with no working script had no navigation at all,
 * on every page, from M5. REDESIGN-PLAN.md's blocker option B. It is now an
 * anchor target: the burger links to #gd-panel and this opens it, with no
 * script involved. CLAUDE.md §7.8 category D.
 *
 * TWO OPEN STATES, NOT ONE, and they never both apply:
 *
 *   :target    the no-JavaScript path
 *   .is-open   what shell.js sets, having preventDefault-ed the link so no
 *              hash is ever written
 *
 * So the :target path runs ONLY when JavaScript is absent, which makes it the
 * path least likely to be exercised by anyone testing normally. It has its own
 * check: tools/noscript-nav.mjs, run with scripting disabled.
 */
.gd-panel:target,
.gd-panel.is-open {
	display: flex;
}

/*
 * Scroll lock while the panel is open. In this sheet rather than inline,
 * because if this rule were missing the page would still be usable, just
 * scrollable behind the panel, rather than showing an unstyled open menu.
 *
 * JavaScript-only by nature: it hangs off a class shell.js sets. With no
 * script the panel is a fixed, full-viewport, independently scrolling overlay
 * and the page behind it can still move. That is a cosmetic loss on the path
 * where the alternative was no navigation at all.
 */
body.gd-menu-open {
	overflow: hidden;
}

/*
 * The close control, no-JavaScript's only way out. See header-gd2.php.
 */
.gd-panel .gd-panel__close {
	position: absolute;
	inset-block-start: .75rem;
	inset-inline-end: var(--gutter);
	display: grid;
	place-items: center;
	width: 42px;
	height: 42px;
	font-size: 26px;
	line-height: 1;
	text-decoration: none;
	color: var(--shell-text-hi);
	background: var(--shell-surface-2);
	border: 1px solid var(--shell-line-hi);
	border-radius: var(--r-sm);
}

/*
 * The close target for the no-script path. Zero-sized and fixed, so moving
 * :target onto it closes the panel without the browser scrolling anywhere.
 */
.gd-nav-closed {
	position: fixed;
	inset-block-start: 0;
	inset-inline-start: 0;
	width: 0;
	height: 0;
}

/*
 * IT STAYS VISIBLE ON BOTH PATHS. An earlier draft had shell.js hide this once
 * it took over, on the grounds that the burger turns into an X and two close
 * affordances in one corner is one too many. The takeover above settles it:
 * the panel now covers the header, so the burger is not reachable while the
 * panel is open on either path, and this control plus Escape is the whole exit.
 * The burger's X rule still fires on aria-expanded and is simply never seen.
 */

.gd-panel ul {
	flex-direction: column;
	gap: .25rem;
}

.gd-panel a {
	display: block;
	padding-block: .85rem;
	font-family: var(--f-display);
	font-size: 1.5rem;
	font-weight: 700;
	letter-spacing: -.03em;
	color: var(--shell-text-hi);
	text-decoration: none;
	border-block-end: 1px solid var(--shell-line);
}

.gd-panel .current-menu-item > a {
	color: var(--shell-accent);
}

.gd-panel__foot {
	display: grid;
	gap: 1rem;
	justify-items: stretch;
}

.gd-panel__line {
	font-family: var(--f-mono);
	font-size: 11px;
	letter-spacing: .12em;
	color: var(--shell-text);
	text-align: center;
	text-decoration: none;
	border: 0;
	padding: 0;
}

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.gdf {
	background: var(--shell-surface);
	border-block-start: 1px solid var(--shell-line);
	padding-block: clamp(3.5rem, 6vw, 5rem) 2rem;
}

.gdf__grid {
	display: grid;
	grid-template-columns: 1.8fr 1fr 1fr 1.2fr;
	gap: 2.5rem;
}

.gdf__brand {
	display: inline-flex;
	text-decoration: none;
}

.gdf__logo {
	width: auto;
	height: 46px;
	aspect-ratio: 213.08 / 64;
}

/* Only rendered if logo-reversed.svg is missing. Full name, never "GEMIAL". */
.gdf__fallback {
	font-family: var(--f-display);
	font-size: 1.25rem;
	font-weight: 700;
	font-stretch: var(--wide-brand);
	letter-spacing: -.03em;
	color: var(--shell-text-hi);
}

.gdf__about {
	max-width: 34ch;
	margin-block-start: 1rem;
	font-family: var(--f-body);
	font-size: 14px;
	line-height: 1.6;
	color: var(--shell-text);
}

.gdf__h {
	margin: 0 0 1.1rem;
	font-family: var(--f-mono);
	font-size: 9.5px;
	font-weight: 500;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: var(--shell-text);
}

.gdf ul {
	display: flex;
	flex-direction: column;
	gap: .6rem;
	list-style: none;
	margin: 0;
	padding: 0;
}

.gdf li {
	display: block;
	font-family: var(--f-body);
	font-size: 14px;
	color: var(--shell-text);
}

.gdf a {
	color: var(--shell-text);
	text-decoration: none;
	transition: color var(--dur-fast) var(--ease);
}

.gdf a:hover {
	color: var(--shell-accent);
}

/* --------------------------------------------------------------------------
   The oversized lockup
   --------------------------------------------------------------------------
   Mirrors logo-reversed.svg at display scale: GEMIAL with DIGITAL letter-
   spaced beneath to exactly the same width. DIGITAL is structural, not
   optional secondary text, and this is the mechanism that makes it so.

   THE NUMBERS ARE SOLVED, NOT EYEBALLED, which is how the SVG did it.

   Measured off logo-reversed.svg rendered at 1600px wide:

     GEMIAL   cap height 182px, ink width 995px
     DIGITAL  cap height  92px, ink width 1002px

   So DIGITAL is set at half the cap height (92/182 = 0.505) and tracked until
   its ink matches. Archivo at wght 700 and wdth 118 measures:

     GEMIAL             4.7190 em
     DIGITAL            4.7230 em  (2.3615 em at half size)
     shortfall          2.3575 em of the GEMIAL size

   CSS letter-spacing is added after EVERY character including the last, so
   six gaps carry the width and the seventh is trailing whitespace:

     ink match          6L = 4.7150 em(own)  ->  L = 0.7858em
     trailing cancelled by an equal negative margin

   Result is 4.7189 em against GEMIAL's 4.7190. Change the font-stretch below
   and both numbers must be re-solved -- see tools/, the measurement is a
   four-line fontkit script.
   -------------------------------------------------------------------------- */

.gdf__word {
	margin-block-start: clamp(2rem, 5vw, 3.5rem);
	font-family: var(--f-display);
	font-size: clamp(2.25rem, 9.5vw, 7rem);
	font-weight: 700;
	font-stretch: 118%;
	line-height: .84;
	color: var(--shell-line-hi);
	user-select: none;
	overflow: hidden;
}

.gdf__word-a,
.gdf__word-b {
	display: block;
}

.gdf__word-b {
	font-size: .5em;
	letter-spacing: .7858em;
	margin-inline-end: -.7858em;
	line-height: 1;
}

.gdf__bar {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	gap: 1rem;
	margin-block-start: clamp(2.5rem, 5vw, 4rem);
	padding-block-start: 1.5rem;
	border-block-start: 1px solid var(--shell-line-hi);
	font-family: var(--f-mono);
	font-size: 10px;
	letter-spacing: .1em;
	color: var(--shell-text);
}

.gdf__legal {
	display: inline-flex;
	gap: 1.25rem;
}

@media (max-width: 900px) {
	.gdf__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 540px) {
	.gdf__grid {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* --------------------------------------------------------------------------
   Focus, shell-wide
   --------------------------------------------------------------------------
   Repeated here rather than left to base.css, because this sheet loads on
   legacy pages where base.css does not. A keyboard user tabbing through the
   header on an Elementor page still gets a visible ring.
   -------------------------------------------------------------------------- */

.gdh :focus-visible,
.gdf :focus-visible,
.gd-panel :focus-visible,
.gd-skip-link:focus-visible {
	outline: 2px solid var(--shell-focus);
	outline-offset: 3px;
	border-radius: var(--r-sm);
}
