/*
 * Groove Site — global layer. Loads on every page and in the editor.
 *
 * theme.json owns the tokens (as --wp--preset--*) and element defaults. This
 * file does what theme.json cannot:
 *   1. the dark scheme, by redefining the preset colours themselves, so every
 *      has-*-color class and every var() use flips with no per-component rules;
 *   2. a metric-matched fallback face so the Inter swap does not shift layout;
 *   3. focus, selection, motion and text-wrapping defaults;
 *   4. the header and scheme toggle, which appear on every template.
 *
 * Custom properties the other stylesheets rely on:
 *   --gs-ease        the house easing curve
 *   --gs-rule        the heavy poster rule width (2px)
 *   --gs-header-h    sticky header height, for scroll-margin and sticky offsets
 *   --gs-noise       the paper-grain background image
 *   --gs-panel-shadow  an open drop-down panel's lift, plus a dim over the
 *                    page below it (never above, so its header stays clear)
 *   --gs-rise        colour stops, ink into the signal gradient, that scroll
 *                    progress runs along (header rule, docs TOC marker)
 */

/* ------------------------------------------------------------------ fonts */

/*
 * Arial/Helvetica scaled to Inter's metrics, so the swap is near-invisible
 * and causes no CLS. Values from Inter's hhea metrics against Arial.
 */
@font-face {
	font-family: "Inter Fallback";
	src: local("Arial"), local("Helvetica Neue"), local("Helvetica");
	size-adjust: 107.06%;
	ascent-override: 90.49%;
	descent-override: 22.56%;
	line-gap-override: 0%;
}

/* ----------------------------------------------------------------- tokens */

:root {
	color-scheme: light dark;

	--gs-ease: var(--wp--custom--ease, cubic-bezier(0.16, 1, 0.3, 1));
	--gs-rule: var(--wp--custom--rule, 2px);
	--gs-header-h: 4rem;
	--gs-gradient: var(--wp--preset--gradient--signal);
	--gs-noise: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E");
	--gs-scrim: rgb(15 15 15 / 0.2);
	/* The second shadow starts at the panel's top edge and runs down. */
	--gs-panel-shadow: 0 1.5rem 2rem -1.5rem rgb(0 0 0 / 0.35), 0 100vmax 0 100vmax var(--gs-scrim);
	/* Ink, then the signal colours: the run the header rule fills with. */
	--gs-rise: var(--wp--preset--color--ink) 0% 5%, var(--wp--preset--color--accent-1) 20%, var(--wp--preset--color--accent-2) 60%, var(--wp--preset--color--accent-3) 100%;
}

/* Dark: the system asks for it and the visitor has not pinned light... */
@media (prefers-color-scheme: dark) {
	:root:not([data-scheme="light"]) {
		--wp--preset--color--base: #0F0F0F;
		--wp--preset--color--ink: #EAE6DE;
		--wp--preset--color--muted: #A6A29A;
		--wp--preset--color--line: #EAE6DE26;
		--wp--preset--color--surface: #1A1A18;
		--gs-scrim: rgb(0 0 0 / 0.55);
	}
}

/* ...or the visitor pinned dark (set before first paint by inc/assets.php). */
:root[data-scheme="dark"] {
	--wp--preset--color--base: #0F0F0F;
	--wp--preset--color--ink: #EAE6DE;
	--wp--preset--color--muted: #A6A29A;
	--wp--preset--color--line: #EAE6DE26;
	--wp--preset--color--surface: #1A1A18;
	--gs-scrim: rgb(0 0 0 / 0.55);
	color-scheme: dark;
}

:root[data-scheme="light"] {
	color-scheme: light;
}

/* ------------------------------------------------------------------- base */

html {
	-webkit-text-size-adjust: 100%;
	text-size-adjust: 100%;
	accent-color: var(--wp--preset--color--accent-3);
}

@media (prefers-reduced-motion: no-preference) {
	html {
		scroll-behavior: smooth;
	}
}

body {
	background-image: var(--gs-noise);
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	font-optical-sizing: auto;
	overflow-x: clip;
}

/*
 * The footer meets the bottom of the window on a short page: the page is at
 * least a screen tall, and the part before the footer takes up the slack.
 * The admin bar pushes the page down by its own height, so it comes off.
 */
.wp-site-blocks {
	display: flex;
	flex-direction: column;
	min-block-size: calc(100dvh - var(--wp-admin--admin-bar--height, 0px));
}

.wp-site-blocks > * {
	flex-shrink: 0;
}

.wp-site-blocks > :has(+ footer:last-child) {
	flex-grow: 1;
}

::selection {
	background-color: var(--wp--preset--color--ink);
	color: var(--wp--preset--color--base);
}

:where(h1, h2, h3, h4, h5, h6, .wp-block-post-title) {
	text-wrap: balance;
}

:where(p, li, dd, figcaption) {
	text-wrap: pretty;
}

/* Anchored headings clear the sticky header. */
:where(h1, h2, h3, h4, [id]) {
	scroll-margin-top: calc(var(--gs-header-h) + 1.5rem);
}

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: 2px solid var(--wp--preset--color--accent-3);
	outline-offset: 3px;
}

:where(code, kbd, samp) {
	font-family: var(--wp--preset--font-family--mono);
	font-size: 0.9em;
}

/* Code reads as typed: JetBrains Mono would draw => and :: as single glyphs. */
:where(code, kbd, samp, pre) {
	font-variant-ligatures: none;
}

:where(p, li, td) > code {
	padding: 0.1em 0.35em;
	background: var(--wp--preset--color--surface);
	overflow-wrap: anywhere;
}

img,
video {
	max-width: 100%;
	height: auto;
}

/* ---------------------------------------------------------------- header */

/*
 * .gs-header is the template-part wrapper (<header>), not the inner group:
 * a sticky element only sticks inside a taller parent, and the wrapper's
 * parent is the page.
 */
.gs-header {
	position: sticky;
	top: 0;
	z-index: 50;
	min-height: var(--gs-header-h);
	background-color: var(--wp--preset--color--base);
	background-image: var(--gs-noise);
	border-bottom: var(--gs-rule) solid var(--wp--preset--color--ink);
}

/* Keep the sticky header below the admin bar for logged-in viewers. */
.admin-bar .gs-header {
	top: var(--wp-admin--admin-bar--height, 32px);
}

.gs-header__inner {
	min-height: var(--gs-header-h);
	padding: 0.75rem var(--wp--preset--spacing--gutter);
	gap: 1rem 1.5rem;
}

.gs-header__tools {
	gap: 0 1.25rem;
}

/* Apart from the links: it acts on the page, it does not lead anywhere. */
.gs-header__tools > .gs-scheme-toggle {
	margin-inline-start: 0.75rem;
	/* The icon, not its hit area, lines up with the gutter. */
	margin-inline-end: -0.6875rem;
}

.gs-header .wp-block-site-title a {
	text-decoration: none;
}

/*
 * The section mark (docs pages only): the section's name after the site
 * title, set off by a hairline, as "Groove Folios | Docs". A quiet subtitle
 * to the logotype, not a second one: the nav links' face and size, but ink,
 * not their muted grey, which would say "somewhere else" when the reader is
 * already here; and regular weight, so it never competes with the current
 * guide. The hairline is a border, so forced colours keep it.
 */
.gs-header__brand {
	align-items: center;
	gap: 0;
}

.gs-section-mark {
	display: inline-flex;
	align-items: center;
	min-height: 2.75rem; /* 44px touch target */
	font-size: var(--wp--preset--font-size--small);
	font-weight: 450;
	color: var(--wp--preset--color--ink);
	text-decoration: none;
	text-underline-offset: 0.25em;
	text-decoration-thickness: 1px;
}

.gs-section-mark::before {
	content: "";
	block-size: 1.125em;
	margin-inline: 0.875rem;
	border-inline-start: 1px solid var(--wp--preset--color--line);
}

.gs-section-mark:hover {
	text-decoration-line: underline;
}

/* Under 360px it would push Menu onto a second row; the name stands alone. */
@media (max-width: 22.49rem) {
	.gs-section-mark {
		display: none;
	}
}

/*
 * Energy on scroll, as on the landing page. Logotype and rule share one run
 * of colour: ink, then the signal gradient. The logotype sweeps through it as
 * the page scrolls, so it starts dark and warms up; the header rule becomes a
 * faint track that the same run fills from the start edge, so its leading
 * edge carries about the logotype's current colour. Colour is plain by 15%
 * of the page. Both run on the page's scroll timeline, with no script.
 *
 * The logotype's stops (--gs-rise-logo) differ from the rule's (--gs-rise)
 * because the geometry does: the rule shows its whole run
 * and its tip sits at the scroll position, while the logotype shows a third
 * of a 300% gradient at a time, so it keeps ink across its first third (dark
 * at the top) and reaches red at the window's far edge by 15%.
 *
 * Where scroll timelines are missing, or motion is reduced, the logotype
 * holds its dark start and the rule stays solid ink, as it always was. The
 * rule only fills where the header stays in view (48rem and up); on phones
 * it scrolls away, so it stays solid there.
 */
.gs-header {
	--gs-rise-logo: var(--wp--preset--color--ink) 0% 30%, var(--wp--preset--color--accent-1) 43%, var(--wp--preset--color--accent-2) 70%, var(--wp--preset--color--accent-3) 100%;
}

@supports (background-clip: text) or (-webkit-background-clip: text) {
	.gs-header .wp-block-site-title a {
		background-image: linear-gradient(120deg, var(--gs-rise-logo));
		background-size: 300% 300%;
		background-position: 0% 50%;
		-webkit-background-clip: text;
		background-clip: text;
		color: transparent;
	}
}

@keyframes gs-sweep {
	from {
		background-position: 0% 50%;
	}

	to {
		background-position: 100% 50%;
	}
}

/* A reveal, not a stretch: the gradient stays put under a moving edge. */
@keyframes gs-fill {
	from {
		clip-path: inset(0 100% 0 0);
	}

	to {
		clip-path: inset(0);
	}
}

@media (prefers-reduced-motion: no-preference) {
	@supports (animation-timeline: scroll()) {
		.gs-header .wp-block-site-title a {
			animation: gs-sweep linear both;
			animation-timeline: scroll(root);
		}
	}
}

@media (prefers-reduced-motion: no-preference) and (min-width: 48rem) {
	@supports (animation-timeline: scroll()) {
		.gs-header {
			border-bottom-color: var(--wp--preset--color--line);
		}

		/*
		 * The fill sits over the track. A page too short to scroll has no
		 * active timeline, so the animation does nothing and the fill shows
		 * whole, which is also the truth (it is all in view).
		 */
		.gs-header::after {
			content: "";
			position: absolute;
			inset-inline: 0;
			inset-block-end: calc(var(--gs-rule) * -1);
			block-size: var(--gs-rule);
			background-image: linear-gradient(to right, var(--gs-rise));
			animation: gs-fill linear both;
			animation-timeline: scroll(root);
		}

		/* Mirrored, so it fills from the start edge, ink first. */
		[dir="rtl"] .gs-header::after {
			scale: -1 1;
		}
	}
}

@media (forced-colors: active) {
	.gs-header .wp-block-site-title a {
		background: none;
		color: LinkText;
	}

	.gs-header::after {
		background: CanvasText;
	}
}

/* ------------------------------------------------------------ primary nav */

/*
 * Plain links, as navigation reads everywhere else: the body face, sentence
 * case. The rest are muted; the current guide is ink and heavier. Weight is
 * the cue that is not colour, and it is set at load and never changes, so
 * no width jumps.
 */
.gs-site-nav__list {
	display: flex;
	flex-wrap: wrap;
	gap: 0 1.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.gs-site-nav a {
	display: inline-flex;
	align-items: center;
	min-height: 2.75rem; /* 44px touch target */
	font-size: var(--wp--preset--font-size--small);
	font-weight: 450;
	text-decoration: none;
	color: var(--wp--preset--color--muted);
	transition: color 0.2s var(--gs-ease);
}

.gs-site-nav a:hover,
.gs-site-nav a[aria-current] {
	color: var(--wp--preset--color--ink);
}

.gs-site-nav a[aria-current] {
	font-weight: 650;
}

/* Disclosure chevrons (Menu, and the docs TOC bar): point up while open. */
.gs-chevron {
	flex: none;
	transition: rotate 0.2s var(--gs-ease);
}

.gs-site-nav:has(:popover-open) .gs-chevron {
	rotate: 180deg;
}

.gs-site-nav__toggle {
	display: none;
}

/*
 * Wide: the list is inline in the header. It is a popover (for narrow
 * screens), so the UA's popover styles are undone here; author styles
 * outrank them, display included.
 */
@media (min-width: 48rem) {
	.gs-site-nav__list[popover] {
		position: static;
		inset: auto;
		display: flex;
		inline-size: auto;
		block-size: auto;
		margin: 0;
		padding: 0;
		border: 0;
		background: none;
		color: inherit;
		overflow: visible;
	}
}

/*
 * Narrow: one header row (name, Menu, scheme). The links are a panel under
 * the header, opened by Menu. The header scrolls away rather than staying
 * pinned; on docs pages the TOC bar is what stays (docs.css).
 */
@media (max-width: 47.99rem) {
	.gs-header {
		position: static;
		anchor-name: --gs-header;
	}

	:where(h1, h2, h3, h4, [id]) {
		scroll-margin-top: 1.5rem;
	}

	/*
	 * One row holds the name, the section mark, Menu and the scheme button.
	 * The mark hugs the name and the two controls close up to the end, so
	 * the mark reads as part of the name, not as the first of the links.
	 */
	.gs-section-mark::before {
		margin-inline: 0.625rem;
	}

	.gs-header__tools {
		gap: 0 0.5rem;
	}

	.gs-header__tools > .gs-scheme-toggle {
		margin-inline-start: 0;
	}

	.gs-site-nav__toggle {
		display: inline-flex;
		align-items: center;
		gap: 0.4em;
		min-height: 2.75rem;
		padding: 0;
		border: 0;
		background: none;
		color: var(--wp--preset--color--ink);
		font: inherit;
		font-size: var(--wp--preset--font-size--small);
		font-weight: 500;
		cursor: pointer;
	}

	/*
	 * The open panel: a tint off the page, lifted by a soft shadow, with the
	 * page below it dimmed, so it reads as a layer on top, not more page.
	 */
	.gs-site-nav__list[popover] {
		flex-direction: column;
		flex-wrap: nowrap;
		inline-size: auto;
		margin: 0;
		padding: 0.5rem var(--wp--preset--spacing--gutter) 1rem;
		border: 0;
		background: var(--wp--preset--color--surface);
		color: inherit;
		box-shadow: var(--gs-panel-shadow);
		transition: opacity 0.2s var(--gs-ease);
		/* Where anchors are unknown: straight under the unscrolled header. */
		inset: calc(var(--gs-header-h) + var(--gs-rule)) 0 auto;
	}

	.gs-site-nav__list:popover-open {
		display: flex;
	}

	@starting-style {
		.gs-site-nav__list:popover-open {
			opacity: 0;
		}
	}

	/*
	 * Closed. The base rule's display outranks the UA's popover hiding, so it
	 * is restated. Where :popover-open is unknown this whole rule is dropped
	 * and the list simply shows.
	 */
	.gs-site-nav__list[popover]:not(:popover-open) {
		display: none;
	}

	.gs-site-nav__list a {
		min-height: 3rem;
		font-size: var(--wp--preset--font-size--medium);
	}

	@supports (anchor-name: --a) {
		.gs-site-nav__list[popover] {
			inset: auto;
			position-anchor: --gs-header;
			position-area: bottom span-all;
			justify-self: stretch;
		}
	}
}

/* ---------------------------------------------------------- scheme toggle */

/*
 * An action, so a button, not a link: an icon in a 44px square that takes a
 * faint fill on hover. It shows the scheme it switches to (view.js sets
 * data-next); its name and tooltip say so.
 */
.gs-scheme-toggle {
	display: inline-grid;
	place-items: center;
	inline-size: 2.75rem;
	block-size: 2.75rem;
	padding: 0;
	border: 0;
	background: none;
	color: var(--wp--preset--color--muted);
	cursor: pointer;
	transition: color 0.2s var(--gs-ease), background-color 0.2s var(--gs-ease);
}

.gs-scheme-toggle:hover {
	background-color: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--ink);
}

.gs-scheme-toggle__icon {
	grid-area: 1 / 1;
}

.gs-scheme-toggle:not([data-next="light"]) .gs-scheme-toggle__icon--sun,
.gs-scheme-toggle[data-next="light"] .gs-scheme-toggle__icon--moon {
	display: none;
}

.gs-scheme-toggle[hidden] {
	display: none;
}

/*
 * With script on, hold the button's space until view.js reveals it, so the
 * links beside it do not jump (CLS). Without script it stays display: none.
 */
.gs-js .gs-scheme-toggle[hidden] {
	display: inline-grid;
	visibility: hidden;
}

/* Wait a moment on hover before interestfor opens the tooltip, as a tooltip should. */
.gs-scheme-toggle {
	interest-delay: 0.3s 0.15s;
}

/*
 * The tooltip: a popover="hint" (blocks/scheme-toggle). It lives in the top
 * layer, so nothing clips it and it needs no z-index. Ink on paper, the
 * page's strongest contrast, so it is plain it belongs to the button and
 * not to the page. It fades in from @starting-style; display and overlay
 * are held for the fade out.
 */
.gs-tip {
	inset: auto;
	margin: 0;
	padding: 0.375rem 0.625rem;
	border: 0;
	background: var(--wp--preset--color--ink);
	color: var(--wp--preset--color--base);
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 500;
	line-height: 1.3;
	white-space: nowrap;
	opacity: 0;
	transition: opacity 0.15s var(--gs-ease), display 0.15s allow-discrete, overlay 0.15s allow-discrete;
}

.gs-tip:popover-open {
	opacity: 1;
}

@starting-style {
	.gs-tip:popover-open {
		opacity: 0;
	}
}

/*
 * Below the button, ending at its right edge, as suits a control at the end
 * of a header; where that would leave the viewport (the landing page's
 * button sits at the left), starting at its left edge; no room below,
 * above. Centring would push it flush against the viewport's edge, since
 * the shift that keeps it inside ignores margins.
 */
@supports (anchor-name: --a) {
	.gs-tip {
		position-area: bottom span-left;
		position-try-fallbacks: bottom span-right, flip-block;
		margin-block-start: 0.5rem;
	}
}

/* ------------------------------------------------------------------ main */

.gs-main {
	padding-block: var(--wp--preset--spacing--60);
}

.gs-kicker {
	font-family: var(--wp--preset--font-family--mono);
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--wp--preset--color--muted);
}

.gs-entry {
	padding-block: 1.5rem;
	border-top: 1px solid var(--wp--preset--color--line);
}

/* ---------------------------------------------------------------- footer */

.gs-footer {
	font-family: var(--wp--preset--font-family--mono);
	font-size: var(--wp--preset--font-size--x-small);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	border-top: 1px solid var(--wp--preset--color--line);
}

.gs-footer__inner {
	padding: 2rem var(--wp--preset--spacing--gutter);
	gap: 0.5rem 2rem;
}

.gs-footer__inner p {
	margin: 0;
}

.gs-footer a {
	text-decoration: none;
}

.gs-footer a:hover {
	text-decoration: underline;
}

/*
 * The Footer menu, between the copyright and the licence line in both
 * footers. It takes the footer's mono caps and colour; hover underlines,
 * as in the rest of the footer.
 */
.gs-footer-links ul {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem 1.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.gs-footer-links a {
	color: inherit;
	text-decoration: none;
}

.gs-footer-links a:hover,
.gs-footer-links a[aria-current] {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.gs-footer-links__empty {
	margin: 0;
	opacity: 0.6;
}

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

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
	}
}
