:root {
	--font-mono: Consolas, Inconsolata, Monaco, monospace;

	/* Follow the system light/dark preference; the switcher pins one below. */
	color-scheme: light dark;

	/* Smoothly cross-fade when the background switches (notably in auto mode,
	   where it follows the first color's lightness). */
	transition: background .4s;
}

/* Each switcher button sets the root's color-scheme + background. "mid" is a
   mid-gray background that still uses dark-mode UI. */
:root[data-theme="light"] {
	color-scheme: light;
	background: #fff;
}

:root[data-theme="dark"] {
	color-scheme: dark;
	background: hsl(220 15% 13%);
}

:root[data-theme="mid"] {
	color-scheme: dark;
	/* Perceptual midpoint between white and black. */
	background: oklab(0.5 0 0);
}

body {
	font: 100%/1.5 system-ui;
	max-width: 84em;
	margin: 1em auto;
	padding-inline: 1em;

	@media (width > 55em) {
		display: grid;
		grid-template-columns: 24em 1fr;
		gap: 0em 2em;
		align-items: start;

		> header,
		.color {
			grid-column: 1 / -1;
		}

		.color {
			display: grid;
			grid-template-columns: subgrid;
			gap: inherit;
		}

		.gamut-mapped {
			grid-column: 2 / -1;
		}
	}
}

body > header {
	text-align: center;
	margin-bottom: 2em;

	h1 {
		font-size: calc(200% + 2vh + 2vw);
		margin-bottom: 1rem;
		line-height: 1.1;
	}

	.filters {
		display: flex;
		flex-wrap: wrap;
		justify-content: center;
		gap: .2em 2em;

		label {
			display: inline-flex;
			align-items: center;
			gap: .3em;
		}
	}
}

h2 {
	margin-top: 0;
	margin-bottom: 0;
	font-size: 150%;
}

/* The Error formula doubles as its own explanation: each coefficient is a live
   input, so editing it both retunes the metric and shows what the metric does. */
.error-formula {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: baseline;
	gap: .1em .4em;

	.name {
		font-weight: bold;
	}

	label {
		display: inline-flex;
		align-items: baseline;
		gap: .15em;
	}

	input {
		/* Hug the digits, but keep a floor so a single-digit weight (and its
		   spinner) doesn't get cramped. */
		field-sizing: content;
		min-width: 2ch;
		text-align: center;
		padding: .05em .2em;
		border: 1px solid light-dark(hsl(220 10% 75%), hsl(220 10% 40%));
		border-radius: .25em;
		background: light-dark(#fff, hsl(220 10% 18%));
	}

	.op {
		opacity: .5;
	}
}

input {
	font: inherit;
}

.swatches .description {
	display: block;
	font-size: 70%;
	color: light-dark(hsl(220 10% 45%), hsl(220 15% 78%));
	text-wrap: balance;
}

/* Browser rendering: a single labeled input, stacked in normal flow */
dl.swatches {
	margin: .5rem 0 0;

	> div {
		position: relative;
		display: grid;
		gap: .5em;
	}

	dd {
		margin: 0;
	}
}

/* Gamut mapped: a ranked list, each method laid out as swatch + details */
ol.swatches {
	list-style: none;
	/* One grid for the whole method list so every row can share the same column
	   tracks via subgrid: rank, swatch, then one column per delta metric, and a
	   trailing fraction that soaks up the slack. Each metric column is `auto`, so
	   it sizes to that metric's widest value across all methods — chips line up in
	   tidy columns and hold their place as values change (the column width is set
	   by the global max, not the current row), without forcing every metric to
	   the same awkward width. */
	display: grid;
	grid-template-columns: 2em 8em repeat(7, minmax(auto, 1fr));
	gap: 1em;
	margin: .5rem 0 0;
	padding: 0;

	> li {
		display: grid;
		grid-column: 1 / -1;
		grid-template-columns: subgrid;
		/* Three rows for the details, plus a flexible row that absorbs any slack up
		   to min-height — so the swatch is tall on wide screens (short content) yet
		   still fills 100% when taller content is what drives the height. */
		grid-template-rows: auto auto auto 1fr;
		min-height: 6em;
		row-gap: .25em;

		/* Fixed-width swatch spanning all rows. min-height: 0 stops its own height
		   from inflating the detail rows, so the details stay tightly stacked; it
		   then fills the full card height. */
		> color-swatch {
			grid-column: 2;
			grid-row: 1 / -1;
			min-height: 0;
			height: 100%;
			width: 100%;
		}

		> h3 {
			grid-column: 3 / -1;
			margin: 0;
			font-size: 105%;
		}

		> .description,
		> .deltas {
			grid-column: 3 / -1;
		}

		/* Rank in its own column beside the swatch, so it doesn't obscure the
		   color being evaluated. Best ranks are green, worst ranks red. */
		&::before {
			content: attr(data-ranking);
			grid-column: 1;
			grid-row: 1 / -1;
			align-self: center;
			justify-self: center;
			font-weight: bold;
			font-size: 105%;
			color: light-dark(hsl(220 10% 55%), hsl(220 15% 70%));
		}

		&.top::before {
			color: var(--color-green-50, light-dark(hsl(70 100% 32%), hsl(75 80% 60%)));
		}

		&.bottom::before {
			color: var(--color-red-40, light-dark(hsl(0 60% 45%), hsl(0 80% 68%)));
		}
	}
}

details.space-coords {
	font-size: 70%;
	margin-top: 1em;

	> summary {
		margin-bottom: .2rem;
		font-size: .8rem;
		text-transform: uppercase;
		font-weight: 520;
		color: light-dark(hsl(220 10% 40%), hsl(220 15% 75%));
	}
}

dl.space-coords {
	display: grid;
	grid-template-columns: auto repeat(3, auto);
	border-block: 1px solid light-dark(hsl(220 10% 90%), hsl(220 10% 32%));
	margin: 0;

	> div {
		grid-column: 1 / -1;
		display: grid;
		grid-template-columns: subgrid;
		border-block: 1px solid light-dark(hsl(220 10% 90%), hsl(220 10% 32%));
		padding-block: .3em;

		&:not(:first-child) {
			border-top: none;
		}

		> dt {
			text-transform: uppercase;
			font-weight: 520;
			color: light-dark(hsl(220 10% 40%), hsl(220 15% 75%));
		}
	}

	dd {
		display: contents;
	}

	dl.coords {
		grid-column: 2 / span 3;
		display: grid;
		grid-template-columns: subgrid;
	}
}

dl.deltas,
dl.coords {
	margin: 0;
	align-items: baseline;

	> div {
		display: flex;
		gap: .2em;
	}

	.delta-e2k,
	.delta-eok,
	.delta-error,
	.delta-time {
		background: light-dark(hsl(220 10% 50% / 8%), hsl(220 10% 80% / 12%));
		border: 1px solid light-dark(hsl(220 10% 50% / 10%), hsl(220 10% 80% / 18%));
		padding: 0 .3em;
		border-radius: .2em;
	}

	dt {
		font-weight: 300;
		color: light-dark(hsl(220 10% 50%), hsl(220 15% 75%));

		~ dt {
			margin-left: .5em;
		}
	}

	dd {
		font-weight: bold;
		margin: 0;
	}
}

/* Raw coordinates: a simple inline run of label/value pairs. */
dl.coords {
	display: flex;
	gap: .1em;

	&:has(> div) {
		gap: .4em;
	}
}

/* Deltas: a subgrid so each metric lands in the shared column its method's row
   inherits from ol.swatches — chips line up across methods and stay put. */
dl.deltas {
	display: grid;
	grid-template-columns: subgrid;
	/* Each chip hugs its content and sits at the column's start, instead of
	   stretching to fill the (1fr) column it shares. */
	justify-items: start;
	border-top: 1px solid light-dark(hsl(220 10% 80%), hsl(220 10% 38%));
	padding-top: .4em;
	margin-top: .8em;
	font-size: 90%;

	dd {
		/* Sign only; the color now marks just the best/worst value per column. */
		&.positive::before {
			content: "+";
		}

		/* Worst value in the column red, best value green (best wins ties). */
		&.max {
			color: var(--color-red-40, light-dark(hsl(0 60% 30%), hsl(0 80% 66%)));
		}

		&.min {
			color: var(--color-green-50, light-dark(hsl(70 100% 32%), hsl(75 80% 60%)));
		}
	}
}

/* Average time fades and sharpens in as the sample grows (--timing-progress 0→1),
   so it's visibly tentative until enough runs accumulate. */
.delta-time {
	--p: var(--timing-progress, 1);
	opacity: calc(.15 + .85 * var(--p));
	filter: blur(calc((1 - var(--p)) * 2px));
	transition: opacity .4s, filter .4s;
}

.rendering {
	.swatches > div > dt {
		margin-top: 1.1em;
	}
}

.gamut-mapped {
	/* Shown in place of the method list when the input needs no mapping. */
	.in-gamut {
		margin: .5rem 0 0;
		font-style: italic;
		opacity: .6;
	}

	color-swatch {
		font-size: 90%;

		/* These swatches carry no caption, so the component's details bar is
		   just an empty strip — invisible on white, a stray dark line under
		   color-scheme: dark. Hide it so the swatch fills the card cleanly. */
		&::part(details) {
			display: none;
		}
	}
}

article.color {
	position: relative;
	margin-bottom: 1rem;

	&:not(:first-child) {
		margin-top: 3rem;
	}
}

button {
	display: flex;
	align-items: center;
	padding: .3em .7em;
	font: inherit;
	background: light-dark(hsl(220 10% 94%), hsl(220 10% 25%));
	border: none;
	border-radius: .25em;
	cursor: pointer;
	transform-origin: bottom;
	transition: .1s;
	width: max-content;

	&:hover {
		background: light-dark(hsl(220 10% 90%), hsl(220 10% 32%))
	}

	&:active {
		scale: .9;
	}

	svg {
		height: 1em;
		margin-right: .3em;
		opacity: .4;
	}
}

.controls {
	position: absolute;
	top: 0;
	right: 0;
	display: flex;
	gap: .3em;
}

/* Three swatch buttons pinned top-right: each previews and applies a page
   background. Active one gets a ring in the current text color. */
.theme-switcher {
	position: absolute;
	top: 1em;
	right: 1em;
	z-index: 10;
	display: flex;
	gap: .4em;
	padding: 2px;
	border-radius: .4em;

	button {
		width: 1.5em;
		height: 1.5em;
		padding: 0;
		box-shadow: inset 0 0 0 1px hsl(0 0% 50% / 40%);

		/* Swatch previews mirror the backgrounds set in :root[data-theme]. */
		&.light { background: #fff; }
		&.dark { background: hsl(220 15% 13%); }
		&.mid { background: oklab(0.5 0 0); }

		/* Auto previews all three at once: it adapts to the color. */
		&.auto {
			background: linear-gradient(135deg, #fff 33%, oklab(0.5 0 0) 33% 66%, hsl(220 15% 13%) 66%);
		}

		&[aria-pressed="true"] {
			outline: 2px solid currentColor;
			outline-offset: 1px;
		}
	}
}
