/* ============================================================================
   GLOBAL NAVIGATION

   A row of text links across the top of the viewport. No pill, no icons, no
   bar — the only things painted are the type and the ground under it.

   The ground is a legibility concession, not a bar: the colour of whatever
   band is behind the links, faded out below them. Matching the band is what
   keeps it from reading as furniture — at rest it is the same colour as the
   thing behind it and you cannot see it at all. It is also what iOS Safari
   samples to tint its own status bar, which is the whole reason the browser
   chrome and the page can hold one colour instead of two. The section below
   has the full note.

   TYPE — size, family, tracking and case are all inherited from body copy;
   the brief was "the same size and styling as body copy" and that is still
   the intent. The one declaration is font-weight: 400. Body went to 300, and
   the light cut at 15px over a saturated red field is genuinely under-inked —
   the links are the only body-size type on the site that has to hold on the
   brand ground. 400 is the book weight, not a bold: the row's rhythm and
   measure are unchanged.

   LAYOUT — the bar *is* the site measure (90% / 1440px, matching .wrapWide),
   so both ends land on the same grid lines as the content beneath. The
   wordmark holds the left margin — it is the origin point of the page's grid,
   and without it the row of links read as five words floating over nothing.
   The links pack right, space-between within --nav-links-measure so that on a
   wide screen they stay a group rather than a scatter. Below that cap the
   max-width is inert and the row justifies — the behaviour you want on a
   phone.

   THEME — the nav sits *over* the page rather than above it, so its ink has
   to follow whatever band is behind it. scripts.js probes what is under the
   links on scroll and stamps data-nav-theme="light|dark|brand" on the header;
   the three rules at the bottom of this file are all that responds. Two of
   them work by flipping color-scheme and letting the existing tokens resolve,
   the same mechanism the always-dark bands and the footer already use (see
   "scoped themes" in 02-tokens).

   Any element can force the result with data-nav-theme="light|dark|brand" on
   itself. Reach for it over full-bleed imagery, where a section's own
   background colour says nothing about what the nav is actually sitting on.
   ============================================================================ */

@layer components {

/* --- The ground -------------------------------------------------------------
   The bar has no bar. What it has is a ground: the colour of the full-width
   band behind it, painted across the top of the viewport and eased out below
   the links, so type scrolling up is veiled and gone rather than crossing the
   nav. Because the colour is the band's own, the ground is invisible at rest —
   it only becomes a thing when content moves under it.

   TWO ELEMENTS, and the split is not cosmetic. #header is full bleed and does
   the painting; .navBar holds the 90%/1440 site measure and the row. iOS
   Safari tints its own status-bar blur — the fade around the Dynamic Island —
   by sampling a fixed element at the top edge, and a 90%-wide element leaves a
   gap at each side for it to fall through. Full bleed is what makes that land,
   and with it the browser chrome and the page hold one colour instead of
   reading as two treatments.

   THE SHAPE is a background-image and not a mask: a mask applies to the
   subtree, so any transparency in it is transparency in the links too, and the
   row has to stay solid. The ground is drawn, not cut. It is fully opaque for
   --nav-solid at the top, holds high through the type where legibility is the
   whole argument, then falls away and finishes at the bottom of the bar.

   WHAT SAFARI TINTS FROM IS NOT THIS. Its status-bar tint samples a fixed
   element at the top edge, but only a plain one, and the rule is narrower than
   it looks: a gradient of literal rgb() stops is sampled, and the same
   gradient with a var() in a stop is not — it was tested both ways. A solid
   background-color built from var() samples fine, so it is gradients
   specifically that want literal colours. There is no way to write this
   gradient without the variable, so this element is not the one Safari reads.

   So .navSampler is the element Safari reads. It is a fixed strip at the top
   edge carrying nothing but background-color: var(--nav-surface) — a plain
   solid colour, which samples, and which was measured re-sampling on scroll.
   It is --nav-solid tall, which is exactly the run the gradient below it
   paints fully opaque in that same colour, so it is not merely subtle, it is
   pixel-identical to what was already there. Change --nav-solid and it stays
   invisible; change one of them alone and it will not.

   The root background is the fallback under all of this, and 03-base still
   points `body` at --nav-surface so it is right rather than stale. But it is
   only a fallback: Safari samples the root once at first paint and does not
   look again, which is why the tint was correct per page and frozen per
   section when `body` was carrying this on its own. Do not go back to that.

   Do not try to make the gradient the sampled element either. Its failure is
   silent — the tint drops to the root and meets the header along a hard
   line.                                                                     */

.navSampler {
	position: fixed;
	inset: 0 0 auto;
	height: var(--nav-solid);
	/* Over the header, so safari tinting works and refers to this band. They
	   are the same colour there, so which one wins is a matter of indifference
	   — this only has to exist, not to be seen. */
	z-index: 1001;
	pointer-events: none;

	/* The explicit fallback matters: before scripts.js writes --nav-surface
	   this is transparent, nothing qualifies, and Safari falls back to the
	   root background — which is the page's resting surface and correct. */
	background-color: var(--nav-surface, transparent);
}

header#header {
	position: fixed;
	inset: 0 0 auto;
	z-index: 1000;

	/* The header is the full width of the viewport but it is not a bar: only
	   the links themselves take the pointer, everything under it stays live. */
	pointer-events: none;

	/* Resting and active ink. Both are light-dark() tokens underneath, which
	   are resolved at the point of use — so they follow the color-scheme the
	   theme rules below set on this element, not the page's. */
	--nav-ink:        var(--color-text-1);
	--nav-ink-active: var(--color-action-text);

	/* scripts.js writes --nav-surface from the band behind the bar. Undeclared
	   until it does, which makes this invalid at computed-value time and
	   resolves background-image to none — the bar paints nothing and the page
	   is as it was. That is the intended resting state, not a failure.

	   color-mix rather than hand-written alphas because the stops are then one
	   colour at declared strengths, and that colour is the semantic one the
	   band is actually painted in. in oklab on both the mix and the ramp: a
	   saturated colour interpolated to the `transparent` keyword travels
	   through transparent *black*, which on the red field is a visible dirty
	   wash across the top of the poster. */
	background-image: linear-gradient(to bottom in oklab,
		var(--nav-surface)                                       0,
		var(--nav-surface)                                       var(--nav-solid),
		color-mix(in oklab, var(--nav-surface) 90%, transparent) 55%,
		color-mix(in oklab, var(--nav-surface) 65%, transparent) 72%,
		color-mix(in oklab, var(--nav-surface) 32%, transparent) 86%,
		color-mix(in oklab, var(--nav-surface) 10%, transparent) 95%,
		transparent                                              100%);
}

/* Asked for less transparency, the ground stops being a fade and becomes a
   band: one flat run of the same colour, no ramp. Note what this does *not*
   cost — the colour is still the band's own, so at rest the bar is exactly as
   invisible as it was, and the difference only appears once content is
   scrolling underneath, which is the moment the preference is actually about.
   .navSampler needs nothing here; it was always solid. */
@media (prefers-reduced-transparency: reduce) {
	header#header {
		background-image: none;
		background-color: var(--nav-surface, transparent);
	}
}

/* The bar is the site measure, not the viewport: wordmark on the left margin,
   links on the right, both landing on the same grid lines as the content
   beneath them. */
.navBar {
	padding-block: var(--nav-inset) var(--space-s);

	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-m);
	width: 90%;
	max-width: 1440px; /* the site measure — matches .wrapWide in 07-shell */
	margin-inline: auto;
}

.navWordmark {
	pointer-events: auto;
	color: var(--nav-ink);
	text-decoration-line: none;
	flex-shrink: 0;
	display: flex;
    align-items: center;
	transition: color 0.35s ease;

	/* The block is the only red on the bar. It is the mark. */
	&::before { 
		content: "";
		color: var(--color-brand);
		background: var(--color-brand);
		/* width: 1em; */
		height: 0.8em;
		display: block;
		margin-right: 0.2em;
		aspect-ratio: 1 / 1;
		transition: background 0.35s ease;
	}

	&:hover { color: var(--nav-ink-active); }
}

/* On the red field the mark cannot be red. It goes to the ink colour and
   earns its separation from weight and size instead — the same argument as
   "no opacity ramp on this surface" in 02-tokens. */
#header[data-nav-theme="brand"] .navWordmark::before { background: var(--nav-ink); }

#header nav ul {
	list-style: none;
	margin: 0;
	padding: 0;

	display: flex;
	flex-direction: row;
	justify-content: space-between;
	align-items: baseline;
	/* A floor only. space-between decides the real spacing at every width the
	   site supports — around 60px at 1440, around 10px at 320 — so the floor's
	   one job is to be small enough that a slightly wider rendering of the
	   labels than measured here still fits on one line. */
	gap: var(--space-2xs) var(--space-m);

	/* The row fits at every width the site supports, with room to spare at
	   320px. flex-wrap is the safety valve for the case that measurement does
	   not cover: someone browsing with a large default font size. Rather than
	   clamp the type against their setting — which is the one thing a nav
	   should not do — the row is allowed to take a second line. */
	flex-wrap: wrap;
}

#header nav a {
	pointer-events: auto;
	position: relative;
	display: inline-block;
	font-weight: 400; /* see the TYPE note at the top of this file */
	color: var(--nav-ink);
	text-decoration-line: none;
	white-space: nowrap;
	transition: color 0.35s ease;

	/* One underline serves two states: hover draws it, the current page keeps
	   it drawn. Deliberately not a weight or opacity change — weight would
	   reflow the row as you move across it, and on the red field there is no
	   opacity available (see the note on .onBrand in 02-tokens). */
	&::after {
		content: "";
		position: absolute;
		inset-inline: 0;
		bottom: -0.35em;
		height: 1px;
		background: currentColor;
		transform: scaleX(0);
		transform-origin: left center;
		transition: transform 0.3s ease;
	}

	&:hover,
	&:focus-visible { color: var(--nav-ink-active); }

	&:hover::after,
	&:focus-visible::after { transform: scaleX(1); }

	&[aria-current="page"] {
		color: var(--nav-ink-active);

		&::after { transform: scaleX(1); }
	}
}

/* --- Themes -----------------------------------------------------------------
   Set by scripts.js from what is behind the bar. The brand state is the only
   one that needs values of its own, for the same reason .onBrand does: the
   neutral text ramp is tuned for near-black grounds and does not clear 4.5:1
   on red, so the ink goes to solid white.                                    */

#header[data-nav-theme="light"] { color-scheme: light; }
#header[data-nav-theme="dark"]  { color-scheme: dark; }

#header[data-nav-theme="brand"] {
	color-scheme: dark;

	--nav-ink:        var(--color-on-brand);
	--nav-ink-active: var(--color-on-brand);
}

/* Under the wordmark + five links there is no room for a spread row, so the
   links pack right and the wordmark keeps the left margin. */
@media only screen and (max-width: 700px) {
	
	.navBar { 
		gap: var(--space-2xs);
		flex-direction: column;
	}

	.navWordmark {
		width: 100%;

		/* The block is the only red on the bar. It is the mark. */
		&::before { 
			width: 100%;
		}
	}
	
	#header nav {
		width: 100%;
	}
	
	#header nav ul {
		justify-content: space-between;
		gap: var(--space-2xs) 0;
		width: 100%;
	}
}


/* Hit area, at every width. Expands the pointer target off the type without
   entering layout, so neither the row's height nor the underline moves. */
#header nav a::before {
	content: "";
	position: absolute;
	inset-block: -0.35em;
	inset-inline: calc(var(--space-3xs) * -1);
}

@media (prefers-reduced-motion: reduce) {
	#header nav a,
	#header nav a::after,
	.navWordmark { transition: none; }
}

/* --- View transitions ---------------------------------------------------
   Cross-document navigations (opted into once, in 01-reset) give every
   element an implicit view-transition-name of "root" and crossfade the
   whole page. Naming the header pulls it out of that root capture into its
   own group — otherwise it would fade out with the old page and fade back
   in with the new one, which reads as a flicker on a bar that never
   actually moved.

   The header renders identically at the same fixed position on every page,
   so the browser's default group animation (which morphs size/position)
   and the old/new crossfade both have nothing useful to animate — they'd
   just hold two stacked, pixel-identical copies at swapped opacity for
   0.25s. Turning that off makes the header hold still and swap instantly,
   which is what "persistent" actually looks like. The rest of the page
   still gets the default crossfade. */
#header { view-transition-name: site-header; }

::view-transition-group(site-header),
::view-transition-old(site-header),
::view-transition-new(site-header) {
	animation: none;
}

} /* @layer components */
