/* ---------------------------------------------------------------
   phillazar.xyz
   --------------------------------------------------------------- */

@font-face {
	font-family: "Geist";
	src: url("/fonts/geist-latin-wght-normal.woff2") format("woff2");
	font-weight: 100 900;
	font-style: normal;
	font-display: swap;
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
		U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
		U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
	color-scheme: light;

	--bg: rgb(238, 230, 218);
	--ink: rgba(0, 0, 0, 0.75);
	--ink-strong: rgba(0, 0, 0, 0.85);
	--ink-mute: rgba(0, 0, 0, 0.45);
	--line: rgba(0, 0, 0, 0.12);

	/* Type */
	--type-h1: clamp(1.6rem, 2.6vw, 2.1rem);
	--type-h2: clamp(1rem, 1.4vw, 1.15rem);
	--type-body: 1rem;

	/* Rhythm */
	--pad: 1.75rem;
	--bar-pad: 1.5rem;
	--gap: 0.5rem;

	/* Grid columns on the Music page. 10 mixes / 5 columns = exactly two rows;
	   fewer columns would mean a third row and a much taller page. */
	--cols: 5;
	--grid-rows: 2;

	/* Vertical pieces of the Music page, named so the grid can subtract them
	   from the viewport. Update --gear-lines if the number of Audio Gear
	   pairings changes. */
	--grid-top: clamp(0.75rem, 1.5vh, 1.25rem);
	--gear-top: clamp(1.1rem, 2.5vh, 1.75rem);
	--gear-lines: 3;
	--gear-h: calc(var(--gear-top) + 2.48rem + 1px + var(--gear-lines) * 1.96rem);

	/* Max width for prose. The mix grid deliberately runs full bleed. */
	--measure: 68rem;
	--measure-text: 40rem;

	--header-h: calc(var(--bar-pad) + var(--type-h1));
	--footer-h: calc(var(--bar-pad) + var(--type-h2) * 1.2);

	--font: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
		"Helvetica Neue", Arial, sans-serif;
}

@media (max-width: 560px) {
	:root {
		--pad: 1rem;
		--bar-pad: 1rem;
		--gap: 0.375rem;
		--cols: 2;
		--grid-rows: 5;
	}
}

*,
*::before,
*::after {
	box-sizing: border-box;
}

html {
	background: var(--bg);
	-webkit-text-size-adjust: 100%;
}

body {
	margin: 0;
	min-height: 100vh;
	min-height: 100dvh;
	display: flex;
	flex-direction: column;
	background: var(--bg);
	color: var(--ink);
	font-family: var(--font);
	font-size: var(--type-body);
	font-weight: 400;
	line-height: 1.5;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

a {
	color: inherit;
	text-decoration: none;
}

a:active {
	opacity: 0.7;
}

img {
	display: block;
	max-width: 100%;
	height: auto;
	border: 0;
}

:focus-visible {
	outline: 1.5px solid var(--ink-strong);
	outline-offset: 3px;
}

.wrap {
	width: 100%;
}

.wrap--prose {
	max-width: var(--measure);
}

/* ---------------------------------------------------------------
   Fixed bars: name above, navigation below
   --------------------------------------------------------------- */

.bar {
	position: fixed;
	left: 0;
	right: 0;
	z-index: 4;
	background: var(--bg);
}

.bar--top {
	top: 0;
	padding: var(--bar-pad) var(--pad) 0;
}

.bar--bottom {
	bottom: 0;
	padding: 0 var(--pad) var(--bar-pad);
}

.bar h1 {
	margin: 0;
	padding: 0;
	font-size: var(--type-h1);
	font-weight: 450;
	line-height: 1;
	letter-spacing: -0.021em;
	color: var(--ink-strong);
}

.nav {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4em 1.5em;
	font-size: var(--type-h2);
	line-height: 1.2;
	letter-spacing: -0.008em;
}

.nav a {
	color: var(--ink-mute);
	transition: color 160ms ease;
}

.nav a.is-current {
	color: var(--ink-strong);
}

@media (hover: hover) {
	.nav a:hover {
		color: var(--ink-strong);
	}
}

/* ---------------------------------------------------------------
   Main column
   --------------------------------------------------------------- */

main {
	flex: 1 0 auto;
	display: flex;
	flex-direction: column;
	justify-content: flex-start;
	padding: calc(var(--header-h) + var(--gap)) var(--pad)
		calc(var(--footer-h) + var(--gap) * 2);
}

main > .wrap {
	flex: 0 1 auto;
}

/* These modifiers must stay at the same specificity as the rule above and
   after it in source order, or the base `flex: 0 1 auto` wins and neither
   has any effect. */

/* Centres a short page's content between the bars rather than leaving the
   slack pooled above the nav. */
main > .wrap--center {
	flex: 1 1 auto;
	display: flex;
	flex-direction: column;
	justify-content: center;
}

/* Distributes a short page's leftover height across its rows. Type size
   never changes — only spacing. */
main > .wrap--fill {
	flex: 1 1 auto;
	display: flex;
	flex-direction: column;
}

/* Intro copy that sits above a page's content. */
.lede {
	max-width: var(--measure-text);
	margin: 0 0 clamp(1.5rem, 4vh, 2.75rem);
}

.lede p {
	margin: 0 0 1em;
}

.lede p:last-child {
	margin-bottom: 0;
}

.lede a {
	border-bottom: 1px solid var(--line);
	transition: border-color 160ms ease;
}

@media (hover: hover) {
	.lede a:hover {
		border-bottom-color: var(--ink-strong);
	}
}

/* ---------------------------------------------------------------
   Mix grid (Music)
   --------------------------------------------------------------- */

.grid {
	display: grid;
	grid-template-columns: repeat(var(--cols), 1fr);
	gap: var(--gap);
	/* Sits directly under the name, so it needs a little room above. */
	margin-top: var(--grid-top);
}

/* Tiles are square, so a wider grid is also a taller one. On a short window
   that would push the gear list past the fold. Cap the grid at the width
   whose height still fits, derived from what the rest of the page uses. On a
   normal window this never binds and the grid stays full bleed. */
@media (min-width: 561px) {
	.grid {
		--chrome: calc(
			var(--header-h) + var(--gap) + var(--grid-top) + var(--gear-h) +
			var(--footer-h) + var(--gap) * 2
		);
		--fits: calc(
			(100dvh - var(--chrome) - (var(--grid-rows) - 1) * var(--gap)) /
			var(--grid-rows)
		);
		max-width: calc(
			var(--cols) * var(--fits) + (var(--cols) - 1) * var(--gap)
		);
	}
}

.grid a {
	display: block;
	aspect-ratio: 1 / 1;
	transition: opacity 160ms ease;
}

.grid img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	background: rgba(0, 0, 0, 0.04);
}

@media (hover: hover) {
	.grid:hover a {
		opacity: 0.45;
	}

	.grid:hover a:hover {
		opacity: 1;
	}
}

/* ---------------------------------------------------------------
   Audio gear (Music page). One line per system, running the full
   width of the grid above it.
   --------------------------------------------------------------- */

.gear {
	margin-top: var(--gear-top);
	padding-top: 0.9rem;
	border-top: 1px solid var(--line);
}

.gear__title {
	margin: 0 0 0.5rem;
	font-size: 0.9rem;
	font-weight: 450;
	line-height: 1.2;
	color: var(--ink-mute);
}

.gear__group {
	list-style: none;
	margin: 0;
	padding: 0.22rem 0;
	font-size: 0.95rem;
	line-height: 1.6;
}

/* Inline rather than flex so a long system wraps as ordinary text. */
.gear__group li {
	display: inline;
}

.gear__group li:not(:last-child)::after {
	content: "\00B7";
	margin: 0 0.6em;
	color: var(--ink-mute);
}

.gear__group a {
	border-bottom: 1px solid transparent;
	transition: color 160ms ease, border-color 160ms ease;
}

@media (hover: hover) {
	.gear__group a:hover {
		color: var(--ink-strong);
		border-bottom-color: var(--ink-strong);
	}
}

@media (max-width: 560px) {
	.gear__group {
		font-size: 0.9rem;
	}
}

/* ---------------------------------------------------------------
   About (landing page)
   --------------------------------------------------------------- */

.about {
	/* The bio is the only content on this page, so it carries a little more
	   size than body copy elsewhere. */
	max-width: 36rem;
	font-size: 1.1rem;
	line-height: 1.55;
}

.about p {
	margin: 0 0 1.15em;
}

.links {
	list-style: none;
	/* The bio's closing line introduces this list, so it sits close to it. */
	margin: 0.4em 0 0;
	padding: 0;
	line-height: 1.75;
	color: var(--ink-mute);
}

.links a {
	border-bottom: 1px solid rgba(0, 0, 0, 0.18);
	transition: color 160ms ease, border-color 160ms ease;
}

@media (hover: hover) {
	.links a:hover {
		color: var(--ink-strong);
		border-bottom-color: var(--ink-strong);
	}
}

/* ---------------------------------------------------------------
   Selected work
   --------------------------------------------------------------- */

.work {
	flex: 1 1 auto;
	display: flex;
	flex-direction: column;
	/* Sits directly under the name with no intro copy, so it needs its own
	   breathing room above the first rule. */
	margin-top: clamp(0.9rem, 2.5vh, 1.75rem);
	max-width: 52rem;
	border-top: 1px solid var(--line);
}

/* Rows share the spare height evenly, but never compress past --row-min —
   past that the list keeps its natural height and the page scrolls. */
.work__item {
	flex: 1 1 auto;
	min-height: 5.5rem;
	display: flex;
	flex-direction: column;
	justify-content: center;
	padding: 1.1rem 0.5rem;
	margin: 0 -0.5rem;
	border-bottom: 1px solid var(--line);
	border-radius: 3px;
	transition: background 180ms ease;
}

.work__title {
	font-size: 1.1rem;
	font-weight: 450;
	color: var(--ink-strong);
}

/* External-link mark, kept out of the text so it never wraps alone. */
.work__title::after {
	content: "\2197";
	display: inline-block;
	margin-left: 0.3em;
	font-size: 0.9em;
	color: var(--ink-mute);
	font-weight: 400;
	transition: transform 180ms ease, color 180ms ease;
}

.work__desc {
	display: block;
	max-width: 44rem;
	margin-top: 0.35rem;
	text-wrap: pretty;
}

@media (hover: hover) {
	.work__item:hover {
		background: rgba(0, 0, 0, 0.045);
	}

	.work__item:hover .work__title::after {
		color: var(--ink-strong);
		transform: translate(1px, -1px);
	}

}

/* ---------------------------------------------------------------
   Page transitions

   Cross-document view transitions: the browser cross-fades between
   navigations on its own. No JavaScript, and browsers without support
   (Firefox, today) simply navigate as before.
   --------------------------------------------------------------- */

@view-transition {
	navigation: auto;
}

/* Naming the bars captures them as their own groups. They are identical on
   every page, so they hold still while the content beneath them changes
   instead of being cross-faded along with it. */
.bar--top {
	view-transition-name: bar-top;
}

.bar--bottom {
	view-transition-name: bar-bottom;
}

/* Neutral cross-fade, used when the direction is unknown — a fresh load, or
   arriving from somewhere outside the nav. */
@keyframes page-out {
	to {
		opacity: 0;
	}
}

@keyframes page-in {
	from {
		opacity: 0;
	}
}

::view-transition-old(root) {
	animation: page-out 140ms cubic-bezier(0.4, 0, 1, 1) both;
}

::view-transition-new(root) {
	animation: page-in 240ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

/* Directional travel. `data-nav` is set from the nav order by the inline
   script in each page's head: moving rightward through About → Music →
   Selected Work slides left, and coming back slides right. */
@keyframes depart-left {
	to {
		opacity: 0;
		transform: translateX(-2.5rem);
	}
}

@keyframes arrive-from-right {
	from {
		opacity: 0;
		transform: translateX(2.5rem);
	}
}

@keyframes depart-right {
	to {
		opacity: 0;
		transform: translateX(2.5rem);
	}
}

@keyframes arrive-from-left {
	from {
		opacity: 0;
		transform: translateX(-2.5rem);
	}
}

html[data-nav="fwd"]::view-transition-old(root) {
	animation: depart-left 180ms cubic-bezier(0.4, 0, 1, 1) both;
}

html[data-nav="fwd"]::view-transition-new(root) {
	animation: arrive-from-right 320ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

html[data-nav="back"]::view-transition-old(root) {
	animation: depart-right 180ms cubic-bezier(0.4, 0, 1, 1) both;
}

html[data-nav="back"]::view-transition-new(root) {
	animation: arrive-from-left 320ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		transition: none !important;
	}

	::view-transition-group(*),
	::view-transition-old(*),
	::view-transition-new(*) {
		animation: none !important;
	}
}
