:root {
	--ground: oklch(97% 0.006 255);
	--surface: white;
	--text: oklch(15% 0 0);
	--muted: oklch(52% 0 0);
	--border: color-mix(in oklch, var(--text) 10%, var(--surface));
	--rule: color-mix(in oklch, var(--text) 6%, var(--surface));
	--positive: oklch(52% 0.13 145);
	--negative: oklch(53% 0.19 25);
	--attention: oklch(72% 0.15 75);
	--radius: 0.5rem;
	--gutter: clamp(1rem, 3vw, 1.5rem);
}

body {
	margin: 0;
	background: var(--ground);
	color: var(--text);
	font: 0.875rem / 1.45 "Geist", system-ui, sans-serif;
}

main,
footer {
	max-inline-size: 72rem;
	margin-inline: auto;
	padding-inline: var(--gutter);
}

main {
	padding-block-start: 2.25rem;
	container-type: inline-size;
}

header {
	display: flex;
	flex-wrap: wrap;
	gap: 1em;
	align-items: center;
}

h1 {
	margin: 0;
	margin-inline-end: auto;
	font-size: 1.5rem;
	line-height: 1.3;
	font-weight: 700;
	text-wrap: balance;
}

#status {
	margin-block: 0.25em 1.5em;
	color: var(--muted);
	text-wrap: pretty;
}

#updated {
	margin: 0;
	color: var(--muted);
}

/* The summary above the table says it on screen; the caption names the table for screen readers */
caption {
	position: absolute;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* A value too long for its cell scrolls inside it. The 1px gap shows the strip’s own fill as hairlines */
#summary {
	display: grid;
	grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) max-content max-content;
	gap: 1px;
	margin-block: 1.5rem;
	overflow: clip;
	background: var(--rule);
	border: 1px solid var(--border);
	border-radius: var(--radius);

	&[hidden] {
		display: none;
	}

	div {
		padding: 0.85em 1.15em;
		background: var(--surface);
	}

	/* A phone has no room for four cells in a row: two rows of two */
	@container (width < 36rem) {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	dt {
		display: flex;
		align-items: center;
		/* As tall as the role switch, so that every value starts at the same height */
		min-block-size: 1.5rem;
		color: var(--muted);
		font-size: 0.85em;
	}

	dd {
		margin: 0.15em 0 0;
		overflow-x: auto;
		scrollbar-width: thin;
		font-size: 1.25rem;
		font-weight: 500;
		font-variant-numeric: tabular-nums;
		white-space: nowrap;
		/* Lets the fades below show only while there is more to scroll to */
		container-type: scroll-state;

		> * {
			display: inline-flex;
			align-items: center;

			/* Fades over the clipped edge */
			&::before,
			&::after {
				content: "";
				position: sticky;
				z-index: 1;
				align-self: stretch;
				flex: none;
				inline-size: 2rem;
				opacity: 0;
				transition: opacity 0.2s;
				pointer-events: none;
			}

			&::before {
				inset-inline-start: 0;
				margin-inline-end: -2rem;
				background: linear-gradient(to right, var(--surface), transparent);
			}

			&::after {
				inset-inline-end: 0;
				margin-inline-start: -2rem;
				background: linear-gradient(to left, var(--surface), transparent);
			}
		}
	}

	@container scroll-state(scrollable: inline-start) {
		dd > ::before {
			opacity: 1;
		}
	}

	@container scroll-state(scrollable: inline-end) {
		dd > ::after {
			opacity: 1;
		}
	}

	img {
		inline-size: 1.2em;
		block-size: auto;
		margin-inline-end: 0.4em;
		border-radius: 50%;
	}

	.roles {
		display: inline-flex;
		gap: 0.25em;
		margin-inline-start: -0.5em;

		a {
			display: inline-flex;
			align-items: center;
			/* The smallest target WCAG allows */
			min-block-size: 1.5rem;
			padding-inline: 0.5em;
			border-radius: 1e5px;
			text-decoration: none;

			&:hover {
				color: var(--text);
			}

			&[aria-current] {
				background: var(--rule);
				color: var(--text);
			}
		}
	}
}

#refresh,
#sign-out,
form button,
#sign-in > button {
	min-block-size: 2.5rem;
	padding-inline: 1em;
	border: none;
	border-radius: calc(var(--radius) * 0.75);
	font: inherit;
	font-weight: 500;
	cursor: pointer;
}

#sign-in > button,
#choose button {
	background: var(--text);
	color: white;

	&:hover {
		background: color-mix(in oklch, var(--text), white 20%);
	}
}

/* Secondary to the table it updates or filters, or to the sign-in */
#refresh,
#sign-out,
#token button,
#narrow button {
	background: color-mix(in oklch, var(--text) 8%, var(--surface));
	color: var(--text);

	&:hover {
		background: color-mix(in oklch, var(--text) 14%, var(--surface));
	}

	&[aria-disabled="true"] {
		color: var(--muted);
		cursor: progress;
	}
}

#sign-in {
	display: grid;
	gap: 0.75em;
	max-inline-size: 24rem;

	&[hidden] {
		display: none;
	}

	small {
		color: var(--muted);
		font-size: 0.85em;
		text-wrap: pretty;
	}

	a {
		color: inherit;
	}
}

/* A rule on each side of the word */
.or {
	display: flex;
	gap: 1em;
	align-items: center;
	margin-block: 0.75em;
	color: var(--muted);

	&::before,
	&::after {
		content: "";
		flex: 1;
		border-block-start: 1px solid var(--border);
	}
}

form {
	label {
		display: flex;
		gap: 0.5em;
		font-weight: 500;
	}

	input {
		min-block-size: 2.5rem;
		padding-inline: 0.75em;
		border: 1px solid color-mix(in oklch, var(--text) 20%, var(--surface));
		border-radius: calc(var(--radius) * 0.75);
		background: var(--surface);
		font: inherit;
		font-weight: 400;
	}
}

#choose {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75em 1.5em;
	align-items: center;

	&[hidden] {
		display: none;
	}

	label {
		align-items: center;
	}

	/* Grows with the name, so a long one is never cut off */
	input {
		field-sizing: content;
		min-inline-size: 12ch;
		max-inline-size: 100%;
	}
}

#narrow {
	margin-block-end: 1rem;

	summary {
		display: inline-flex;
		gap: 0.5em;
		align-items: center;
		color: var(--muted);
		font-weight: 500;
		cursor: pointer;

		&:hover {
			color: var(--text);
		}

		/* A chevron that turns down while open */
		&::before {
			content: "";
			inline-size: 1em;
			aspect-ratio: 1;
			background-color: currentColor;
			mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 4l4 4-4 4'/%3E%3C/svg%3E") no-repeat center / contain;
			transition: rotate 0.2s;
		}
	}

	&[open] summary::before {
		rotate: 90deg;
	}

	code {
		padding: 0.1em 0.5em;
		border-radius: 1e5px;
		background: var(--rule);
		color: var(--text);
		font-size: 0.85em;

		&:empty {
			display: none;
		}
	}

	form {
		display: flex;
		flex-wrap: wrap;
		gap: 0.5em 0.75em;
		align-items: center;
		margin-block-start: 0.75em;
		padding: 0.85em 1.15em;
		background: var(--surface);
		border: 1px solid var(--border);
		border-radius: var(--radius);
	}

	label {
		flex: 1;
		align-items: center;
	}

	input[type="search"] {
		flex: 1;
		min-inline-size: 16ch;
	}

	small {
		flex-basis: 100%;
		color: var(--muted);
		font-size: 0.85em;
	}

	a {
		color: var(--muted);

		&:hover {
			color: var(--text);
		}
	}
}

#token {
	display: grid;
	gap: 0.5em;

	label {
		flex-direction: column;
	}
}

progress {
	--duration: 1.5s;

	inline-size: 100%;
	block-size: 0.25rem;
	appearance: none;
	border: 0 solid;
	border-radius: 1e5px;
	overflow: clip;
	background: var(--border);
	/* Lets the fill move in track units (cqi) */
	container-type: inline-size;

	@media (prefers-reduced-motion: reduce) {
		/* Slowed, not stopped: without its animation the indeterminate fill has no width */
		--duration: 10s;
	}

	&::-webkit-progress-bar {
		background: transparent;
	}

	&::-webkit-progress-value {
		background: var(--text);
		border-radius: 1e5px;
		animation: progress-indeterminate var(--duration) infinite linear;
	}

	/* Firefox, e.g. Zen; a rule shared with the WebKit pseudo-element would be dropped as invalid in both */
	&::-moz-progress-bar {
		background: var(--text);
		border-radius: 1e5px;
		animation: progress-indeterminate var(--duration) infinite linear;
	}
}

@keyframes progress-indeterminate {
	from,
	to {
		inline-size: 30%;
		margin-right: auto;
	}

	from {
		translate: -100%;
	}

	to {
		translate: 100cqi;
	}
}

/* Too narrow for the table: it scrolls in its own frame, the width of the summary above it, instead of the page.
   Only then, since a scrolling frame would also pin the sticky header to itself rather than the window */
@container (width < 48rem) {
	.scroller {
		overflow-x: auto;
		border-radius: var(--radius);
	}
}

table {
	inline-size: 100%;
	border-spacing: 0;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	font-variant-numeric: tabular-nums;
}

th,
td {
	padding: 0.7em 1.15em;
	vertical-align: middle;
}

th {
	font-weight: 400;
	text-align: start;
}

/* Figures: every column after the pull request */
:is(th, td):not(:first-child) {
	text-align: end;
	white-space: nowrap;
}

td:not(.requested) {
	color: var(--muted);
}

thead {
	position: sticky;
	inset-block-start: 0;
	z-index: 1;

	th {
		background: var(--surface);
		color: var(--muted);
		font-size: 0.85em;

		&:first-child {
			border-start-start-radius: var(--radius);
		}

		&:last-child {
			border-start-end-radius: var(--radius);
		}
	}
}

tbody {
	tr {
		view-transition-name: match-element;
	}

	:is(th, td) {
		border-block-start: 1px solid var(--rule);
	}

	small {
		color: var(--muted);
		font-size: 0.85em;
	}
}

:is(#summary, tbody) a {
	color: inherit;
	text-decoration: none;

	&:hover {
		text-decoration: underline;
	}
}

/* Avatar in a fixed slot, so titles and sublines share one left edge */
.pull-request {
	display: grid;
	grid-template-columns: 2rem 1fr;
	column-gap: 0.85em;

	a {
		font-weight: 500;
	}

	img {
		grid-row: span 2;
		align-self: center;
		inline-size: 100%;
		block-size: auto;
		border-radius: 50%;
		background: var(--rule);
	}
}

/* A value still loading leaves its cell or time empty: a flat bar holds its place.
   An empty time draws the bar itself, since its ::before is a scroll fade in the summary */
td:empty::before,
time:empty {
	content: "";
	display: inline-block;
	inline-size: 3em;
	block-size: 0.8em;
	vertical-align: middle;
	border-radius: 0.25em;
	background: color-mix(in oklch, var(--text) 8%, var(--surface));
}

/* The whole PR, standing in for a diff since a review */
.whole {
	filter: grayscale(1);
}

.tag {
	padding: 0.1em 0.5em;
	border-radius: 1e5px;
	background: var(--rule);
}

/* Avatars with a badge for what each did */
.reviewers ul {
	display: flex;
	gap: 0.4em;
	justify-content: end;
	margin: 0;
	padding: 0;
	list-style: none;
}

.reviewers li {
	position: relative;

	img {
		display: block;
		inline-size: 1.5rem;
		block-size: auto;
		border-radius: 50%;
		background: var(--rule);
	}

	&::after {
		content: "";
		position: absolute;
		inset-block-end: -0.2em;
		inset-inline-end: -0.2em;
		inline-size: 0.85rem;
		aspect-ratio: 1;
		border-radius: 50%;
		background: var(--badge) var(--icon, none) center / 75% no-repeat;
		/* Cuts the badge out of the avatar */
		box-shadow: 0 0 0 2px var(--surface);
	}

	&.awaiting {
		--badge: var(--attention);
	}

	&.approved {
		--badge: var(--positive);
		--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E");
	}

	&.changes-requested {
		--badge: var(--negative);
		--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M8 3v6M5 6h6M5 12h6'/%3E%3C/svg%3E");
	}

	&.commented {
		--badge: var(--muted);
		--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='white'%3E%3Cpath d='M3 3.5h10v6.5H8l-3 2.5V10H3z'/%3E%3C/svg%3E");
	}
}

.added {
	color: var(--positive);
}

.deleted {
	color: var(--negative);
}

/* GitHub’s diffstat: five squares split by the share of added, then deleted lines */
.diffstat {
	--added: round(5 * var(--additions) / max(var(--additions) + var(--deletions), 1));
	/* An empty diff gets five grey squares, not five red ones */
	--deleted: min(5 - var(--added), 5 * var(--deletions));

	display: inline-block;
	inline-size: calc(5 * 0.6em + 4px);
	block-size: 0.6em;
	margin-inline-start: 0.3em;
	background: linear-gradient(
		to right,
		var(--positive) calc(var(--added) * 20%),
		var(--negative) 0 calc((var(--added) + var(--deleted)) * 20%),
		var(--border) 0
	);
	/* Cuts the strip into squares with 1px gaps */
	mask: repeating-linear-gradient(to right, black 0 calc(20% - 1px), transparent 0 20%);
}

th button {
	padding: 0;
	border: none;
	background: none;
	color: inherit;
	font: inherit;
	cursor: pointer;

	&:hover {
		color: var(--text);
	}

	/* The arrow keeps its space while hidden, so headers do not shift on sort */
	&::after {
		content: "";
		display: inline-block;
		inline-size: 1em;
		aspect-ratio: 1;
		margin-inline-start: 0.3em;
		vertical-align: -0.1em;
		background-color: currentColor;
		mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3v10M4 9l4 4 4-4'/%3E%3C/svg%3E") no-repeat center / contain;
	}
}

th:not([aria-sort]) button::after {
	visibility: hidden;
}

th[aria-sort] button {
	color: var(--text);
}

th[aria-sort="ascending"] button::after {
	scale: 1 -1;
}

footer {
	padding-block: 1.5rem 2.25rem;
	color: var(--muted);
	font-size: 0.85em;

	a {
		color: inherit;
	}
}

@media (prefers-reduced-motion: reduce) {
	::view-transition-group(*) {
		animation: none;
	}
}
