/* gabrielrossetto.com
 *
 * A personal home. No pitch and no conversion path: nothing here is trying to
 * turn a visitor into anything. Variance 6, motion 4, density 3: asymmetric
 * but not showy, restrained movement, airy.
 *
 * Locks, decided once and applied everywhere:
 *   Theme .... one theme per page, auto from prefers-color-scheme
 *   Colour ... one accent, rust, on a cool neutral ground
 *   Shape .... radius 0 on every surface. Print, not product UI
 *   Type ..... Geist for structure, Literata for reading, Geist Mono for dates and code
 *
 * No build step and no JavaScript. Caddy serves these files straight off disk,
 * which is why the fonts are self-hosted: a CSP of font-src 'self' has nothing
 * to allow through.
 */

/* ---------------------------------------------------------------- fonts --
 * Latin subsets only, variable, pulled from Google Fonts and served from here.
 * Literata reads body copy; the Digxit design system had already named it as
 * the candidate for that job. No italic face is loaded because nothing on the
 * page is italic. Add one back the day something is.
 */
@font-face {
	font-family: 'Literata';
	src: url('assets/fonts/literata-latin.woff2') format('woff2');
	font-weight: 400 700;
	font-style: normal;
	font-display: swap;
}
@font-face {
	font-family: 'Geist';
	src: url('assets/fonts/geist-latin.woff2') format('woff2');
	font-weight: 300 700;
	font-style: normal;
	font-display: swap;
}
@font-face {
	font-family: 'Geist Mono';
	src: url('assets/fonts/geistmono-latin.woff2') format('woff2');
	font-weight: 400 600;
	font-style: normal;
	font-display: swap;
}

/* --------------------------------------------------------------- tokens -- */
:root {
	--sans: 'Geist', system-ui, -apple-system, 'Segoe UI', sans-serif;
	--serif: 'Literata', Georgia, 'Iowan Old Style', serif;
	--mono: 'Geist Mono', ui-monospace, 'SF Mono', Menlo, monospace;

	/* Cool neutral ground. Never pure white, never pure black. */
	--paper: #f7f6f4;
	--surface: #fdfdfc;
	--ink: #1a1a1c;
	--ink-2: #55545a;
	--ink-3: #86848b;
	--rule: #e2e0db;
	--accent: #b4472a; /* 5.05:1 on --paper */
	--accent-strong: #93381f;
	--tint-petinder: #eceff1;
	--tint-denon: #e9eae4;

	--measure: 66ch;
	--gutter: clamp(1.25rem, 5vw, 4rem);
	--page: 1180px;
	/* Applied top and bottom, so the space between two sections is twice this.
	 * Sized for that: 11rem at the boundary, not 22rem. */
	--section-gap: clamp(3.5rem, 7vh, 5.5rem);
	--ease: cubic-bezier(0.16, 1, 0.3, 1);
}

@media (prefers-color-scheme: dark) {
	:root {
		--paper: #141416;
		--surface: #1b1b1e;
		--ink: #eceae7;
		--ink-2: #a3a1a0;
		--ink-3: #78767a;
		--rule: #2b2b30;
		--accent: #e0734d; /* 5.9:1 on --paper */
		--accent-strong: #f08a66;
		--tint-petinder: #1e2124;
		--tint-denon: #1f211d;
	}
}

/* ---------------------------------------------------------------- reset -- */
*,
*::before,
*::after {
	box-sizing: border-box;
}
html {
	-webkit-text-size-adjust: 100%;
}
body {
	margin: 0;
	background: var(--paper);
	color: var(--ink);
	font-family: var(--serif);
	font-size: 1.0625rem;
	line-height: 1.65;
	font-synthesis-weight: none;
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}
img {
	max-width: 100%;
	height: auto;
	display: block;
}
a {
	color: inherit;
}
h1,
h2,
h3 {
	font-family: var(--sans);
	font-weight: 600;
	letter-spacing: -0.025em;
	margin: 0;
	text-wrap: balance;
}
p {
	margin: 0;
}
::selection {
	background: var(--accent);
	color: var(--paper);
}
:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 3px;
}

/* --------------------------------------------------------------- layout -- */
.wrap {
	width: 100%;
	max-width: var(--page);
	margin-inline: auto;
	padding-inline: var(--gutter);
}
section {
	padding-block: var(--section-gap);
}

/* ------------------------------------------------------------------ nav --
 * One line at every width, 64px tall. No hamburger needed for three links.
 */
.nav {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem;
	height: 64px;
	border-bottom: 1px solid var(--rule);
}
.nav__mark {
	font-family: var(--sans);
	font-weight: 600;
	font-size: 0.95rem;
	letter-spacing: -0.01em;
	text-decoration: none;
}
.nav__links {
	display: flex;
	gap: clamp(1rem, 3vw, 2.25rem);
	font-family: var(--sans);
	font-size: 0.9rem;
}
.nav__links a {
	text-decoration: none;
	color: var(--ink-2);
	transition: color 0.2s var(--ease);
}
.nav__links a:hover {
	color: var(--accent);
}

/* --------------------------------------------------------------- shared -- */
.section-title {
	font-size: clamp(1.6rem, 3.4vw, 2.3rem);
	line-height: 1.15;
	margin-bottom: clamp(2rem, 4vw, 3rem);
}

/* ------------------------------------------------------------- projects --
 * Three projects, exactly three cells. Asymmetric trio: one tall cell
 * carrying real artwork, two shorter ones. Each cell has its own ground so the
 * grid is not three identical white boxes.
 */
.bento {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 1px;
	background: var(--rule);
	border: 1px solid var(--rule);
}
.cell {
	background: var(--paper);
	padding: clamp(1.5rem, 3vw, 2.5rem);
	display: flex;
	flex-direction: column;
	gap: 1rem;
	text-decoration: none;
	transition: background-color 0.3s var(--ease);
}
/* The game screenshot is 16:9, so this cell runs the full width rather than
 * standing tall. A landscape image in a two-row cell leaves a pool of empty
 * space underneath it that reads as a fault. */
.cell--lead {
	grid-column: 1 / -1;
	gap: 1.5rem;
}
/* This cell is full width, so a space-between header would fling the status
 * mark to the far edge, a long way from the name it belongs to. Hold it to the
 * width of the paragraph underneath. */
.cell--lead .cell__head {
	max-width: 48ch;
}
.cell__shot {
	width: 100%;
	border: 1px solid var(--rule);
	transition: transform 0.5s var(--ease);
}
/* The table composite carries its own shadow on a transparent background, so
 * it needs no frame. A border here would draw a box around empty space. */
.cell__shot--table {
	border: 0;
}
.cell:hover .cell__shot {
	transform: translateY(-3px);
}
/* The panel composite carries its own shadow too, so no frame. */
.cell__shot--panel {
	border: 0;
	margin-top: auto;
	align-self: center;
}

/* The copy only ever fills about half this full-width cell, so the room
 * photograph takes the space beside it rather than leaving it empty. The
 * interface composite then gets the full width underneath, which it needs:
 * it carries far more detail than the photograph does. */
.cell__top {
	display: grid;
	grid-template-columns: 1fr minmax(0, 300px);
	gap: clamp(1.5rem, 3vw, 2.75rem);
	align-items: start;
}
.cell__room {
	width: 100%;
	border: 1px solid var(--rule);
	transition: transform 0.5s var(--ease);
}
.cell:hover .cell__room {
	transform: translateY(-3px);
}
.cell--petinder {
	background: var(--tint-petinder);
}
.cell--denon {
	background: var(--tint-denon);
}
.cell__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1rem;
}
.cell__name {
	font-family: var(--sans);
	font-size: clamp(1.25rem, 2.4vw, 1.75rem);
	font-weight: 600;
	letter-spacing: -0.025em;
	transition: color 0.2s var(--ease);
}
.cell:hover .cell__name {
	color: var(--accent);
}
.cell__meta {
	font-family: var(--mono);
	font-size: 0.75rem;
	color: var(--ink-3);
	white-space: nowrap;
}
/* The accent at rest marks the projects that actually lead somewhere: the game
 * you can open, the firmware you can read. The one with nothing at the other
 * end stays grey, which is the honest signal and the reason this is state
 * rather than decoration. */
.cell__meta--live {
	color: var(--accent);
}
.cell__body {
	color: var(--ink-2);
	font-size: 0.98rem;
	max-width: 48ch;
}

/* ---------------------------------------------------------------- about -- */
.prose {
	max-width: var(--measure);
	font-size: 1.1rem;
}
.prose p + p {
	margin-top: 1.4em;
}

/* --------------------------------------------------------------- footer -- */
.footer {
	border-top: 1px solid var(--rule);
	padding-block: 3rem 4rem;
	display: flex;
	flex-wrap: wrap;
	gap: 1.5rem 3rem;
	align-items: baseline;
	justify-content: space-between;
	font-family: var(--sans);
	font-size: 0.9rem;
	color: var(--ink-3);
}

/* --------------------------------------------------------------- mobile --
 * Every asymmetric grid above collapses to one column. Declared here rather
 * than assumed.
 */
@media (max-width: 860px) {
	.bento {
		grid-template-columns: 1fr;
	}
	.cell--lead {
		grid-column: auto;
	}
	.cell__top {
		grid-template-columns: 1fr;
	}
}
@media (max-width: 560px) {
	
	
	.nav__links {
		gap: 1rem;
	}
}

/* --------------------------------------------------------------- motion --
 * One move, with a reason: each section arrives as it enters the viewport,
 * which gives the page a sequence instead of dumping it all at once. It runs
 * on a scroll-driven timeline, so there is no scroll listener and no
 * JavaScript at all. Browsers without animation-timeline simply show the
 * content, which is the correct fallback.
 */
@media (prefers-reduced-motion: no-preference) {
	@supports (animation-timeline: view()) {
		.reveal {
			animation: reveal linear both;
			animation-timeline: view();
			animation-range: entry 4% cover 20%;
		}
	}
}

@keyframes reveal {
	from {
		opacity: 0;
		transform: translateY(20px);
	}
	to {
		opacity: 1;
		transform: none;
	}
}
