/*
	SITE CHROME. The header, the sign-off and the footer, plus the newsletter form's
	shared skeleton: everything that is the same on every public page, in one file, so
	the home and a policy page cannot drift apart.

	Loaded by every public page (src/_includes/masthead.njk and signoff.njk are what it
	dresses). It sits AFTER news.css in the page style list so the story page's compact
	masthead rules, which the internal dashboard still wears, never reach the shared one.
	It sits BEFORE home.css and page.css so each context can skin the lane chips it owns.

	It does not restate .wrap or the focus rule: every page that loads this file also
	loads home.css or news.css, and both carry the reading shell already.

	Palette, type and spacing tokens live in tokens.css. Keyboard hyphen only,
	sentence-case, no BEM: descriptive names and native nesting.
*/

/* The drawer's closed state is visibility and a transform, never [hidden] or
   display:none, because a box that is not laid out cannot animate out of the way - the
   old inline panel toggled the attribute and could only appear and disappear. visibility
   is what still takes it out of the tab order and off a screen reader while it is shut,
   which display:none did for free and opacity alone would not do at all. */

/* --------------------------------------------------------------- masthead */

.masthead {
	position: sticky;
	top: 0;
	z-index: 40;
	background-color: var(--canvas);
	border-block-start: 3px solid var(--accent);
	border-block-end: 1px solid var(--hairline);
	box-shadow: 0 16px 32px -26px rgb(0 0 0 / 0.95);
}

/* The masthead used to run three rows: the brand, the genres, and a rail of story
   types. The types are the river's topic bank now, so two rows are left and the
   remaining ones carry more air rather than the same air with a gap where the third
   row was. */
.masthead-top {
	display: flex;
	align-items: center;
	gap: 1.15rem;
	min-height: 3.85rem;
}

/* Centred, not baseline: the wordmark is an image now, and an image has no text
   baseline to share with the tagline, so baseline alignment left the row visibly
   uneven (the owner's call, 2026-08-16). */
.masthead-brand {
	display: flex;
	align-items: center;
	gap: 0.85rem;
	min-width: 0;
}

/* The wordmark is the owner's SVG logo now; the type rules stay because the internal
   boards' compact masthead still sets the name in text. The logo takes its height from
   the row and keeps its own width; the img height/width attributes only stop layout
   shift before the file paints. */
/* inline-flex with a floor rather than the bare inline the text wordmark left behind:
   the logo is an image now, so the link box was exactly the image's own 32px and the
   way home from anywhere on the site was the shortest target on the page
   (audited 2026-08-28). The logo's own size is untouched; the box around it grows. */
.wordmark {
	display: inline-flex;
	align-items: center;
	min-height: 2.75rem;
	font-family: var(--font-display);
	font-size: clamp(1.15rem, 2.1vw, 1.45rem);
	letter-spacing: -0.035em;
	color: var(--text);
	text-decoration: none;
	white-space: nowrap;
}

.wordmark-logo {
	display: block;
	height: clamp(2rem, 3.5vw, 2.6rem);
	width: auto;
}

.tagline {
	display: none;
	font-size: 0.78rem;
	color: var(--muted);
	white-space: nowrap;
}

.utility-nav {
	display: none;
	margin-inline-start: auto;

	ul {
		display: flex;
		gap: 1.4rem;
	}

	a {
		font-size: 0.8rem;
		color: var(--muted);
		text-decoration: none;

		&:hover {
			color: var(--text);
			text-decoration: underline;
			text-underline-offset: 4px;
		}
	}
}

/* Above the drawer it opens, so the control that opened the sheet is the control that
   closes it and never slides under it. */
.nav-toggle {
	position: relative;
	z-index: 3;
	margin-inline-start: auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.75rem;
	height: 2.75rem;
	border: 1px solid var(--hairline-bright);
	background-color: transparent;
	color: var(--text);
	font-size: 1rem;
	cursor: pointer;

	&:hover {
		border-color: var(--accent);
		color: var(--accent-text);
	}

	&[aria-expanded='true'] {
		background-color: var(--accent);
		border-color: var(--accent);
		color: var(--accent-ink);
	}
}

/* One button, two glyphs: aria-expanded is the single source of which one shows, so the
   state a screen reader is told and the state a sighted reader sees cannot disagree. */
.nav-toggle-close,
.nav-toggle[aria-expanded='true'] .nav-toggle-open {
	display: none;
}

.nav-toggle[aria-expanded='true'] .nav-toggle-close {
	display: block;
}

/*
	The rail under the brand row, in 03's dense one-row grammar: two groups of links on
	a single line, divided by a hairline, the second group set smaller and in the mono
	so the eye reads two kinds of link rather than twelve of one. Genres lead and answer
	to the river; the four trackers close the line and answer to the bands below, each
	behind a small square in its band's colour.

	The square is decoration and says so, so the link never depends on it: the label is
	the link, the colour is the tie back to the band it lands on.
*/
.site-rail {
	border-block-start: 1px solid var(--hairline);
}

.site-rail-inner {
	display: flex;
	align-items: stretch;
	width: 100%;
	max-width: var(--measure);
	margin-inline: auto;
	padding-inline: var(--gutter);
	overflow-x: auto;
	scrollbar-width: none;
	/* The rail scrolls until the whole line fits. Fading the trailing edge is the
	   only affordance that says so without adding a control.

	   The fade is a fixed length rather than a share of the width, which it used to
	   be. A percentage grows with the viewport, and once the rail carried twelve
	   links a fifteenth of 1024px was swallowing the last one whole: it read as a
	   broken label instead of a link with more behind it. 3.5rem is what 85% came to
	   on a phone, which is where the affordance was already right. */
	-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 3.5rem), transparent 100%);
	mask-image: linear-gradient(to right, #000 calc(100% - 3.5rem), transparent 100%);
}

.genre-nav,
.tracker-nav {
	ul {
		display: flex;
		align-items: center;
		height: 100%;
	}

	li {
		flex: 0 0 auto;
	}

	a {
		display: flex;
		align-items: center;
		padding-block: 0.8rem;
		color: var(--muted);
		text-decoration: none;
		white-space: nowrap;
		border-block-end: 2px solid transparent;
	}
}

/* The trailing pad matches the trackers' leading one, so the divider has the same
   air on both sides once the rail overflows and the auto margin goes to zero. */
.genre-nav {
	padding-inline-end: clamp(0.9rem, 2vw, 1.35rem);

	/* The same thin rule that divides this block from the trackers, repeated between
	   the genres themselves (the owner's 2026-08-16 call), so the rail reads as one
	   ruled strip end to end. Half the old gap sits each side of the rule, keeping the
	   overall rhythm exactly where it was. */
	ul {
		gap: 0.75rem;
	}

	li + li {
		border-inline-start: 1px solid var(--hairline-bright);
		padding-inline-start: 0.75rem;
	}

	a {
		font-size: 0.85rem;
		font-weight: 500;

		&:hover {
			color: var(--text);
			border-block-end-color: var(--accent);
		}
	}
}

/* Pushed to the trailing edge while the line has room, sitting straight after the
   genres once it does not. The auto margin resolves to zero the moment the rail
   overflows, which is what stops a gap opening in the middle of a scrolled line. */
.tracker-nav {
	margin-inline-start: auto;
	padding-inline-start: clamp(0.9rem, 2vw, 1.35rem);
	border-inline-start: 1px solid var(--hairline-bright);

	ul {
		gap: clamp(0.9rem, 2vw, 1.35rem);
	}

	a {
		gap: 0.5rem;
		font-family: var(--font-mono);
		font-size: 0.72rem;
		letter-spacing: 0.02em;

		&:hover {
			color: var(--text);
			border-block-end-color: var(--band-accent);
		}
	}
}

.tracker-key {
	flex: none;
	width: 0.5rem;
	height: 0.5rem;
	background-color: var(--band-accent);
}

/*
	THE TRACKER MAP. One block, read by the four rail links above and by the four bands
	on the home page (home.css), so a band and the link that points at it can never
	drift to different colours. It lives here rather than with the bands because the
	rail is on every page and the bands are on one, and splitting the block between two
	files is exactly the drift it exists to prevent. Every accent-inked rule on either
	side names --band-accent or --band-accent-text and nothing names a hue, which is what
	keeps the exception to the single-orange discipline inside these sections.

	--band-accent inks surfaces and rules; --band-accent-text inks type. The two differ
	only for the release calendar, whose #ff5c1a is a surface orange that never becomes
	ink; the other three hues are bright enough to be both. tokens.css carries the
	measured contrast for all four.

	--plate-ink scales the whole plate layer at once. The lit ground each band reaches
	is the same luminance on all four, so mint does not wash out a band that lilac
	merely tints, and every neutral pairing measured on the orange plate holds on the
	other three. The scale is a measurement: it is what lands #f0c04e, #7ef0c1 and
	#c99df0 on the ground #ff5c1a reaches at full strength. The rail reads none of it,
	and an unused custom property costs a page nothing.
*/
[data-tracker] {
	--band-accent: var(--accent);
	--band-accent-text: var(--accent-text);
	--plate-ink: 1;

	/* The row divider, carrying a whisper of the band. A rule, never a wash behind
	   type, which is the same line the house draws for the orange. */
	--band-row-rule: color-mix(in srgb, var(--band-accent) 20%, var(--hairline));

	/* The plates run hot in these four sections, so quiet type steps up with the
	   ground: #b6b6c0 holds 4.5:1 over the strongest lit corner any band reaches,
	   measured against the composited plate rather than the flat panel. */
	--muted: #b6b6c0;
}

[data-tracker='release-calendar'] {
	--band-accent: var(--tracker-release);
	--band-accent-text: var(--tracker-release-text);
	--plate-ink: 1;
}

[data-tracker='tour-guide'] {
	--band-accent: var(--tracker-tour);
	--band-accent-text: var(--tracker-tour-text);
	--plate-ink: 0.85;
}

[data-tracker='festival-tracker'] {
	--band-accent: var(--tracker-festival);
	--band-accent-text: var(--tracker-festival-text);
	--plate-ink: 0.78;
}

[data-tracker='award-watch'] {
	--band-accent: var(--tracker-award);
	--band-accent-text: var(--tracker-award-text);
	--plate-ink: 0.92;
}

/*
	THE DRAWER behind the menu button, below 1024. It sits over the page from the
	trailing edge rather than pushing it down, which is what the inline panel here used
	to do: under a sticky masthead, opening the menu reflowed the article the reader was
	in the middle of. Same shape songsuey.com uses (adopted 2026-08-28).

	Fixed to the viewport, so it is the full height of the screen whatever the masthead
	is doing and whatever the page has scrolled to. It scrolls internally, because it
	carries every genre now and 18 rows will not fit a phone in portrait.

	The width is capped as well as proportional: 80vw alone would be a 900px sheet on a
	tablet in landscape, which stops reading as a drawer and starts reading as a broken
	page.
*/
.nav-panel {
	position: fixed;
	inset-block: 0;
	inset-inline-end: 0;
	z-index: 2;
	width: min(80vw, 320px);
	padding: 4.5rem 1.35rem 2rem;
	overflow-y: auto;
	overscroll-behavior: contain;
	background-color: var(--panel);
	border-inline-start: 1px solid var(--hairline-bright);
	box-shadow: -12px 0 40px rgb(0 0 0 / 45%);
	transform: translateX(100%);
	visibility: hidden;
	transition: transform 0.28s ease, visibility 0.28s;
}

.nav-panel.is-open {
	transform: translateX(0);
	visibility: visible;
}

/* Over the page, under the drawer, and the click-outside target: a tap anywhere off
   the sheet closes it. */
.nav-backdrop {
	position: fixed;
	inset: 0;
	z-index: 1;
	background-color: rgb(0 0 0 / 60%);
	opacity: 0;
	visibility: hidden;
	transition: opacity 0.28s ease, visibility 0.28s;
}

.nav-backdrop.is-open {
	opacity: 1;
	visibility: visible;
}

/* The page behind a drawer does not scroll. Set on <body> by the script, because the
   drawer is fixed and a scrolling page under it reads as two documents at once. */
body.nav-open {
	overflow: hidden;
}

/* The group labels, in the footer's own grammar so the two lists of the same
   destinations read as one site rather than two schemes. */
/* 0.75rem rather than the 0.68 this and the footer heading both started at: uppercase
   mono at 0.68 renders around 10.9px, which is under the size a phone sets legibly, and
   these two are the labels on the site's only mobile navigation (audited 2026-08-28). */
.nav-panel-heading {
	font-family: var(--font-mono);
	font-size: 0.75rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--muted-2);
	margin-block: 1.4rem 0.5rem;

	&:first-child {
		margin-block-start: 0;
	}
}

.nav-panel-list {
	a {
		display: flex;
		align-items: center;
		min-height: 2.75rem;
		font-size: 0.95rem;
		color: var(--text);
		text-decoration: none;
		border-block-end: 1px solid var(--hairline);

		&:hover {
			color: var(--accent-text);
		}
	}

	li:last-child a {
		border-block-end: 0;
	}
}

/* The same key square the rail carries, at the same size, so a reader who opens the
   menu on a phone is looking at the rail's own list rather than a second scheme. */
.nav-panel-trackers {
	a {
		gap: 0.75rem;
	}
}

/* ------------------------------------------------------- the lane chips */
/*
	The genre picker every newsletter form carries, from the one partial in
	src/_includes/newsletter-form.njk. The structure is here; the ink belongs to
	whichever surface the form is standing on, so each context skins .lane-chip-band
	(home.css), .lane-chip-quiet (below) or .lane-chip-page (page.css).
*/

.lane-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 0.45rem;
}

/* Each lane is a real checkbox named `lanes`, so the picks submit with the form; the
   label wears the chip and takes the focus ring for it. The tick floats at the chip's
   leading edge instead of taking inline space, so checking a lane never shifts the word
   or reflows the row. */
.lane-chip {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0.42rem 1.4rem;
	border: 1px solid var(--hairline-bright);
	color: var(--muted);
	font-size: 0.82rem;
	font-weight: 600;
	cursor: pointer;
	transition: background-color 140ms linear, color 140ms linear;

	input {
		position: absolute;
		inset-block-start: 50%;
		inset-inline-start: 0.7rem;
		width: 1px;
		height: 1px;
		margin: 0;
		opacity: 0;
	}

	.lane-tick {
		position: absolute;
		inset-inline-start: 0.45rem;
		font-size: 0.72em;
		opacity: 0;
	}

	&:has(input:checked) .lane-tick {
		opacity: 1;
	}

	&:has(input:focus-visible) {
		outline: 2px solid var(--focus-ring);
		outline-offset: 3px;
	}
}

/* ----------------------------------------------------------- the status line */

/* Empty until a submit says something. The plate, the mark and the wording all change
   together, so a refusal can never be mistaken for a confirmation. */
.form-status {
	display: none;
	align-items: baseline;
	gap: 0.5rem;
}

.form-status[data-state] {
	display: flex;
}

.status-icon {
	display: none;
	flex: 0 0 auto;
}

.form-status[data-state='ok'] .status-ok,
.form-status[data-state='error'] .status-warn {
	display: inline-block;
}

/* --------------------------------------------------------------- sign-off */

/* The sign-off and the shell's own footer share a ground, so the two read as one
   plate: the brand and the quiet second signup, then the link row and the sibling
   line under a hairline. */
.signoff {
	background-color: var(--footer-ground);
	border-block-start: 3px solid var(--accent);
	padding-block: clamp(2.25rem, 4.5vw, 3.5rem) clamp(1.75rem, 3vw, 2.5rem);
}

.signoff-inner {
	display: grid;
	gap: 2.25rem;
}

.signoff-wordmark {
	line-height: 1;
}

.signoff-logo {
	display: block;
	height: clamp(2.25rem, 5vw, 3.25rem);
	width: auto;
}

.signoff-tagline {
	margin-block-start: 0.6rem;
	font-size: 0.9rem;
	color: var(--muted);
}

.quiet-kicker {
	font-family: var(--font-display);
	font-size: 0.7rem;
	font-weight: 400;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--accent-text);
	margin-block-end: 0.9rem;
}

.quiet-label,
.quiet-legend {
	display: block;
	font-family: var(--font-mono);
	font-size: 0.72rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--muted);
	margin-block-end: 0.45rem;
}

.quiet-entry {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

.quiet-input {
	flex: 1 1 12rem;
	min-width: 0;
	padding: 0.6rem 0.75rem;
	background-color: var(--canvas);
	border: 1px solid var(--hairline-bright);
	color: var(--text);
	font-size: 0.88rem;

	&::placeholder {
		color: var(--muted);
		opacity: 1;
	}

	&:hover {
		border-color: var(--muted-2);
	}

	&:user-invalid {
		border-color: var(--accent);
	}
}

/* min-height rather than padding alone: at the narrowest widths this landed on 39px,
   one pixel under the floor, and it is the button on the site's only signup
   (audited 2026-08-28). */
.quiet-submit {
	flex: 0 0 auto;
	min-height: 2.75rem;
	padding: 0.6rem 1rem;
	border: 0;
	background-color: var(--text);
	color: var(--canvas);
	font-size: 0.84rem;
	font-weight: 700;
	cursor: pointer;

	&:hover {
		background-color: var(--accent);
		color: var(--accent-ink);
	}
}

.quiet-lanes {
	margin-block-start: 1.1rem;
	border: 0;
	padding: 0;
	margin-inline: 0;
}

/* A checked lane fills bone, not accent: an orange fill on dark is the Trending
   treatment and nothing else on the page may wear it. Bone is already this form's
   action colour, so the chip agrees with its own submit. */
.lane-chip-quiet {
	padding: 0.3rem 0.6rem;
	border: 1px solid var(--hairline-bright);
	color: var(--muted);
	font-size: 0.78rem;
	font-weight: 500;

	&:hover {
		background-color: transparent;
		border-color: var(--muted-2);
		color: var(--text);
	}

	&:has(input:checked) {
		background-color: var(--text);
		border-color: var(--text);
		color: var(--canvas);

		.lane-tick {
			color: var(--canvas);
		}
	}
}

.quiet-status {
	font-size: 0.84rem;
	font-weight: 600;
	color: var(--accent-text);

	&[data-state] {
		margin-block-start: 1rem;
	}

	&[data-state='error'] {
		color: var(--text);

		.status-warn {
			color: var(--accent-text);
		}
	}
}

.quiet-fine {
	margin-block-start: 1rem;
	font-size: 0.72rem;
	color: var(--muted-2);
}

/* The pre-launch newsletter overlay. Every -form-wrap is a positioning context regardless
   of whether comingSoon is on, so toggling the flag never changes this element's box model
   - only .has-coming-soon-overlay adds the dimmed, non-interactive state underneath.
   Substring match ("contains"), not "ends with": the wrapper carries a second class
   (has-coming-soon-overlay) appended after band-form-wrap/quiet-form-wrap/page-form-wrap
   exactly when this rule needs to fire, and an ends-with selector stops matching the
   moment anything follows it in the attribute value. */
[class*="-form-wrap"] {
	position: relative;
}

.has-coming-soon-overlay [class*="-form"] {
	filter: blur(2px) saturate(0.6);
	opacity: 0.55;
	user-select: none;
}

.newsletter-coming-soon-overlay {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	text-align: center;
	padding: 1rem;
	background-color: color-mix(in srgb, var(--canvas) 55%, transparent);
}

.newsletter-coming-soon-badge {
	padding: 0.6rem 1.2rem;
	background-color: var(--accent);
	color: var(--accent-ink);
	font-family: var(--font-display);
	font-size: 0.85rem;
	font-weight: 700;
	letter-spacing: 0.02em;
	box-shadow: 0 4px 18px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* The shell's footer, sitting on the sign-off's ground as its closing strip. The bare
   flex layout of the link row lives in global.css, because base.njk renders this footer
   on every page including the two internal boards, which load no chrome. */
.site-footer {
	background-color: var(--footer-ground);
	border-block-start: 1px solid var(--hairline);
	padding: 1.4rem var(--gutter) clamp(2rem, 3.5vw, 2.75rem);
	color: var(--muted-2);
	font-size: 0.8rem;

	> * {
		width: 100%;
		max-width: calc(var(--measure) - 2 * var(--gutter));
		margin-inline: auto;
	}

	a {
		color: var(--muted);

		&:hover {
			color: var(--accent-text);
		}
	}
}

/* The grid itself (display: grid, the column count, the gap) lives in global.css: the
   footer renders on every page including the two internal boards, which load none of
   the site chrome, and a footer with 22 links in one unstructured column would be worse
   there than the plain flex-wrap row it replaced. Two columns by default, widening at
   the breakpoints below. Coverage is this footer's whole reason to exist: the 12 type
   hubs had no link anywhere else on the site before it (2026-08-27), so this is where a
   reader actually finds /releases/, the site's biggest single page. */
.footer-heading {
	font-family: var(--font-mono);
	font-size: 0.75rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--muted-2);
	margin-block-end: 0.65rem;
}

.footer-group a {
	font-size: 0.86rem;
	text-decoration: none;

	&:hover {
		text-decoration: underline;
		text-underline-offset: 4px;
	}
}

.footer-sibling {
	margin-block-start: 1rem;
	font-family: var(--font-mono);
	font-size: 0.72rem;
	letter-spacing: 0.04em;
	color: var(--muted-2);
}

/* ------------------------------------------------------- the award stage rail */

/*
	THE STAGE RAIL, and it is here for the reason the tracker map above is here: two
	pages draw it. The award band on the home page runs it under its lockup, and
	/award-watch/ runs the same rail under its timeline for the featured edition. It
	lived in home.css while the home page was the only page that had one; splitting it
	between two stylesheets now would be exactly the drift the map above exists to
	prevent, so the whole grammar moved here unchanged and home.css lost its copy.

	The award cycle, end to end. Three markers and they are shapes before they are
	colours: a done stage is filled, the live one is a thick ring, an upcoming one is a
	thin hollow ring. The stage that is next also carries aria-current="step". The rail
	takes its band's accent like everything else marked in the section, so award watch
	reads in one colour rather than a lilac head sitting over an orange rail.

	Vertical on small screens, horizontal from 768 up (the breakpoint block below). The
	connector is drawn per stage rather than as one line behind the row, so a wrap never
	leaves a rule hanging in mid air.
*/

.stage-track {
	display: grid;
	margin-block-start: clamp(2rem, 3.5vw, 2.75rem);
}

.stage {
	position: relative;
	display: grid;
	grid-template-columns: 1.5rem minmax(0, 1fr);
	grid-template-areas:
		'marker name'
		'marker note';
	column-gap: 0.85rem;
	padding-block-end: 1.2rem;

	&::before {
		content: '';
		position: absolute;
		inset-block: 1.3rem -0.05rem;
		inset-inline-start: calc(0.5rem - 1px);
		width: 2px;
		background-color: var(--hairline-bright);
	}

	&:last-child {
		padding-block-end: 0;

		&::before {
			display: none;
		}
	}
}

/* A finished stage paints its trailing connector in the band accent, so the rail
   reads as progress: lilac up to the live marker, hairline past it. */
.stage-done::before {
	background-color: var(--band-accent);
}

.stage-marker {
	position: relative;
	grid-area: marker;
	justify-self: start;
	align-self: start;
	width: 1rem;
	height: 1rem;
	margin-block-start: 0.14rem;
	border: 2px solid var(--hairline-bright);
	border-radius: 50%;
	background-color: var(--panel);
}

.stage-done .stage-marker {
	border-color: var(--band-accent);
	background-color: var(--band-accent);
}

.stage-current .stage-marker,
.stage-next .stage-marker {
	border-width: 3px;
	border-color: var(--band-accent);
	background-color: var(--canvas);
}

/* The live stage pings: a ring that swells out of the marker and fades. Status
   telemetry, not an entrance. No standing halo: at rest the live marker is just
   the thick orange ring, and reduced motion keeps exactly that. */
@media (prefers-reduced-motion: no-preference) {
	.stage-current .stage-marker::after,
	.stage-next .stage-marker::after {
		content: '';
		position: absolute;
		top: 50%;
		left: 50%;
		width: 1rem;
		height: 1rem;
		border: 2px solid var(--band-accent);
		border-radius: 50%;
		opacity: 0;
		transform: translate(-50%, -50%) scale(1);
		animation: stage-ping 2.4s cubic-bezier(0, 0, 0.2, 1) infinite;
	}
}

/* Born exactly on the marker's outline and expands outward only, so the ring
   never shows inside the marker. Explicit translate keeps it dead-centered
   regardless of the marker's border width. */
@keyframes stage-ping {
	0% {
		transform: translate(-50%, -50%) scale(1);
		opacity: 0.6;
	}

	70%,
	100% {
		transform: translate(-50%, -50%) scale(2.1);
		opacity: 0;
	}
}

.stage-name {
	grid-area: name;
	font-family: var(--font-display);
	font-size: 0.85rem;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--muted);
}

.stage-done .stage-name {
	color: var(--text);
}

.stage-current .stage-name,
.stage-next .stage-name {
	color: var(--band-accent-text);
}

.stage-note {
	grid-area: note;
	margin-block-start: 0.2rem;
	font-family: var(--font-mono);
	font-size: 0.82rem;
	line-height: 1.45;
	color: var(--muted);
}

/* ------------------------------------------------------------ breakpoints */

@media (min-width: 768px) {
	.footer-columns {
		grid-template-columns: repeat(3, 1fr);
	}

	.tagline {
		display: block;
		padding-inline-start: 0.85rem;
		border-inline-start: 1px solid var(--hairline-bright);
	}

	/* The eight genres as four across and two down, in every form on the site: the
	   band on its orange plate, the quiet one in the sign-off, and the newsletter
	   page's own. Below this the chips wrap as a row of pills, which is the only
	   shape that fits a phone. The cap keeps a chip chip-sized when the form itself
	   has the whole measure to spend. */
	.lane-chips {
		display: grid;
		grid-template-columns: repeat(4, minmax(0, 1fr));
		max-width: 32rem;
	}

	.signoff-inner {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
		gap: 2rem 3rem;
		align-items: start;
	}

	/* The quiet form holds to the right edge of the sign-off, so its own right edge
	   meets the content edge the brand block starts from. The words inside it stay
	   left-aligned: only the box moves. */
	.quiet-form {
		justify-self: end;
		width: 100%;
		max-width: 26rem;
	}

	/* The stage rail turns horizontal: markers on one line, each with its label and
	   its note beneath, the connector running from one marker to the next. */
	.stage-track {
		display: flex;
		flex-wrap: wrap;
		row-gap: 1.75rem;
	}

	/*
		Horizontal alignment: the first stage sits on the left edge, the last on
		the right, and the ones between center in their cells. Each stage draws
		up to two half-connectors, a leading one from its box edge to its marker
		and a trailing one from its marker onward, so the line always travels
		marker to marker no matter where a stage's marker sits in its cell. Both
		run through the marker's vertical center (1rem marker, center 0.5rem).
	*/
	/* End cells take half the flex of middle cells (1-2-2-2-1), which lands the
	   five markers at exactly 0, 25, 50, 75, and 100 percent of the rail: even
	   spacing with the first flush left and the last flush right. */
	.stage {
		flex: 2 1 0;
		min-width: 0;
		grid-template-columns: minmax(0, 1fr);
		grid-template-areas:
			'marker'
			'name'
			'note';
		padding-block-end: 0;
		padding-inline: 0.35rem;

		&::before,
		&::after {
			content: '';
			position: absolute;
			inset-block: calc(0.5rem - 1px) auto;
			width: auto;
			height: 2px;
			background-color: var(--hairline-bright);
		}

		&::before {
			inset-inline: 0 calc(50% + 0.85rem);
		}

		&::after {
			inset-inline: calc(50% + 0.85rem) 0;
		}

		&:not(:first-child):not(:last-child) {
			justify-items: center;
			text-align: center;

			.stage-marker {
				justify-self: center;
			}
		}

		&:first-child {
			flex: 1 1 0;
			padding-inline-start: 0;

			&::before {
				display: none;
			}

			&::after {
				inset-inline: 1.35rem 0;
			}
		}

		&:last-child {
			flex: 1 1 0;
			padding-inline-end: 0;
			justify-items: end;
			text-align: right;

			.stage-marker {
				justify-self: end;
			}

			&::before {
				display: block;
				inset-inline: 0 1.35rem;
			}

			&::after {
				display: none;
			}
		}
	}

	/* Progress follows the line through both halves of a segment: a finished
	   stage paints its trailing half, and the stage right after it paints its
	   leading half to meet it at the marker. */
	.stage-done + .stage::before {
		background-color: var(--band-accent);
	}

	.stage-done::after {
		background-color: var(--band-accent);
	}

	.stage-marker {
		margin-block-start: 0;
	}

	.stage-name {
		margin-block-start: 0.85rem;
	}
}

@media (min-width: 1024px) {
	.footer-columns {
		grid-template-columns: repeat(5, 1fr);
	}

	.utility-nav {
		display: block;
	}

	.nav-toggle,
	.nav-panel,
	.nav-backdrop {
		display: none;
	}

	.signoff-inner {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
		gap: 2.25rem 5rem;
	}
}

@media (min-width: 1440px) {
	/* The eight genres used to fit inline from 1024 up and the fade came off there.
	   Four tracker links joined the line, and the line now needs the full measure to
	   hold them, so the fade stays on until here. Taking it off at 1024 would drop the
	   only affordance the rail has at exactly the widths where it still scrolls. */
	.site-rail-inner {
		-webkit-mask-image: none;
		mask-image: none;
	}
}

/* --------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
	.lane-chip {
		transition: none !important;
	}

	/* The drawer still opens and closes; it just arrives rather than slides. visibility
	   keeps switching either way, so the closed sheet stays out of the tab order. */
	.nav-panel,
	.nav-backdrop {
		transition: none !important;
	}
}
