@layer reset, theme, base, components, pages, motion, accessibility;

@layer theme {
	@font-face {
		font-family: "Archivo";
		font-style: normal;
		font-weight: 100 900;
		font-display: swap;
		src: url("../assets/fonts/archivo-latin-ext.woff2") format("woff2");
		unicode-range:
			U+0100-02BA, U+02BD-02FF, U+0304, U+0308, U+0329, U+1E00-1EFF, U+20AC;
	}

	@font-face {
		font-family: "Archivo";
		font-style: normal;
		font-weight: 100 900;
		font-display: swap;
		src: url("../assets/fonts/archivo-latin.woff2") format("woff2");
	}

	@font-face {
		font-family: "Source Serif 4";
		font-style: normal;
		font-weight: 400 700;
		font-display: swap;
		src: url("../assets/fonts/source-serif-4-latin-ext.woff2") format("woff2");
		unicode-range:
			U+0100-02BA, U+02BD-02FF, U+0304, U+0308, U+0329, U+1E00-1EFF, U+20AC;
	}

	@font-face {
		font-family: "Source Serif 4";
		font-style: normal;
		font-weight: 400 700;
		font-display: swap;
		src: url("../assets/fonts/source-serif-4-latin.woff2") format("woff2");
	}

	@font-face {
		font-family: "ITC Modern No. 216";
		font-style: normal;
		font-weight: 300;
		font-display: swap;
		src: url("../assets/fonts/itc-modern-no-216-light.woff2") format("woff2");
	}

	@font-face {
		font-family: "ITC Modern No. 216";
		font-style: normal;
		font-weight: 400;
		font-display: swap;
		src: url("../assets/fonts/itc-modern-no-216-regular.woff2") format("woff2");
	}

	/* @font-face {
		font-family: "Fraunces";
		font-style: normal;
		font-weight: 100 900;
		font-display: swap;
		src: url("../assets/fonts/fraunces-latin-ext.woff2") format("woff2");
		unicode-range:
			U+0100-02BA, U+02BD-02FF, U+0304, U+0308, U+0329, U+1E00-1EFF, U+20AC;
	}

	@font-face {
		font-family: "Fraunces";
		font-style: normal;
		font-weight: 100 900;
		font-display: swap;
		src: url("../assets/fonts/fraunces-latin.woff2") format("woff2");
	} */

	:root {
		color-scheme: light;

		--color-brand: #102a78;
		--color-brand-deep: #071d5c;
		--color-action: color-mix(in oklch, white 8%, var(--color-brand));
		--color-accent: #ef4a47;
		--color-highlight: #f7c843;
		--color-demo: #1f6f43;
		--color-page: #f6f4ed;
		--color-surface: #fffefa;
		--color-surface-muted: #e5e2d8;
		--color-text: #161616;
		--color-text-muted: #6c6b66;
		--color-rule: color-mix(in oklch, var(--color-brand) 14%, transparent);
		--color-focus: color-mix(in oklch, var(--color-brand) 15%, transparent);
		/* the red margin line on paper surfaces (sheets, journal rulings) */
		--paper-margin: color-mix(in oklch, var(--color-accent) 22%, transparent);
		--shadow-card: 0 0.6rem 1.8rem
			color-mix(in oklch, var(--color-text) 9%, transparent);
		--shadow-panel: 0 0.8rem 2.5rem
			color-mix(in oklch, var(--color-text) 6%, transparent);
		--shadow-raised: 0 0.1rem 0.5rem rgb(22 22 22 / 10%);
		--grid-size: 4.5rem;
		--sidebar-size: 12.5rem;
		/* the compact panel padding shared by form panels across pages
		   (search box, create/edit forms, fieldsets, footers, notices) */
		--panel-pad: 0.75rem;
		--font-size-micro: 0.625rem;
		--font-size-label: 0.7rem;
		--font-serif: "Source Serif 4", Georgia, serif;
		--font-sans: "Archivo", Arial, sans-serif;
		--font-display: "ITC Modern No. 216", "Source Serif 4", Georgia, serif;
	}

	/* Slovak needs a display face that carries the full diacritics
	   (ť ň ľ…), which the vintage ITC Modern lacks — so in Slovak the
	   whole display face swaps to Fraunces; English keeps ITC Modern. */

	/* html[lang="sk"] {
		--font-display: "Fraunces", "Source Serif 4", Georgia, serif;
	} */
}

@layer pages {
	/* in Slovak the display face is Fraunces; give the big page
	   titles a slightly fuller weight and a touch more air between letters
	   there (English keeps ITC Modern at its lightweight, tight setting).
	   In the pages layer so it beats the components-layer weight 300. */
	/* html[lang="sk"] :is(.page-header, .form-page-header) h1 {
		font-weight: 400;
		letter-spacing: -0.04em;
	} */
}

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

	:where(body, h1, h2, h3, p, ol, ul, fieldset) {
		margin: 0;
	}

	:where(ol, ul) {
		padding: 0;
	}

	:where(button, input, select, textarea) {
		font: inherit;
	}

	img {
		display: block;
		max-inline-size: 100%;
	}
}

@layer base {
	html {
		background: var(--color-page);
		scroll-behavior: smooth;
		scrollbar-gutter: stable;
	}

	body {
		min-inline-size: 20rem;
		background: var(--color-page);
		color: var(--color-brand);
		font-family: var(--font-serif);
		-webkit-font-smoothing: antialiased;
	}

	a {
		color: inherit;
	}

	::selection {
		background: var(--color-highlight);
		color: var(--color-text);
	}

	:is(input, select, textarea)::placeholder {
		/* the hint voice: thin, muted and clearly softer than typed text;
		   Firefox needs an explicit opacity or it dims placeholders twice */
		color: var(--color-text-muted);
		font-weight: 300;
		opacity: 1;
	}

	.visually-hidden,
	.skip-link:not(:focus) {
		position: absolute;
		inline-size: 1px;
		block-size: 1px;
		padding: 0;
		border: 0;
		margin: -1px;
		overflow: clip;
		clip-path: inset(50%);
		white-space: nowrap;
	}

	.skip-link {
		position: fixed;
		inset-block-start: 1rem;
		inset-inline-start: 1rem;
		z-index: 200;
		padding: 0.75rem 1rem;
		background: var(--color-highlight);
		color: var(--color-brand-deep);
		font-family: var(--font-sans);
		font-weight: 800;
	}
}

@layer components {
	.app-shell {
		display: grid;
		grid-template-columns: var(--sidebar-size) minmax(0, 1fr);
		min-block-size: 100dvh;
	}

	.site-header {
		position: sticky;
		inset-block-start: 0;
		z-index: 20;
		display: flex;
		flex-direction: column;
		block-size: 100dvh;
		padding: 2.5rem 1.75rem 2rem;
		background:
			linear-gradient(
				color-mix(in oklch, white 5%, transparent) 1px,
				transparent 1px
			),
			linear-gradient(
				90deg,
				color-mix(in oklch, white 5%, transparent) 1px,
				transparent 1px
			),
			var(--color-brand);
		background-attachment: fixed;
		background-size: var(--grid-size) var(--grid-size);
		color: white;
	}

	.wordmark {
		inline-size: max-content;
		color: white;
		font-family: var(--font-display);
		font-size: 2.9rem;
		font-weight: 300;
		letter-spacing: -0.075em;
		line-height: 0.68;
		text-decoration: none;

		> span {
			display: block;
		}
	}

	.wordmark-dot {
		color: var(--color-accent);
	}

	.primary-navigation {
		display: grid;
		gap: 0.55rem;
		margin-block-start: 5rem;
		font-family: var(--font-sans);

		a {
			display: flex;
			align-items: center;
			gap: 0.8rem;
			min-block-size: 2.9rem;
			padding: 0.6rem 0.75rem;
			border-radius: 0.55rem;
			color: color-mix(in oklch, white 58%, transparent);
			font-size: 0.7rem;
			font-weight: 750;
			letter-spacing: 0.04em;
			text-decoration: none;
			text-transform: uppercase;
		}

		a {
			&:is(:hover, :focus-visible, .active) {
				background: color-mix(in oklch, white 10%, transparent);
				color: white;
			}

			&.active {
				box-shadow: inset 0.18rem 0 var(--color-accent);
			}
		}
	}

	.nav-icon {
		display: grid;
		inline-size: 1.25rem;
		color: var(--color-highlight);
		font-size: 1rem;
		line-height: 1;
		place-items: center;
	}

	.account-navigation {
		cursor: pointer;
		margin-block: auto 0;
		font-family: var(--font-sans);
		text-decoration: none;
	}

	/* the account trigger keeps the old placeholder look */
	.account-summary {
		display: flex;
		align-items: center;
		gap: 0.7rem;
		padding: 0.45rem 0.65rem;
		/* longhand so the auto top margin of .account-navigation (which
		   pins the account to the sidebar's foot) survives */
		margin-block: auto -0.45rem;
		margin-inline: -0.65rem;
		border: 0;
		border-radius: 0.55rem;
		background: transparent;
		cursor: pointer;
		color: white;
		font-family: var(--font-sans);
		font-size: var(--font-size-label);
		font-weight: 750;
		letter-spacing: 0.04em;
		text-transform: uppercase;

		&:is(:hover, :focus-visible),
		&[aria-expanded="true"] {
			background: color-mix(in oklch, white 10%, transparent);
		}
	}

	.account-placeholder {
		display: flex;
		align-items: center;
		gap: 0.7rem;
		color: white;
		font-size: var(--font-size-label);
		font-weight: 750;
		letter-spacing: 0.04em;
		text-transform: uppercase;
	}

	/* the logout popover: light dismiss and escape are built in; where
	   anchors are supported it hangs off the account button (flipping
	   above when the viewport bottom would clip it), otherwise it falls
	   back to the top-right corner of the screen */
	.account-menu {
		/* an explicit top-right pin: a fixed element with `inset: auto`
		   sits at its static position, which on mobile is inside the
		   header — once the header scrolls away that position is above the
		   viewport and the popup's top gets cut off. Pinning it keeps it
		   on-screen in every browser; the anchor block below takes over
		   where position-area is really supported. */
		position: fixed;
		inset-block-end: auto;
		inset-block-start: 3.8rem;
		inset-inline-end: 1rem;
		inset-inline-start: auto;
		min-inline-size: 7.5rem;
		max-block-size: calc(100dvh - 5rem);
		padding: 0.35rem;
		overflow-y: auto;
		overflow-x: clip;

		/* faint red gallery symbols behind the logout item */
		&::before {
			position: absolute;
			inset: 0;
			background: var(--color-accent);
			content: "";
			opacity: 0.3;
			mask-image: url("../../assets/images/gallery-symbols.svg?v=2");
			mask-position: center;
			mask-repeat: no-repeat;
			mask-size: 11rem auto;
			pointer-events: none;
		}

		form {
			position: relative;
			margin: 0;
		}

		button {
			display: block;
			inline-size: 100%;
			padding: 0.6rem 0.75rem;
			border: 0;
			border-radius: 0.45rem;
			background: transparent;
			color: var(--color-text);
			font-family: var(--font-sans);
			font-size: var(--font-size-label);
			font-weight: 750;
			letter-spacing: 0.04em;
			text-transform: uppercase;
			text-align: center;
			cursor: pointer;

			&:is(:hover, :focus-visible) {
				background: color-mix(in oklch, var(--color-brand) 8%, transparent);
				color: var(--color-brand-deep);
			}
		}

		/* the EN/SK language pills: the method-switcher track idea,
		   shrunk to popover size; the pressed pill reads as raised */
		.locale-switch {
			display: flex;
			gap: 0.2rem;
			margin-block-end: 0.35rem;
			padding: 0.2rem;
			border-radius: 0.5rem;
			background: var(--color-surface-muted);

			button {
				flex: 1;
				padding-block: 0.3rem;
				border-radius: 0.4rem;

				&.active {
					background: var(--color-surface);
					color: var(--color-brand-deep);
					box-shadow: 0 1px 2px rgb(0 0 0 / 0.18);
				}
			}
		}
	}

	@supports (anchor-name: --account) and (position-area: block-end) {
		.account-summary {
			anchor-name: --account;
		}

		.account-menu {
			inset: auto;
			position-anchor: --account;
			margin-block-start: 0.3rem;
			position-area: block-end span-inline-end;
			position-try-fallbacks: flip-block;
		}
	}

	.avatar {
		display: grid;
		inline-size: 2.1rem;
		aspect-ratio: 1;
		border-radius: 50%;
		background: var(--color-highlight);
		color: var(--color-brand);
		font-size: var(--font-size-micro);
		place-items: center;
	}

	.main-content {
		min-inline-size: 0;
	}

	:is(.page-wrap, .form-page-wrap) {
		inline-size: min(100% - 5rem, 88rem);
		margin-inline: auto;
	}

	/* the add-game and lists flows share one narrower column */
	.form-page-wrap {
		max-inline-size: 58rem;
		padding-block-end: 6rem;
	}

	.page-wrap {
		padding-block-end: 6rem;
	}

	:is(.page-header, .form-page-header) {
		position: relative;
		isolation: isolate;
		padding-block: 4.25rem 2.75rem;

		&::before {
			position: absolute;
			inset-block: 0;
			/* reach the content column's left edge past the wrap's margin
			   (which differs per page), so the fixed grid phase matches the
			   nav; a transform would break background-attachment: fixed */
			inset-inline-start: calc((100% - (100vw - var(--sidebar-size))) / 2);
			z-index: -1;
			inline-size: calc(100vw - var(--sidebar-size));
			background:
				linear-gradient(var(--color-rule) 1px, transparent 1px),
				linear-gradient(90deg, var(--color-rule) 1px, transparent 1px);
			background-attachment: fixed;
			background-size: var(--grid-size) var(--grid-size);
			content: "";
			mask-image: linear-gradient(to bottom, rgb(0 0 0 / 78%), transparent 96%);
			opacity: 0.6;
			pointer-events: none;
		}

		h1 {
			margin: 0;
			font-family: var(--font-display);
			font-size: clamp(4.5rem, 8vw, 7rem);
			font-weight: 300;
			letter-spacing: -0.075em;
			line-height: 0.72;
			text-wrap: balance;

			span {
				color: var(--color-accent);
			}
		}
	}

	.page-header {
		display: grid;
		grid-template-columns: minmax(0, 1fr) auto;
		grid-template-rows: auto auto;
		column-gap: 2rem;
		row-gap: 0;

		hgroup {
			display: contents;
		}

		.eyebrow {
			grid-column: 1;
			grid-row: 1;
		}

		h1 {
			grid-column: 1;
			grid-row: 2;
		}

		.add-game-button {
			grid-column: 2;
			grid-row: 2;
			align-self: center;
		}
	}

	.eyebrow {
		position: relative;
		z-index: 1;
		margin-block-end: 0.45rem;
		color: var(--color-accent);
		font-family: var(--font-sans);
		font-size: var(--font-size-label);
		font-weight: 800;
		letter-spacing: 0.12em;
		text-transform: uppercase;
	}

	/* the halo is only for the big page headers on the warm page paper,
	   where the label can ride over the display h1. The blue journal hero
	   and the in-sheet eyebrows stay plain. */
	:is(.page-header, .form-page-header) .eyebrow {
		text-shadow:
			0 1px 0 var(--color-page),
			1px 0 0 var(--color-page),
			0 -1px 0 var(--color-page),
			-1px 0 0 var(--color-page),
			1px 1px 0 var(--color-page),
			-1px -1px 0 var(--color-page),
			1px -1px 0 var(--color-page),
			-1px 1px 0 var(--color-page);
	}

	:is(.add-game-button, .primary-button) {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		gap: 0.55rem;
		min-block-size: 3rem;
		padding: 0.8rem 1.15rem;
		border: 0;
		border-radius: 0.45rem;
		background: var(--color-action);
		color: white;
		cursor: pointer;
		font-family: var(--font-sans);
		font-size: var(--font-size-label);
		font-weight: 800;
		letter-spacing: 0.07em;
		text-decoration: none;
		text-transform: uppercase;

		&:is(:hover, :focus-visible) {
			background: var(--color-accent);
		}
	}

	.add-game-button span {
		color: var(--color-highlight);
		font-size: 1.2rem;
	}

	.add-game-button {
		min-block-size: 3.6rem;
		/* the ＋ glyph leads with its own blank advance, which reads as
		   extra left padding; trim the start and pad the end to balance */
		padding-block: 0.95rem;
		padding-inline: 1.2rem 1.6rem;
		font-size: 0.75rem;
	}

	/* in Slovak the “Ludotéka.” title runs longer than “Library.”, so on
	   a narrow portrait screen the ＋ button that shares its header row
	   crowds it; shrink the button and ease the title just there. On
	   larger (and landscape) screens Slovak renders at the same sizes as
	   English. Cheap and reversible. */
	@media (width <= 38rem) {
		html[lang="sk"] .add-game-button {
			min-block-size: 2.5rem;
			padding-block: 0.45rem;
			padding-inline: 0.7rem 1rem;
			font-size: 0.66rem;
		}

		html[lang="sk"] .add-game-button span {
			font-size: 1rem;
		}

		html[lang="sk"] .page-header h1 {
			font-size: clamp(3rem, 15vw, 4rem);
		}
	}

	/* the one status -> semantic colour mapping; every surface (tabs,
	   choice cards, index chips, glance boxes, strip labels) reads its
	   --status-color and --status-ink from these shared classes */
	.status-playing {
		--status-color: var(--color-accent);
		--status-ink: white;
	}

	.status-finished {
		--status-color: var(--color-brand);
		--status-ink: white;
	}

	.status-backlog {
		--status-color: var(--color-highlight);
		--status-ink: var(--color-brand-deep);
	}

	/* the one demo-kind -> colour mapping; the demo badge reads as its
	   own category (green) rather than any single tracking status */
	.kind-demo {
		--status-color: var(--color-demo);
		--status-ink: white;
	}

	/* the shared Markdown-notes recipe: serif body with display-font
   headings, used by the journal preview and the list notes slip;
   contexts set their own size, rhythm and margin line */
	.rendered-notes {
		color: var(--color-text);
		font-family: var(--font-serif);
		overflow-wrap: anywhere;

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

		> :last-child {
			margin-block-end: 0;
		}

		h1,
		h2,
		h3 {
			margin-block: 0.9rem 0;
			font-family: var(--font-display);
			font-weight: 300;
			letter-spacing: -0.02em;
			line-height: 1.2;
		}

		h1 {
			font-size: 1.5rem;
		}

		h2 {
			font-size: 1.25rem;
		}

		h3 {
			font-size: 1.05rem;
		}

		p,
		ul,
		ol,
		blockquote {
			margin-block-end: 1.8rem;
		}

		ul,
		ol {
			padding-inline-start: 1.5rem;
		}

		blockquote {
			margin-inline: 0;
			padding-inline-start: 0.85rem;
			border-inline-start: 2px solid
				color-mix(in oklch, var(--color-accent) 45%, transparent);
			color: var(--color-text-muted);
			font-style: italic;
		}

		code {
			padding: 0.1em 0.35em;
			border-radius: 0.2rem;
			background: color-mix(in oklch, var(--color-rule) 55%, transparent);
			font-family: var(--font-mono, monospace);
			font-size: 0.85em;
		}

		a {
			color: var(--color-brand-deep);
			text-decoration-thickness: 1px;
			text-underline-offset: 0.15em;
		}
	}

	/* lazy covers fade in once decoded; the .js gate keeps images fully
	   visible when script is unavailable. Shared by cards, the journal
	   hero cover and list rows, so it lives next to the other shared
	   recipes. */
	.js :is(.cover-display img, .journal-cover img, .entry-cover) {
		opacity: 0;
		transition: opacity 260ms ease;
	}

	.js :is(.cover-display img, .journal-cover img, .entry-cover).is-loaded {
		opacity: 1;
	}

	/* the tape recipe: one translucent strip with a hand-rolled edge,
	   shared by the paper sheets, the tracking card and the notes slip.
	   Knobs default to the cards' classic corner tape; a centred tape is
	   one modifier away. The torn hem rides along on .paper-edge. */

	:is(.paper-edge, .paper-tape) {
		position: relative;

		--tape-top: -0.55rem;
		--tape-offset: 2.25rem;
		--tape-size: 4.5rem;
		--tape-thickness: 1.2rem;
		--tape-rotate: 2deg;
		--tape-color: color-mix(in oklch, var(--color-highlight) 46%, transparent);
	}

	:is(.paper-edge, .paper-tape)::before {
		position: absolute;
		inset-block-start: var(--tape-top);
		inset-inline-end: var(--tape-offset);
		inline-size: var(--tape-size);
		block-size: var(--tape-thickness);
		background: var(--tape-color);
		box-shadow: inset 0 0 0 1px rgb(255 255 255 / 24%);
		content: "";
		rotate: var(--tape-rotate);
	}

	/* a centred tape, for the tracking card and the notes slip */
	.paper-tape-center {
		--tape-top: -0.5rem;
		--tape-thickness: 1.1rem;
		--tape-rotate: -1.5deg;

		&::before {
			inset-inline-end: auto;
			inset-inline-start: 50%;
			translate: -50%;
		}
	}

	/* the torn bottom edge, exactly like the notes sheet */
	.paper-edge::after {
		position: absolute;
		inset-inline: 0;
		inset-block-end: -0.65rem;
		block-size: 0.7rem;
		background:
			linear-gradient(135deg, var(--color-surface) 50%, transparent 51%) 0 0 / 1rem
			100% repeat-x,
			linear-gradient(225deg, var(--color-surface) 50%, transparent 51%) 0.5rem 0 /
			1rem 100% repeat-x;
		content: "";
		pointer-events: none;
	}

	/* the notebook-paper recipe: ruled sheet with the torn edge and tape.
	   The edge decorations live on .paper-edge; compose both classes to
	   dress a surface as paper. */

	.paper-sheet {
		border: 1px solid var(--color-rule);
		border-radius: 0.25rem 0.25rem 0 0;
		background:
			linear-gradient(
				90deg,
				transparent 1.35rem,
				var(--paper-margin) 1.35rem 1.4rem,
				transparent 1.4rem
			),
			var(--color-surface);
		box-shadow: var(--shadow-panel);
	}

	/* status and error flashes, shared by every page */

	.flash-message {
		padding: 0.9rem 1rem;
		border-inline-start: 0.25rem solid var(--color-accent);
		border-radius: 0.25rem;
		margin-block-end: 2rem;
		background: var(--color-highlight);
		color: var(--color-brand-deep);
		font-family: var(--font-sans);
		font-size: 0.72rem;
		font-weight: 750;
	}

	.flash-error {
		border-inline-start-color: var(--color-accent);
		background: color-mix(
			in oklch,
			var(--color-accent) 12%,
			var(--color-surface)
		);
		color: var(--color-accent);
	}

	/* the empty-state recipe: gallery symbols fading on page paper, shared
	   by the library, lists and stats */

	.empty-state {
		position: relative;
		padding: 4.5rem 1rem;
		overflow: clip;
		text-align: center;

		&::before {
			position: absolute;
			inset: 0;
			background:
				url("../../assets/images/gallery-symbols.svg?v=2") center / 12rem 9rem;
			content: "";
			filter: grayscale(1) brightness(0.45);
			mask-image: linear-gradient(
				to bottom,
				transparent,
				black 38% 62%,
				transparent
			);
			pointer-events: none;
		}

		> * {
			position: relative;
		}
	}

	/* the panel recipe: one 1px rule-edged surface, shared by the add-game
	   panels, the list create/edit panels, the ranked sheet and the entry
	   search results. Compose with classes that carry the layout. */

	.surface-panel {
		border: 1px solid var(--color-rule);
		border-radius: 0.75rem;
		background: var(--color-surface);
		box-shadow: var(--shadow-panel);
	}

	/* the strip and chart section headings, shared across pages */

	.section-heading {
		display: flex;
		align-items: baseline;
		gap: 0.6rem;
		margin-block-end: 1.25rem;

		h2 {
			color: var(--color-text);
			font-family: var(--font-sans);
			font-size: 1.2rem;
			font-weight: 800;
			letter-spacing: -0.025em;

			a {
				text-decoration: none;
			}
		}

		> span {
			color: var(--color-accent);
			font-family: var(--font-sans);
			font-size: var(--font-size-label);
			font-weight: 800;
		}
	}

	/* the controller-symbol divider, shared across pages */

	.controller-divider {
		--controller-glyph-size: 1rem;

		position: relative;
		display: flex;
		justify-content: center;
		align-items: center;
		block-size: 1px;
		border: 0;
		margin-block: 3.75rem 3rem;
		background: transparent;
		color: var(--color-accent);
		font-size: var(--controller-glyph-size);
		overflow: visible;

		&::before,
		&::after {
			flex: 1 1 auto;
			block-size: 1px;
			content: "";
		}

		&::before {
			background: linear-gradient(90deg, transparent, var(--color-rule) 40%);
		}

		&::after {
			background: linear-gradient(90deg, var(--color-rule) 60%, transparent);
		}

		/* A fixed box gives every glyph the same optical centre. The font
		   sizes and offsets compensate for Archivo's differing ink bounds. */
		> span {
			--controller-glyph-offset: 0;

			display: inline-grid;
			block-size: 1.25rem;
			padding-inline: 0.5rem;
			background: transparent;
			color: var(--color-accent);
			font-family: var(--font-sans);
			font-size: 1em;
			font-weight: 800;
			line-height: 1;
			place-items: center;
			translate: 0 var(--controller-glyph-offset);
		}

		> span:nth-child(1) {
			--controller-glyph-offset: -0.8px;

			padding-inline-start: 1.15rem;
			font-size: 1.062em;
		}
		> span:nth-child(2) {
			--controller-glyph-offset: 0;

			position: relative;
			color: transparent;
			font-size: 0.974em;

			/* Font engines position Archivo's × differently. Draw the same
			   mark from two centred strokes so Safari and Chromium agree. */
			&::before,
			&::after {
				position: absolute;
				inset-block-start: 50%;
				inset-inline-start: 50%;
				inline-size: 0.6rem;
				block-size: 0.16rem;
				background: var(--color-accent);
				content: "";
			}

			&::before {
				transform: translate(-50%, -50%) rotate(45deg);
			}

			&::after {
				transform: translate(-50%, -50%) rotate(-45deg);
			}
		}
		> span:nth-child(3) {
			--controller-glyph-offset: -0.6px;

			font-size: 0.69em;
		}
		> span:nth-child(4) {
			--controller-glyph-offset: -1.6px;

			padding-inline-end: 1.15rem;
			font-size: 1em;
		}
	}
}

@layer motion {
	@media (prefers-reduced-motion: no-preference) {
		:is(.add-game-button, .primary-button) {
			transition:
				background 150ms ease,
				translate 150ms ease;

			&:is(:hover, :focus-visible) {
				translate: 0 -0.1rem;
			}
		}

		/* soft cross-fade between pages; browsers without the View
		   Transitions API keep today's instant navigation */
		@view-transition {
			navigation: auto;
		}

		/* the persistent chrome never participates in the transition:
		   naming it lifts it into its own layer, pinned still, so only
		   the content drifts — the blue nav cannot blink */
		:is(.site-header, .admin-header) {
			view-transition-name: app-header;
		}

		::view-transition-group(app-header) {
			animation: none;
		}

		::view-transition-old(root) {
			animation: 90ms ease-out both vt-page-out;
		}

		::view-transition-new(root) {
			animation: 140ms ease-out both vt-page-in;
		}

		@keyframes vt-page-out {
			to {
				opacity: 0;
			}
		}

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

@layer accessibility {
	@media (prefers-contrast: more) {
		:root {
			--color-text-muted: var(--color-text);
			--color-rule: color-mix(in oklch, var(--color-brand) 32%, transparent);
		}
	}

	@media (forced-colors: active) {
		:is(.skip-link, .add-game-button, .primary-button, .site-header) {
			border: 1px solid CanvasText;
		}

		:is(a, button, input, select, textarea):focus-visible {
			outline: 2px solid Highlight;
			outline-offset: 2px;
		}
	}
}

@media (width <= 52rem) {
	@layer theme {
		:root {
			--grid-size: 3.5rem;
			--sidebar-size: 0rem;
		}
	}

	@layer base {
		body {
			/* the safe-area half only activates in the standalone home
			   screen app; plain Safari reports 0 and keeps today's 4.5rem */
			padding-block-end: calc(4.5rem + env(safe-area-inset-bottom, 0px));
		}
	}

	@layer components {
		.app-shell {
			display: block;
		}

		.site-header {
			/* the bottom tab bar is the phone navigation; the brand row scrolls
		   away with the page instead of pinning above the content */
			position: relative;
			z-index: 30;
			flex-direction: row;
			align-items: center;
			block-size: 4.25rem;
			padding: 0.8rem 1.25rem;
			box-shadow: 0 0.15rem 1rem
				color-mix(in oklch, var(--color-brand-deep) 15%, transparent);
		}

		.wordmark {
			font-size: 2rem;
			line-height: 0.65;
		}

		.primary-navigation {
			position: fixed;
			inset-inline: 0;
			inset-block-end: 0;
			z-index: 40;
			grid-template-columns: repeat(4, 1fr);
			gap: 0;
			/* the button area stays 4.5rem; only the home-indicator gap is
			   added, and just a fraction of it, so the bar hugs the bottom */
			block-size: calc(
				4.5rem +
				max(0.35rem, env(safe-area-inset-bottom, 0px) * 0.45) +
				0.2rem
			);
			padding: 0.35rem max(0.5rem, env(safe-area-inset-right))
				/* only part of the home-indicator inset: hugging it closer
				   than the full system gap reads better under the tabs */
				max(0.55rem, calc(env(safe-area-inset-bottom, 0px) * 0.45 + 0.2rem))
				max(0.5rem, env(safe-area-inset-left));
			border-block-start: 1px solid var(--color-rule);
			margin: 0;
			background: var(--color-surface);
			box-shadow: 0 -0.25rem 1.5rem
				color-mix(in oklch, var(--color-text) 8%, transparent);

			a {
				flex-direction: column;
				justify-content: center;
				gap: 0.25rem;
				min-block-size: 0;
				padding: 0.25rem;
				border-radius: 0.65rem;
				color: var(--color-text-muted);
				font-size: var(--font-size-micro);
				letter-spacing: 0.02em;
			}

			a {
				&:is(:hover, :focus-visible, .active) {
					background: transparent;
					color: var(--color-brand);
				}

				&.active {
					/* the raised brand-tinted pill; the accent icon is the
					   navigation's own signature */
					background: color-mix(
						in oklch,
						var(--color-brand) 8%,
						var(--color-surface)
					);
					box-shadow:
						inset 0 0 0 1px var(--color-rule),
						0 0.15rem 0.5rem color-mix(in oklch, var(--color-text) 8%, transparent);

					.nav-icon {
						color: var(--color-accent);
					}
				}
			}
		}

		.nav-icon {
			block-size: 1.25rem;
			color: currentColor;
			font-size: 1rem;
		}

		.account-navigation {
			margin: 0 0 0 auto;
		}

		.account-summary > span:last-child,
		.account-placeholder > span:last-child {
			display: none;
		}

		.avatar {
			inline-size: 2rem;
		}

		:is(.page-wrap, .form-page-wrap) {
			inline-size: min(100% - 2rem, 88rem);
			padding-block-end: 2rem;
		}

		:is(.page-header, .form-page-header) {
			padding-block: 2.5rem 2rem;

			&::before {
				opacity: 0.48;
			}

			h1 {
				font-size: clamp(3.8rem, 18vw, 5.4rem);
			}
		}
	}
}

@media (width <= 38rem) {
	@layer components {
		.page-header {
			column-gap: 1rem;

			h1 {
				font-size: clamp(3.5rem, 17vw, 4.5rem);
			}
		}

		.eyebrow {
			margin-block-end: 0.3rem;
		}

		.add-game-button {
			min-block-size: 3.2rem;
			/* keep the ＋ lead-trim at phone size too */
			padding-block: 0.85rem;
			padding-inline: 0.7rem 1rem;
			font-size: 0.68rem;
		}

		.primary-button {
			inline-size: 100%;
		}
	}
}
