/* cntxt · cntxtlabs.co
   Type after nelson.co: system stack, one size, weight carries hierarchy,
   and no font-smoothing override, which is what keeps the weight in it.
   Colour after ia.net/writer. */

:root {
	--bg: #F7F7F7;
	--text: #222222;
	--muted: #6B6B74;
	--rule: #D6D6D6;
	--blue: #00B2FF;
	--step: 80ms;
	--ease: cubic-bezier(0.22, 0.61, 0.36, 1);
	--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
	--duration: 340ms;
}

@media (prefers-color-scheme: dark) {
	:root {
		--bg: #070707;
		--text: #F0F0F0;
		--muted: #7A7A84;
		--rule: #2A2A2A;
		--blue: #00B2FF;
	}
}

/* Turned over by a certain sequence of keys. Inverts whichever side you
   happen to be on. */

:root.flip {
	--bg: #070707;
	--text: #F0F0F0;
	--muted: #7A7A84;
	--rule: #2A2A2A;
}

@media (prefers-color-scheme: dark) {
	:root.flip {
		--bg: #F7F7F7;
		--text: #222222;
		--muted: #6B6B74;
		--rule: #D6D6D6;
	}
}

/* Chosen by hand from the menu, and remembered. It outranks the system
   setting, and the flip still turns it over. */

:root[data-theme="light"],
:root[data-theme="dark"].flip {
	--bg: #F7F7F7;
	--text: #222222;
	--muted: #6B6B74;
	--rule: #D6D6D6;
	color-scheme: light;
}

:root[data-theme="dark"],
:root[data-theme="light"].flip {
	--bg: #070707;
	--text: #F0F0F0;
	--muted: #7A7A84;
	--rule: #2A2A2A;
	color-scheme: dark;
}

/* Left alone for a while, the mark comes out to keep you company. */

.idle .mark {
	opacity: 1;
	transform: translate(var(--look-x, 0px), var(--look-y, 0px)) rotate(var(--look-r, 0deg));
	animation: bob 2.4s ease-in-out infinite;
}

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

html {
	background-color: var(--bg);
	color-scheme: light dark;
	transition: background-color 420ms ease, color 420ms ease;
	font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
	font-size: 15px;
	font-weight: 400;
	-webkit-text-size-adjust: 100%;
	scrollbar-width: none;
}

/* Hide the scrollbar indicator while preserving normal scrolling. */
html::-webkit-scrollbar {
	display: none;
}

::selection {
	background-color: rgba(0, 178, 255, 0.22);
}

/* 600px of text. Footer sits at the bottom of the viewport whenever the
   page is too short to push it there itself. */

body {
	font-size: 1rem;
	color: var(--text);
	line-height: 1.6;
	text-align: left;
	margin: 0 auto;
	padding: 15em max(1.5em, env(safe-area-inset-right)) max(2em, env(safe-area-inset-bottom)) max(1.5em, env(safe-area-inset-left));
	max-width: calc(600px + 3em);
	min-height: 100vh;
	min-height: 100svh;
	display: flex;
	flex-direction: column;
}

@media (max-width: 34rem) {
	body {
		padding-top: 6.5em;
	}
}

/* Type. Two weights for the page, a third for the wordmark. */

h1,
h2,
h3,
p,
footer {
	font-size: 1rem;
}

h1 {
	margin-top: 3em;
	margin-bottom: 0;
	font-weight: 500;
	color: var(--text);
	line-height: 1.6;
	text-wrap: balance;
}

h2 {
	margin-top: 1em;
	margin-bottom: 0;
	font-weight: 500;
	color: var(--text);
	line-height: 1.6;
}

/* Sit the following line against the title, same as cntxt and its subtitle. */
h1:not(.title) + p,
h1:not(.title) + ul,
h2 + p {
	margin-top: 0;
}


p {
	margin-top: 0;
	margin-bottom: 1.2em;
	text-wrap: pretty;
}

.lead {
	margin-top: 3em;
}

strong {
	font-weight: 500;
}

/* Masthead. Identical on every page, heaviest thing on it. */

.title {
	margin-top: 0;
	margin-bottom: 0;
	font-weight: 700;
}

.sub {
	margin-top: 0;
	margin-bottom: 0;
}

/* Links. Heavier than the text, blue on hover. No underline. */

a:link,
a:visited {
	color: var(--text);
	font-weight: 500;
	text-decoration: none;
	transition: color 170ms ease;
}

a:focus-visible {
	color: var(--blue);
}

/* Hover colours only where there is a real pointer. On a phone, :hover
   sticks to whatever was last touched and leaves it blue. */

@media (hover: hover) {
	a:hover {
		color: var(--blue);
	}
}

a:focus-visible {
	outline: 1px dotted currentColor;
	outline-offset: 0.18em;
}

/* Reveals: text that unfolds. The wordmark says how it is said, the
   subtitle thinks highly of the place, the footer counts from the
   beginning. With a pointer they open on hover or keyboard focus; on a
   phone a tap opens one and another tap closes it. */

.grow {
	display: inline-block;
	max-width: 0;
	opacity: 0;
	overflow: hidden;
	vertical-align: bottom;
	white-space: nowrap;
	transition: max-width var(--duration) var(--ease), opacity 260ms ease;
}

.reveals:focus-visible .grow,
.reveals.open .grow {
	max-width: 8ch;
	opacity: 1;
}

.reveals:focus-visible .grow.grow-wide,
.reveals.open .grow.grow-wide,
.reveals:focus-visible .grow.grow-long,
.reveals.open .grow.grow-long {
	max-width: 40ch;
}

.reveals:focus-visible .grow.grow-time,
.reveals.open .grow.grow-time {
	max-width: 44ch;
}

.reveals:focus-visible .grow.grow-live,
.reveals.open .grow.grow-live {
	max-width: 46ch;
}

@media (hover: hover) {
	.reveals:hover .grow {
		max-width: 8ch;
		opacity: 1;
	}

	.reveals:hover .grow.grow-wide,
	.reveals:hover .grow.grow-long {
		max-width: 40ch;
	}

	.reveals:hover .grow.grow-time {
		max-width: 44ch;
	}

	.reveals:hover .grow.grow-live {
		max-width: 46ch;
	}

	.reveals:hover .strike {
		text-decoration-color: currentColor;
	}
}

/* On a phone the hidden words are not squeezed into a line that cannot
   wrap. Closed, they are not there at all. Open, they sit in the sentence
   like any other words, wrap where they need to, and fade in. */

@media (hover: none) {
	.grow {
		display: none;
		max-width: none;
		white-space: normal;
		transition: none;
	}

	.reveals.open .grow {
		display: inline;
		animation: growIn 320ms var(--ease-out);
	}

	.reveals {
		cursor: pointer;
		-webkit-tap-highlight-color: transparent;
		touch-action: manipulation;
	}
}

@keyframes growIn {
	from { opacity: 0; }
}

/* Live lines and the wordmark's pronunciation. A heading keeps its weight;
   what it reveals is plain text. The wordmark is the one reveal that
   slides open rather than typing. */

h1 .grow,
.titlelink .grow {
	font-weight: 400;
}

/* Typed reveals. When motion is welcome, the script keeps each hidden part
   in the page for screen readers but out of sight, and types a copy
   beside it instead of sliding it open. The overrides are forceful on
   purpose: every way a reveal opens (hover, focus, a tap, the company's
   name typed) now goes through the typing. */

.typing-reveals .reveals:not(.titlelink) .grow {
	position: absolute !important;
	display: inline-block !important;
	width: 1px !important;
	height: 1px !important;
	max-width: none !important;
	margin: -1px !important;
	padding: 0 !important;
	overflow: hidden !important;
	clip-path: inset(50%) !important;
	white-space: nowrap !important;
	opacity: 1 !important;
	animation: none !important;
}

.typed {
	display: inline;
}

.typed.caret::after,
.descriptor.typing em::after {
	content: "";
	display: inline-block;
	width: 1px;
	height: 1.05em;
	margin-left: 1px;
	vertical-align: -0.15em;
	background-color: var(--blue);
}

/* What kind of company. Different on every load, and on every click,
   when the word is backspaced away and the next one typed in behind the
   same cyan caret. On arrival at the site it is typed in too. */

.descriptor.typing em::after {
	content: "";
	display: inline-block;
	width: 1px;
	height: 1.05em;
	margin-left: 1px;
	vertical-align: -0.15em;
	background-color: var(--blue);
}

.descriptor {
	display: inline-block;
	cursor: pointer;
}

/* A reveal with punctuation after it. The collapsed text is an inline
   block, which would otherwise let the comma wrap onto a line of its own. */

.nowrap {
	white-space: nowrap;
}

@media (hover: none) {
	.nowrap {
		white-space: normal;
	}
}

.descriptor:focus-visible {
	outline: 1px dotted currentColor;
	outline-offset: 0.18em;
}

.strike {
	transition: text-decoration-color 200ms ease;
	text-decoration: line-through;
	text-decoration-color: transparent;
	text-decoration-thickness: 1px;
}

.reveals:focus-visible .strike,
.reveals.open .strike {
	text-decoration-color: currentColor;
}

/* A narrow window with a pointer has no room for the longest line. A
   phone has, because there it wraps. */

@media (hover: hover) and (max-width: 34rem) {
	.grow-wide {
		display: none;
	}
}

.titlelink {
	font-weight: 700;
	position: relative;
}

/* The mark sits above the wordmark, out of the flow so nothing below it
   moves. It drops in, turns itself the right way up, then keeps bobbing
   for as long as you stay. */

.mark {
	position: absolute;
	left: 0;
	bottom: calc(100% + 0.5em);
	width: 1.7em;
	height: 1.7em;
	color: currentColor;
	opacity: 0;
	pointer-events: none;
	transform: translateY(10px) scale(0.72) rotate(-150deg);
	transition: opacity 240ms ease, transform 560ms var(--ease-out);
}

.titlelink:focus-visible .mark,
.titlelink:hover .mark {
	opacity: 1;
	transform: translate(var(--look-x, 0px), var(--look-y, 0px)) scale(1) rotate(var(--look-r, 0deg));
	animation: bob 2.4s ease-in-out 560ms infinite;
}

@keyframes bob {
	0%, 100% { translate: 0 0; }
	50% { translate: 0 -5px; }
}

/* Typed the company name. Everything that hides comes out for a moment. */

.egg .mark {
	display: block;
	opacity: 1;
	transform: none;
	animation: bob 2.4s ease-in-out infinite;
}

.project-symbols-enabled.egg .sym {
	display: block;
	opacity: 1;
	transform: none;
}

.egg .titlelink .grow,
.egg .reveals .grow {
	max-width: 40ch;
	opacity: 1;
}

.egg .reveals .strike {
	text-decoration-color: currentColor;
}

@keyframes markIn {
	from {
		opacity: 0;
		transform: translateY(10px) scale(0.72) rotate(-150deg);
	}
	to {
		opacity: 1;
		transform: translateY(0) scale(1) rotate(0deg);
	}
}

/* Without a pointer, the first tap on the wordmark says its name in full
   and the second follows the link. */

@media (hover: none) {
	.projects a,
	.projects .name {
		display: inline-block;
		padding: 0.4em 0;
	}
}

/* No hover on a phone, so the mark arrives on its own, bobs twice to say
   hello, and then rests. Anything that moves for good is a distraction. */

@media (max-width: 34rem), (hover: none) {
	.mark {
		color: var(--text);
		pointer-events: auto;
		animation: markIn 560ms var(--ease-out) 700ms forwards,
			bob 2.4s ease-in-out 1260ms 2;
	}

	.titlelink:hover .mark,
	.titlelink:focus-visible .mark {
		color: var(--text);
	}
}

.titlelink:link,
.titlelink:visited {
	color: var(--text);
}

.titlelink:focus-visible {
	color: var(--blue);
}

@media (hover: hover) {
	.titlelink:hover {
		color: var(--blue);
	}
}

/* Nav. Home only. */

nav {
	margin-top: 2em;
	margin-bottom: 2em;
}

nav a {
	margin-right: 1.5em;
	font-weight: 400;
}

nav a:link,
nav a:visited {
	color: var(--muted);
}

nav a:focus-visible {
	color: var(--blue);
}

nav a[aria-current="page"]:link,
nav a[aria-current="page"]:visited {
	color: var(--text);
	font-weight: 500;
}

nav a[aria-current="page"]:hover {
	color: var(--text);
}

/* Live metrics, inline in the sentence. Tabular figures hold each number's
   width steady while it counts, so the words after it do not shuffle. The
   mark and the number take the accent only when you point at them. */

.metric {
	white-space: nowrap;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}

.metric .metric-label {
	white-space: normal;
}

.metrics svg {
	width: 1.05em;
	height: 1.05em;
	margin-right: 0.35em;
	vertical-align: -0.16em;
	color: var(--text);
	transition: color 170ms ease;
}

.figure {
	display: inline-block;
	font-weight: 500;
	font-variant-numeric: tabular-nums;
	font-feature-settings: "tnum" 1;
	text-align: left;
	transition: color 170ms ease;
}

@media (hover: hover) {
	.metric:hover svg,
	.metric:hover .figure {
		color: var(--blue);
	}
}

/* Project list. Name, then what it is. */

ul {
	margin: 0;
	padding: 0;
	list-style: none;
}

.projects {
	margin-top: 0.75em;
}

.projects li {
	position: relative;
	margin-bottom: 1.5em;
}

/* The name is the link, but the whole row answers to it: point at or tap
   the description and you are pointing at the name. */

.projects li > a::after {
	content: "";
	position: absolute;
	inset: 0;
}

.projects .desc {
	display: block;
	margin-top: 0.1em;
}

.projects .name {
	font-weight: 500;
}

/* Unpublished. The name and the description stay in place, just
   out of focus, and they are no longer links. The blur is a shadow of
   transparent text rather than filter: blur(), whose compositing layer
   tinted the heavier names pink on some displays. */

.projects .blurred .name,
.projects .blurred .desc {
	color: transparent;
	text-shadow: 0 0 7px var(--text);
	user-select: none;
	-webkit-user-select: none;
	pointer-events: none;
}

/* Symbols are dormant. Add project-symbols-enabled to html to restore them. */

/* Each project keeps a symbol in the left margin. It stays out of the text
   flow, so nothing moves when it arrives. Pointer devices with room for it
   only: a phone never sees these. */

.sym {
	display: none;
}

@media (hover: hover) and (min-width: 48rem) {
	.sym {
		display: block;
		position: absolute;
		right: 100%;
		margin-right: 0.42em;
		top: 0.12em;
		width: 1.15em;
		height: 1.15em;
		color: var(--blue);
		opacity: 0;
		pointer-events: none;
		transform: translateY(5px) scale(0.7) rotate(-90deg);
		transition: opacity 130ms ease, transform 260ms var(--ease);
	}

	.sym svg {
		display: block;
		width: 100%;
		height: 100%;
	}

	.project-symbols-enabled .projects a:hover + .sym,
	.project-symbols-enabled .projects a:focus-visible + .sym {
		opacity: 1;
		transform: none;
	}
}

.projects.collection {
	margin-top: 0.75em;
}

/* Buttons, after iA. A small solid pill in the ink colour with the label
   in the paper colour, so it turns over with the page in dark mode and on
   an ink band. The quiet one is a tint of the ink, for the second thing
   you might want. Pointed at, a button leans in a little; pressed, it
   gives. On a touch screen each one answers to a slightly larger area
   than it draws, so it is still easy to hit. */

.actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6em;
	margin: 2em 0 2.25em;
}

.action {
	display: flex;
}

.action[hidden] {
	display: none;
}

.button,
a.button:link,
a.button:visited {
	appearance: none;
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	margin: 0;
	padding: 0.38em 0.95em;
	border: 0;
	border-radius: 999px;
	background-color: var(--text);
	color: var(--bg);
	font: inherit;
	font-weight: 600;
	line-height: 1.2;
	text-decoration: none;
	cursor: pointer;
	touch-action: manipulation;
	-webkit-tap-highlight-color: transparent;
	transition: transform 240ms var(--ease), background-color 420ms ease, color 420ms ease;
}

.button.quiet,
a.button.quiet:link,
a.button.quiet:visited {
	background-color: var(--rule);
	background-color: color-mix(in srgb, var(--text) 9%, var(--bg));
	color: var(--text);
}

@media (hover: hover) {
	.button:hover,
	a.button:hover {
		color: var(--bg);
		transform: scale(1.03);
	}

	.button.quiet:hover,
	a.button.quiet:hover {
		color: var(--text);
	}
}

.button:active {
	transform: scale(0.97);
	transition-duration: 90ms;
}

.button:focus-visible,
a.button:focus-visible {
	color: var(--bg);
	outline: 2px solid var(--blue);
	outline-offset: 3px;
}

.button.quiet:focus-visible,
a.button.quiet:focus-visible {
	color: var(--text);
}

@media (pointer: coarse) {
	.button::after {
		content: "";
		position: absolute;
		inset: -0.45em -0.2em;
	}
}

/* Download buttons, after getleft.app. With a pointer, the label fades
   and the pill keeps its width but grows upward into a card of the same
   colour: the App Store QR code in the label's colour (light on the dark
   pill, dark on the light one), and under it a short line saying what to
   scan or search for. The card is one layer under the label, uncovered
   from the bottom up with a clip, so nothing around the button shifts.
   On a phone the button simply opens the App Store. */

.button .qr {
	display: none;
}

@media (hover: hover) and (pointer: fine) {
	.button.download {
		isolation: isolate;
	}

	.button.download:hover,
	.button.download:focus-visible {
		z-index: 4;
		transform: none;
		filter: drop-shadow(0 0.6em 1.1em rgba(0, 0, 0, 0.2));
	}

	.button.download .button-label {
		transition: opacity 140ms ease 80ms;
	}

	.button.download:hover .button-label,
	.button.download:focus-visible .button-label {
		opacity: 0;
		transition: opacity 90ms linear;
	}

	/* The pill is 1.96em tall (a 1.2 line and 0.38em above and below), so
	   its corners are 0.98em; the square keeps those corners as it grows. */
	.button.download .qr {
		position: absolute;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: -1;
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 0.4em;
		padding: 0.5em 0.5em 0.6em;
		border-radius: 0.98em;
		background-color: inherit;
		pointer-events: none;
		clip-path: inset(calc(100% - 1.96em) 0 0 0 round 0.98em);
		transition: clip-path 300ms cubic-bezier(0.32, 0.72, 0, 1);
	}

	.button.download:hover .qr,
	.button.download:focus-visible .qr {
		pointer-events: auto;
		clip-path: inset(0 0 0 0 round 0.98em);
		transition: clip-path 480ms cubic-bezier(0.22, 1, 0.36, 1);
	}

	.qr-code,
	.qr-text {
		opacity: 0;
		transform: scale(0.9);
		transition: opacity 100ms ease, transform 200ms ease;
	}

	/* Three set lines, small, as on getleft.app: what to do, the app's
	   name, where. */
	.qr-text {
		font-size: 0.58em;
		font-weight: 400;
		line-height: 1.3;
		text-align: center;
		white-space: nowrap;
		transform: translateY(0.4em);
	}

	.qr-text strong {
		font-weight: 600;
	}

	.qr-code {
		display: block;
		flex: none;
		width: 100%;
		aspect-ratio: 1;
		background-color: currentColor;
		-webkit-mask-size: contain;
		mask-size: contain;
		-webkit-mask-repeat: no-repeat;
		mask-repeat: no-repeat;
	}

	.button.download:hover .qr-text,
	.button.download:focus-visible .qr-text,
	.button.download:hover .qr-code,
	.button.download:focus-visible .qr-code {
		opacity: 1;
		transform: none;
		transition: opacity 200ms ease 110ms, transform 380ms var(--ease-out) 80ms;
	}
}

/* Footer. Held to the bottom. The dashed rule walks slowly, like a
   cursor at the end of the page. The rule is one tile wider than the
   footer and slides by that tile, so the walk is a transform the
   compositor runs, not a repaint on every frame. */

footer {
	position: relative;
	overflow: hidden;
	overflow: clip;
	margin-top: auto;
	padding-top: 0.75em;
	font-size: 0.8667rem; /* 13px: the one step down from the page */
	color: var(--muted);
	text-align: left;
}

footer::before {
	content: "";
	position: absolute;
	top: 0;
	left: -12px;
	right: 0;
	height: 1px;
	background-image: repeating-linear-gradient(
		90deg,
		var(--rule) 0 3px,
		transparent 3px 9px
	);
	background-size: 12px 1px;
	background-repeat: repeat-x;
	animation: rule-crawl 22s linear infinite;
}

@keyframes rule-crawl {
	to { transform: translateX(12px); }
}

footer p {
	margin: 0;
	font-size: inherit;
	overflow-wrap: break-word;
}

/* Everything eases up on arrival. */

@keyframes fadeInFromBottom {
	from {
		opacity: 0;
		transform: translateY(0.9em);
	}
	to {
		opacity: 1;
		transform: none;
	}
}

.reveal > * {
	animation: fadeInFromBottom 680ms var(--ease-out) var(--d, 0ms) both;
}

.reveal > *:nth-child(2) { --d: calc(1 * var(--step)); }
.reveal > *:nth-child(3) { --d: calc(2 * var(--step)); }
.reveal > *:nth-child(4) { --d: calc(3 * var(--step)); }
.reveal > *:nth-child(5) { --d: calc(4 * var(--step)); }
.reveal > *:nth-child(6) { --d: calc(5 * var(--step)); }
.reveal > *:nth-child(7) { --d: calc(6 * var(--step)); }
.reveal > *:nth-child(8) { --d: calc(7 * var(--step)); }
.reveal > *:nth-child(n + 9) { --d: calc(8 * var(--step)); }

/* A list does not arrive as one block. Its rows follow each other in. */

.reveal > .projects {
	animation: none;
}

.reveal > .projects > li {
	animation: fadeInFromBottom 680ms var(--ease-out) both;
	animation-delay: calc(var(--d, 0ms) + var(--i, 0) * 55ms);
}

.reveal > .projects > li:nth-child(2) { --i: 1; }
.reveal > .projects > li:nth-child(3) { --i: 2; }
.reveal > .projects > li:nth-child(4) { --i: 3; }
.reveal > .projects > li:nth-child(n + 5) { --i: 4; }

/* Coming from another page on the site, the masthead is already where it
   belongs. It stays put and only the page under it arrives. */

.within .reveal > .title,
.within .reveal > .sub {
	animation: none;
}

/* Between pages, where the browser can, the old page hands over to the
   new one with a short crossfade instead of a cut. */

@media (prefers-reduced-motion: no-preference) {
	@view-transition {
		navigation: auto;
	}
}

@media (prefers-reduced-motion: reduce) {
	.reveal > *,
	.reveal > .projects > li {
		animation: none;
	}

	.grow,
	.strike,
	a,
	.mark {
		transition: none;
	}

	.titlelink:hover .mark,
	.egg .mark,
	.project-symbols-enabled.egg .sym {
		animation: none;
	}

	.sym,
	.button,
	.button.download .qr,
	.button.download:hover .qr,
	.qr-code,
	.qr-text,
	.button.download:hover .qr-code,
	.button.download:hover .qr-text,
	.button.download .button-label,
	.button.download:hover .button-label {
		transition: none;
	}

	html {
		transition: none;
	}

	.idle .mark {
		animation: none;
	}

	.mark {
		transform: none;
	}

	footer::before {
		animation: none;
	}

	@media (max-width: 34rem) {
		.mark {
			animation: none;
			opacity: 1;
		}
	}
}

/* A press of the space bar. Fixed layer, no pointer events, gone in a few
   seconds. It is a child of body, so it has to opt out of the arrival
   animation that every other child gets. */

.confetti {
	position: fixed;
	inset: 0;
	z-index: 9;
	overflow: hidden;
	pointer-events: none;
	opacity: 1;
	animation: none;
}

.confetti i {
	position: absolute;
	top: -8vh;
	display: block;
	background-color: var(--blue);
	animation-name: fall;
	animation-timing-function: linear;
	animation-fill-mode: forwards;
}

@keyframes fall {
	from {
		transform: translate(0, 0) rotate(0deg);
	}
	to {
		transform: translate(var(--drift, 0), 116vh) rotate(var(--spin, 0deg));
	}
}

/* The menu. Command K, Control K or a slash opens it. It is not a panel
   over the page but the page itself, turned into a list: same column,
   same type, the dashed rule from the footer under what you type, and
   the line you are on marked the way outbound links are, with >. */

.menu {
	position: fixed;
	inset: 0;
	width: 100%;
	max-width: none;
	height: 100%;
	max-height: none;
	margin: 0;
	border: 0;
	padding: 15em max(1.5em, env(safe-area-inset-right)) 2em max(1.5em, env(safe-area-inset-left));
	background-color: var(--bg);
	color: var(--text);
	font: inherit;
	line-height: 1.6;
	overflow-y: auto;
}

.menu::backdrop {
	background: transparent;
}

.menu[open] {
	animation: menuIn 200ms var(--ease-out);
}

@keyframes menuIn {
	from { opacity: 0; transform: translateY(0.4em); }
	to { opacity: 1; transform: none; }
}

.menu-column {
	max-width: 600px;
	margin: 0 auto;
}

.menu input {
	display: block;
	width: 100%;
	margin: 0 0 0.75em;
	padding: 0 0 0.6em;
	border: 0;
	border-radius: 0;
	background: repeating-linear-gradient(90deg, var(--rule) 0 3px, transparent 3px 9px) bottom left / 12px 1px repeat-x;
	color: var(--text);
	font: inherit;
	font-weight: 500;
	outline: none;
	appearance: none;
}

.menu input::placeholder {
	color: var(--muted);
	font-weight: 400;
}

.menu ul {
	margin: 0;
}

.menu li {
	display: flex;
	padding: 0.2em 0;
	cursor: pointer;
}

.menu li::before {
	content: ">";
	width: 0;
	overflow: hidden;
	flex: none;
	transition: width 140ms var(--ease);
}

.menu li[aria-selected="true"] {
	color: var(--blue);
}

.menu li[aria-selected="true"]::before {
	width: 1.1em;
}

.menu .name {
	flex: 1;
	font-weight: 500;
}

.menu .hint {
	padding-left: 1em;
	color: var(--muted);
	text-align: right;
}

.menu li[aria-selected="true"] .hint {
	color: inherit;
}

.menu .empty {
	color: var(--muted);
	cursor: default;
}

@media (max-width: 34rem) {
	.menu {
		padding-top: 6.5em;
	}
}

@media (prefers-reduced-motion: reduce) {
	.menu[open] {
		animation: none;
	}

	.menu li::before {
		transition: none;
	}
}
