.button_to {
	display: inline-flex;
	margin: 0;
	padding: 0;
}

.destroy {
	align-items: center;
	border: none;
	background: none;
	color: var(--color-destroy);
	cursor: pointer;
	display: inline-flex;
	font-family: "Hanken Grotesk", sans-serif;
	font-size: 0.85rem;
	font-weight: 500;
	gap: 0.4em;
	padding: 0.6em 0;
	opacity: 0.5;
	transition: opacity 0.15s ease;

	&:hover {
		opacity: 1;
	}

	svg {
		width: 13px;
		height: 13px;
	}
}

.form-container {
	--accent-tint: var(--color-surface-alt);

	display: flex;
	flex-direction: column;
	gap: 0;
}

/* A modal is shrink-to-fit, so one made of prose and choices rather than fields
   sizes to its longest sentence — a confirmation was coming out a hundred
   pixels wider than the settings modals either side of it. Held to the dialog's
   own minimum, which is the width every other settings modal settles at.
   Not on .form-container itself: the item modal is legitimately wider. */
.form-container--confirm {
	max-width: 42ch;
}

/* Scoped to .form-container: this is the modal/auth form shell, and as a bare
   form:not(.button_to) it also caught the one-select access form in a settings
   row, padding it out by 1.5em and inflating those rows. */
.form-container form:not(.button_to) {
	display: flex;
	flex-direction: column;
	gap: 1.25em;
	padding: 1.5em;

	.form-field:not([hidden]) {
		display: flex;
		flex-direction: column;
		gap: 0.45em;

		&:has(.color-select-wrapper),
		&:has(.sticker-select-wrapper) {
			align-items: flex-start;
		}

		label {
			color: var(--color-text);
			font-size: 0.75rem;
			font-weight: 600;
			letter-spacing: 0.04em;
			text-transform: uppercase;

			&:has(.color-swatch),
			&:has(.sticker-swatch),
			&:has(.sticker-none-mark) {
				display: flex;
			}
		}

		input,
		select {
			background-color: var(--accent-tint);
			border: none;
			border-radius: var(--radius-md);
			font-size: 1rem;
			font-weight: 400;
			letter-spacing: -0.01em;
			padding: 0.7em 1em;
			color: var(--color-text);
			font-family: "Hanken Grotesk", sans-serif;
			transition: box-shadow 0.15s ease;

			&::placeholder {
				color: var(--color-text-subtle);
			}

			&:focus {
				box-shadow: 0 0 0 2px var(--color-accent);
				outline: none;
			}
		}

		/* The browser's own button, sitting in the same tinted well the text
		   fields use, rather than floating on the modal's white. */
		input[type="file"] {
			cursor: pointer;
			font-size: 0.85rem;
			padding: 0.6em 0.85em;

			&::file-selector-button {
				background: var(--color-surface);
				border: none;
				border-radius: var(--radius-pill);
				color: var(--color-text-muted);
				cursor: pointer;
				font-family: "Hanken Grotesk", sans-serif;
				font-size: 0.8rem;
				font-weight: 600;
				margin-right: 0.85em;
				padding: 0.45em 1em;
			}
		}

		select {
			cursor: pointer;
			appearance: none;
			background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23736c7d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
			background-repeat: no-repeat;
			background-position: right 0.85em center;
			background-size: 1em;
			padding-right: 2.5em;
		}
	}

	/* Wraps the fields so a single disabled attribute takes the lot. */
	.form-fieldset {
		border: none;
		display: flex;
		flex-direction: column;
		gap: 1.25em;
		margin: 0;
		min-width: 0;
		padding: 0;

		&[disabled] {
			opacity: 0.75;
		}
	}

	.form-field-row {
		display: flex;
		gap: var(--space-md);
	}

	/* A control sitting in the field's own tinted well beside the input, rather
	   than on the modal's white — the colour chip next to a select it colours. */
	.form-field-row--tinted {
		align-items: center;
		gap: 0.6em;

		> select {
			flex: 1;
			min-width: 0;
		}
	}

	/* Column picker: sits at the top of the modal as a bare dot + a row of
	   chips, one per person. A dropdown used to do this and can't any more — an
	   item is for a set of people now, and a set is what the chips show at a
	   glance, ticked or not, without opening anything. It wraps rather than
	   scrolling: a household has a handful of columns, not a list. */
	.form-column {
		align-items: center;
		display: flex;
		flex-wrap: wrap;
		gap: 0.55em;
		margin-top: -0.5em;
		/* The sticker strip hangs from this row rather than from the chip that
		   opens it: against the chip it would be a max-content box — which makes
		   the grid's own percentage cap circular — and on a phone it would open
		   off the right-hand edge. */
		position: relative;

		select {
			appearance: none;
			background-color: transparent;
			background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23736c7d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
			background-position: right center;
			background-repeat: no-repeat;
			background-size: 0.9em;
			border: none;
			border-radius: var(--radius-sm);
			color: var(--accent-ink, var(--color-text));
			cursor: pointer;
			font-family: "Hanken Grotesk", sans-serif;
			font-size: 0.85rem;
			font-weight: 600;
			letter-spacing: -0.01em;
			padding: 0 1.4em 0 0;

			&:focus-visible {
				outline: 2px solid var(--form-accent);
				outline-offset: 3px;
			}
		}
	}

	/* Who an item is for. A chip that opens a list, like the sticker beside it:
	   this was a row of named chips, which read beautifully and sized terribly —
	   the dialog is shrink-to-fit, so six people, or one long name, pushed the
	   modal out past the rest of the form. The chip below is the same size
	   whatever shape the planner is. */
	.form-columns {
		flex-shrink: 1;
		min-width: 0;
	}

	.form-columns-toggle {
		align-items: center;
		background: none;
		border: 1px solid var(--color-surface-alt);
		border-radius: var(--radius-pill);
		color: var(--color-text-muted);
		cursor: pointer;
		display: flex;
		font-family: inherit;
		font-size: 0.72rem;
		font-weight: 600;
		gap: 0.35em;
		letter-spacing: 0.01em;
		max-width: 14ch;
		padding: 0.25em 0.7em;
		transition:
			border-color 0.15s ease,
			color 0.15s ease;

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

		&:focus-visible {
			outline: 2px solid var(--form-accent, var(--color-accent));
			outline-offset: 2px;
		}
	}

	.form-columns-summary {
		align-items: center;
		display: flex;
		gap: 0.35em;
		min-width: 0;
	}

	/* The dots overlap a little, so four of them cost about the width of two —
	   the chip has to hold its size, and past the limit the label does the
	   counting anyway. */
	.form-columns-dots {
		display: flex;
		flex-shrink: 0;

		.form-columns-dot + .form-columns-dot {
			margin-left: -0.2em;
		}
	}

	/* A long name gives way rather than widening the modal. This one rule is the
	   whole reason the control changed shape. */
	.form-columns-label {
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.form-columns-dot {
		background: var(--chip-color, var(--color-text-subtle));
		border: 1px solid var(--color-surface);
		border-radius: 50%;
		flex-shrink: 0;
		height: 0.6em;
		width: 0.6em;
	}

	/* The list itself, anchored to the row like the sticker strip so it can
	   never widen the dialog: it is out of flow, and what it holds is its own
	   business. */
	/* Inside the form as well as at the top of the file. A panel that lays
	   itself out — the column list sets display: flex — does so from in here,
	   which outweighs a bare .form-popover[hidden] and leaves a shut panel
	   painted on screen. Stated at this depth it outweighs them all instead,
	   and covers any panel added to this row later. */
	.form-popover[hidden] {
		display: none;
	}

	.form-columns-list {
		display: flex;
		flex-direction: column;
		gap: 0.1em;
		/* Long enough to be worth scrolling rather than filling the screen. */
		max-height: 15em;
		min-width: 11em;
		overflow-y: auto;
	}

	.form-columns-rule {
		border: 0;
		border-top: 1px solid var(--color-surface-alt);
		margin: 0.3em 0.5em;
	}

	.form-columns-option {
		align-items: center;
		border-radius: var(--radius-sm);
		color: var(--color-text-muted);
		cursor: pointer;
		display: flex;
		font-size: 0.8rem;
		font-weight: 500;
		gap: 0.5em;
		padding: 0.35em 0.5em;
		transition:
			background-color 0.15s ease,
			color 0.15s ease;

		input[type="checkbox"] {
			height: 0;
			margin: 0;
			opacity: 0;
			position: absolute;
			width: 0;
		}

		.form-columns-name {
			overflow: hidden;
			text-overflow: ellipsis;
			white-space: nowrap;
		}

		.form-columns-note {
			color: var(--color-text-subtle);
			font-size: 0.72em;
			font-weight: 500;
			margin-left: auto;
			padding-left: 0.6em;
		}

		/* Both halves of the answer in one mark: the lane's colour says who, and
		   whether it is filled says yes or no. A row of these reads as "as many
		   as you like" — the bare names it replaced read as a menu where picking
		   one would drop the last. */
		.form-columns-box {
			border: 1.5px solid var(--chip-color, var(--color-text-subtle));
			border-radius: 0.28em;
			flex-shrink: 0;
			height: 1em;
			position: relative;
			transition:
				background-color 0.12s ease,
				border-color 0.12s ease;
			width: 1em;

			&::after {
				border-bottom: 2px solid var(--color-surface);
				border-left: 2px solid var(--color-surface);
				content: "";
				height: 0.22em;
				left: 50%;
				opacity: 0;
				position: absolute;
				top: 45%;
				transform: translate(-50%, -50%) rotate(-45deg);
				width: 0.45em;
			}
		}

		input:checked ~ .form-columns-box {
			background-color: var(--chip-color, var(--color-text-subtle));

			&::after {
				opacity: 1;
			}
		}

		svg {
			height: 0.8rem;
			margin-left: auto;
			opacity: 0.5;
			width: 0.8rem;
		}

		&:hover {
			background-color: var(--color-surface-alt);
		}

		&:has(input:checked) {
			color: var(--color-text);
			font-weight: 600;
		}

		&:has(input:focus-visible) {
			outline: 2px solid var(--form-accent);
			outline-offset: -2px;
		}

		/* Someone else's lane: it says who the item is for without pretending
		   you can change it. */
		&.form-columns-option--locked {
			cursor: default;
			opacity: 0.65;

			&:hover {
				background-color: transparent;
			}
		}
	}

	/* Maybe: a pill toggle next to the column, quiet until it is on. */
	.form-maybe {
		align-items: center;
		background: none;
		border: 1px solid var(--color-surface-alt);
		border-radius: var(--radius-pill);
		color: var(--color-text-subtle);
		cursor: pointer;
		display: flex;
		font-size: 0.72rem;
		font-weight: 600;
		gap: 0.35em;
		letter-spacing: 0.01em;
		margin-left: auto;
		padding: 0.25em 0.7em;
		text-transform: none;
		transition:
			background-color 0.15s ease,
			border-color 0.15s ease,
			color 0.15s ease;

		input[type="checkbox"] {
			height: 0;
			margin: 0;
			opacity: 0;
			position: absolute;
			width: 0;
		}

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

		&:has(input:checked) {
			background-color: color-mix(
				in srgb,
				var(--form-accent) 12%,
				var(--color-surface)
			);
			border-color: var(--form-accent);
			color: var(--accent-ink, var(--color-text));
		}

		&:has(input:focus-visible) {
			outline: 2px solid var(--form-accent);
			outline-offset: 2px;
		}
	}

	/* Time: start and end fields with duration shortcuts sat between them. */
	.time-picker {
		gap: 0.6em;

		/* The day, in the same quiet register as the repeat line: a label and a
		   borderless control, not another boxed field. */
		.time-picker-date {
			align-items: center;
			display: flex;
			gap: 0.3em;
			margin-bottom: 0.15em;

			label {
				color: var(--color-text-muted);
				font-size: 0.85rem;
				font-weight: 500;
				letter-spacing: -0.01em;
				text-transform: none;
			}

			input[type="date"] {
				background-color: transparent;
				border: none;
				border-radius: var(--radius-sm);
				color: var(--color-text-muted);
				cursor: pointer;
				font-family: "Hanken Grotesk", sans-serif;
				font-size: 0.85rem;
				font-weight: 600;
				letter-spacing: -0.01em;
				padding: 0.25em 0.4em;
				transition:
					background-color 0.15s ease,
					color 0.15s ease;

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

				&:focus-visible {
					outline: 2px solid var(--form-accent, var(--color-accent));
					outline-offset: 1px;
				}
			}
		}

		.time-picker-row {
			align-items: center;
			display: flex;
			flex-wrap: wrap;
			gap: 0.5em;
		}

		/* Free-text time entry with a half-hourly list, in place of
		   input[type="time"] and its wildly varying native UI. Nested under
		   .time-field so it outweighs the generic .form-field input styling. */
		.time-field {
			flex-shrink: 0;
			position: relative;

			.time-field-input {
				font-variant-numeric: lining-nums tabular-nums;
				padding: 0.5em 0.7em;
				text-align: center;
				width: 5.5em;
			}

			.time-field-list {
				background-color: var(--color-surface);
				border-radius: var(--radius-md);
				box-shadow: var(--shadow-lift);
				list-style: none;
				margin: 0.3em 0 0;
				/* Its resting size, but never taller than the room it was
				   measured to have. The list overlays the form, but it is still
				   laid out inside a modal that is only as tall as its contents:
				   one that outgrew it would hand the modal a scrollbar and shunt
				   the fields around instead of sitting over them. */
				max-height: min(11em, var(--time-field-room, 11em));
				overflow-y: auto;
				padding: 0.3em;
				position: absolute;
				top: 100%;
				width: 100%;
				z-index: 10;

				&[hidden] {
					display: none;
				}
			}

			.time-field-option {
				border-radius: var(--radius-sm);
				cursor: pointer;
				font-size: 0.85rem;
				font-variant-numeric: lining-nums tabular-nums;
				padding: 0.35em 0.5em;
				text-align: center;

				&:hover,
				&[data-active="true"] {
					background-color: color-mix(
						in srgb,
						var(--form-accent) 14%,
						var(--color-surface)
					);
					color: var(--accent-ink, var(--color-text));
				}
			}
		}

		.time-picker-durations {
			display: flex;
			flex-wrap: wrap;
			gap: 0.3em;
		}

		.time-chip {
			background-color: var(--color-surface-alt);
			border: 1px solid transparent;
			border-radius: var(--radius-pill);
			color: var(--color-text-muted);
			cursor: pointer;
			font-family: "Hanken Grotesk", sans-serif;
			font-size: 0.75rem;
			font-weight: 600;
			letter-spacing: -0.01em;
			padding: 0.4em 0.7em;
			transition:
				background-color 0.15s ease,
				border-color 0.15s ease,
				color 0.15s ease;

			&:hover {
				background-color: color-mix(
					in srgb,
					var(--form-accent) 14%,
					var(--color-surface)
				);
				color: var(--accent-ink, var(--color-text));
			}

			&:active {
				transform: translateY(1px);
			}

			&:focus-visible {
				outline: 2px solid var(--form-accent);
				outline-offset: 2px;
			}
		}
	}

	/* A single quiet line in sentence case, kept out of .form-field so it does
	   not inherit the uppercase labels of the fields around it. Repeat on an
	   item, and how a calendar is kept: the same control, so the same rule
	   rather than a second copy of it. */
	/* The source picker has to wrap the fields it swaps, so its wrapper carries
	   the form's own column and gap rather than collapsing them into a block. */
	.form-source-group {
		display: flex;
		flex-direction: column;
		gap: 1.25em;
		min-width: 0;
	}

	.form-repeat,
	.form-keep {
		display: flex;
		flex-direction: column;
		gap: 0.4em;

		/* Set unconditionally above, which would otherwise outrank [hidden]. */
		&[hidden] {
			display: none;
		}

		.form-repeat-row,
		.form-keep-row {
			align-items: center;
			display: flex;
			flex-wrap: wrap;
			gap: 0.3em;

			&[hidden] {
				display: none;
			}
		}

		label {
			color: var(--color-text-muted);
			font-size: 0.85rem;
			font-weight: 500;
			letter-spacing: -0.01em;
			text-transform: none;
		}

		select,
		input[type="date"] {
			background-color: transparent;
			border: none;
			border-radius: var(--radius-sm);
			color: var(--color-text-muted);
			cursor: pointer;
			font-family: "Hanken Grotesk", sans-serif;
			font-size: 0.85rem;
			font-weight: 600;
			letter-spacing: -0.01em;
			padding: 0.25em 0.4em;
			transition:
				background-color 0.15s ease,
				color 0.15s ease;

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

			&:focus-visible {
				outline: 2px solid var(--form-accent, var(--color-accent));
				outline-offset: 1px;
			}
		}

		select {
			appearance: none;
			background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23736c7d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
			background-position: right 0.3em center;
			background-repeat: no-repeat;
			background-size: 0.85em;
			padding-right: 1.5em;
		}

		.form-repeat-until {
			align-items: center;
			display: flex;
			gap: 0.3em;

			&[hidden] {
				display: none;
			}
		}

		/* The default — this item only — is the safe one, so it needs no shouting. */
		/* What happens instead, when the rule has been changed and the scope
		   choice above no longer has anything to decide. */
		.form-repeat-note {
			color: var(--color-text-subtle);
			font-size: 0.78rem;
			line-height: 1.45;
			margin: 0;

			&[hidden] {
				display: none;
			}
		}
	}

	.color-select-wrapper {
		display: flex;
		flex-wrap: wrap;
		gap: 0.6em;
		padding: 0.25em 0;
		width: 100%;

		.color-option {
			cursor: pointer;
			position: relative;
			margin: 0;
			padding: 0;

			input[type="radio"] {
				position: absolute;
				opacity: 0;
				width: 0;
				height: 0;
				margin: 0;
			}

			.color-swatch {
				display: block;
				width: 2.5em;
				height: 2.5em;
				border-radius: var(--radius-sm);
				transition: all 0.15s ease;
			}

			/* "Match column" on the item form: the column's colour exactly, since
			   that is what the item would be. The dashed inner ring says it is
			   borrowed rather than picked — the shades beside it are solid. */
			.color-swatch--inherit {
				background-color: var(--column-accent);
				border: 2px dashed var(--color-surface);
			}

			input[type="radio"]:checked + .color-swatch {
				box-shadow:
					0 0 0 2px var(--color-surface),
					0 0 0 4px var(--color-text);
				transform: scale(1.05);
			}

			&:hover .color-swatch {
				transform: scale(1.05);
			}
		}
	}

	/* Ten stickers counting "none", so five to a row is two even rows. Capped
	   against the container too, so the strip can never come out wider than
	   whatever it opens inside. */
	.sticker-select-wrapper {
		display: grid;
		/* Five fixed tracks rather than auto-fill against a percentage: the panel
		   is sized by what it holds now, and auto-fill inside a max-content box
		   has nothing to fill. Nine stickers make two rows, and the way out gets
		   a third of its own. */
		grid-template-columns: repeat(5, 2.75em);
		gap: 0.25em;

		.sticker-option {
			align-items: center;
			border-radius: var(--radius-sm);
			cursor: pointer;
			display: flex;
			justify-content: center;
			margin: 0;
			padding: 0;
			position: relative;
			transition:
				background-color 0.15s ease,
				box-shadow 0.15s ease;

			img {
				display: block;
				height: 2.75em;
				padding: 0.2em;
				transition: transform 0.15s ease;
				width: 2.75em;
			}

			input[type="radio"] {
				height: 0;
				margin: 0;
				opacity: 0;
				position: absolute;
				width: 0;
			}

			/* A ring in the modal's own accent, not a grey fill: grey reads as
			   pressed or unavailable, and every other selected thing in this form
			   is marked with the accent. */
			&:has(input:checked) {
				box-shadow: inset 0 0 0 2px var(--form-accent, var(--color-accent));

				img {
					transform: scale(1.06);
				}
			}

			&:hover {
				background-color: var(--color-surface-alt);
			}

			&:has(input:focus-visible) {
				outline: 2px solid var(--form-accent, var(--color-accent));
				outline-offset: 1px;
			}
		}

		/* Taking the sticker off is not a sticker, so it is a labelled row under
		   the grid rather than an unlabelled dash in the first square of it. */
		.sticker-option--none {
			border-top: 1px solid var(--color-surface-alt);
			border-radius: 0;
			color: var(--color-text-muted);
			font-size: 0.8rem;
			font-weight: 500;
			gap: 0.45em;
			grid-column: 1 / -1;
			justify-content: flex-start;
			letter-spacing: -0.01em;
			margin-top: 0.25em;
			padding: 0.5em 0.55em 0.3em;
			text-transform: none;

			&:has(input:checked) {
				box-shadow: none;
				color: var(--color-text);
				font-weight: 600;
			}

			.sticker-none-mark svg {
				display: block;
				height: 0.95rem;
				width: 0.95rem;
			}
		}
	}
}

/* The shell every menu in the item modal's top row shares: the colour chip's
   ramp, the sticker grid and the column list. Each one hangs off its own chip
   rather than off the row, so it reads as belonging to the button that opened
   it — and sizes itself to what it holds, so nothing floats in dead margins.

   No percentage widths in here. A panel sized against a max-content chip is
   circular, which is why these used to be pinned to the row instead; the fixed
   ceiling and the controller's nudge back inside the form do that job now. */
.form-popover {
	/* A hairline as well as the lift: the modal is the same white the panel is,
	   so the shadow alone leaves the edge — and the caret entirely — to be
	   guessed at. */
	--popover-edge: color-mix(in srgb, var(--color-text-subtle) 22%, transparent);

	background-color: var(--color-surface);
	border: 1px solid var(--popover-edge);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-lift);
	left: 0;
	margin: 0.5em 0 0;
	max-width: min(20em, 74vw);
	padding: 0.4em;
	position: absolute;
	top: 100%;
	transform: translateX(var(--popover-shift, 0px));
	width: max-content;
	z-index: 10;

	/* Points back at the chip, wherever the panel had to move to stay inside
	   the form. A rotated square rather than a bordered triangle, so it can
	   carry the panel's own edge on the two sides that show. */
	&::after {
		background-color: var(--color-surface);
		border-left: 1px solid var(--popover-edge);
		border-top: 1px solid var(--popover-edge);
		content: "";
		height: 0.6em;
		left: var(--popover-caret, 1em);
		position: absolute;
		top: -0.31em;
		transform: translateX(-50%) rotate(45deg);
		width: 0.6em;
	}

	&[hidden] {
		display: none;
	}
}

.form-popover--above {
	bottom: 100%;
	margin: 0 0 0.5em;
	top: auto;

	&::after {
		border: 0 solid var(--popover-edge);
		border-bottom-width: 1px;
		border-right-width: 1px;
		top: auto;
		bottom: -0.31em;
	}
}

/* The shade chip and its strip of tiles. A dot would only report a colour; a
   framed rounded square is the shape a colour control has everywhere else, at a
   size you can actually judge a shade at.

   Lifted out of the item modal's column row because a calendar picks a shade
   too: everything here is a CSS expression over --column-accent, so whatever
   sets that one property gets a working picker with no colour maths of its own.
   --form-accent is the item modal's readable variant of the shade in force; a
   modal that does not set one falls back to the app accent. */
.form-shade {
	flex-shrink: 0;
	position: relative;
}

.form-shade-toggle {
	background: none;
	border: none;
	border-radius: var(--radius-sm);
	cursor: pointer;
	display: flex;
	/* Padding grows the tap target; the negative pull keeps the row the
	   height it was and the chip flush with the start of the row. */
	padding: 0.35em;
	margin: -0.35em 0 -0.35em -0.35em;

	&:hover .form-shade-swatch {
		box-shadow:
			inset 0 0 0 1px rgba(42, 39, 48, 0.22),
			0 0 0 2px var(--color-surface),
			0 0 0 3px var(--form-accent, var(--color-accent));
	}

	&:focus-visible {
		outline: 2px solid var(--form-accent, var(--color-accent));
		outline-offset: 1px;
	}
}

.form-shade-swatch {
	background-color: var(--form-accent, var(--color-accent));
	border-radius: var(--radius-sm);
	display: block;
	flex-shrink: 0;
	/* Whole pixels at the default 16px root: a fractional box lands the
	   edge between two pixels, which is its own kind of blur.

	   In rem rather than em: this sits inside a button, and a button takes the
	   UA's own font rather than the page's, so an em here is a sixth smaller
	   than an em anywhere else — and the sticker chip beside it, sized the same
	   way in em, came out a different size for the same number. */
	height: var(--chip-size);
	width: var(--chip-size);
	transition:
		background-color 0.15s ease,
		box-shadow 0.15s ease;
	/* The frame is the affordance, and it has to be a line rather than a
	   haze — one inset hairline, no spread and no blur radius, so the
	   edge stays exactly where the box is. */
	box-shadow: inset 0 0 0 1px rgba(42, 39, 48, 0.22);
}

.form-shade-list {
	display: flex;
	/* Each row carries its own padding, so a gap on top of it would only
	   push the strip wider — and at 320px the modal leaves 214px. Wrapping
	   stays as a safety net, but the ramp now holds one line everywhere,
	   which is the whole point of ordering it. */
	flex-wrap: wrap;
	list-style: none;
	padding: 0.5em;
}

/* A solid block of colour carries far more weight than a line icon of the
   same size, so these sit well below the sticker picker's 2.75em. The row
   around the tile keeps the tap target near 40px regardless. */
.form-shade-option {
	cursor: pointer;
	display: flex;
	padding: 0.35em;
	position: relative;

	/* The rest of the strip is one ramp, lightest to darkest. The default
	   is not a step on it, so it sits on the other side of a rule rather
	   than breaking the run of tones. */
	&:first-child {
		/* Wide enough that the selected tile's 3px ring never crowds the
		   rule — at 0.35em the ring sat closer to it than the next tile
		   did, and the rule read as off-centre even though it wasn't. */
		margin-right: 0.75em;

		&::after {
			background-color: var(--color-surface-alt);
			bottom: 0.35em;
			content: "";
			/* Measured from this row's right edge rather than as a negative
			   inset, so the offset is a plain half-of-the-margin. A hairline
			   wants whole pixels more than it wants the exact midpoint: this
			   lands it on 6–7px of a 12px gap, half a pixel right of centre
			   and crisp, rather than straddling 5.5–6.5px and blurring. */
			left: 100%;
			position: absolute;
			top: 0.35em;
			transform: translateX(0.375em);
			width: 1px;
		}
	}

	&:hover .form-shade-tile,
	&[data-active="true"] .form-shade-tile {
		box-shadow:
			inset 0 0 0 1px rgba(42, 39, 48, 0.22),
			0 0 0 2px var(--color-surface),
			0 0 0 3px var(--color-text-subtle);
	}

	/* Last, so the shade in force stays marked while you move over the
	   others. A ring rather than a tick: it reads the same on every fill. */
	&[aria-selected="true"] .form-shade-tile {
		box-shadow:
			inset 0 0 0 1px rgba(42, 39, 48, 0.22),
			0 0 0 2px var(--color-surface),
			0 0 0 3px var(--color-text);
	}
}

.form-shade-tile {
	border-radius: var(--radius-sm);
	box-shadow: inset 0 0 0 1px rgba(42, 39, 48, 0.22);
	display: block;
	height: 1.75em;
	transition: box-shadow 0.15s ease;
	width: 1.75em;
}

.form-hint {
	color: var(--color-text-subtle);
	font-size: 0.78rem;
	line-height: 1.5;
	margin: -0.5em 0 0;
	/* A measure, for the same reason .form-prose has one: the dialog is
	   shrink-to-fit, so without a cap a paragraph is what decides how wide the
	   modal is, and one with a sentence of explanation in it ends up half again
	   the width of one without. */
	max-width: 45ch;
}

/* A confirmation's opening line is the thing being asked, not an aside about a
   field, so it reads at body size in the body colour rather than in the hint's
   small subtle type.

   The width is the container's job, not this paragraph's: see
   .form-container--confirm. */
.form-prose {
	color: var(--color-text);
	font-size: 0.9rem;
	line-height: 1.55;
	margin: 0;

	strong {
		font-weight: 600;
	}
}

/* A choice between two courses of action, each of which needs a line of
   explanation — too much for a select, and the wrong shape for the quiet
   inline rows elsewhere. */
.form-choices {
	border: none;
	display: flex;
	flex-direction: column;
	gap: 0.9em;
	margin: 0;
	min-width: 0;
	padding: 0;
}

.form-choice {
	column-gap: 0.6em;
	cursor: pointer;
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	row-gap: 0.15em;

	input[type="radio"] {
		accent-color: var(--color-accent);
		/* Aligned to the label's line rather than the top of the block, so it
		   stays level with the words it belongs to. */
		align-self: center;
		height: 1em;
		margin: 0;
		width: 1em;
	}

	&:has(input:checked) .form-choice-label {
		color: var(--color-text);
	}
}

.form-choice-label {
	color: var(--color-text-muted);
	font-size: 0.9rem;
	font-weight: 600;
	letter-spacing: -0.01em;
}

.form-choice-hint {
	color: var(--color-text-subtle);
	font-size: 0.78rem;
	grid-column: 2;
	line-height: 1.5;
}

/* A fact about the item rather than a control — who owns it, where it came
   from — so it gets a line of its own above the buttons instead of competing
   with them for the actions row. */
.form-note {
	align-items: flex-start;
	background: var(--color-surface-alt);
	border-radius: var(--radius-md);
	color: var(--color-text-muted);
	display: flex;
	font-size: 0.78rem;
	gap: 0.55em;
	line-height: 1.45;
	margin: 0;
	padding: 0.65em 0.85em;

	svg {
		flex-shrink: 0;
		height: 1.15em;
		/* Optically level with the first line rather than its box. */
		margin-top: 0.1em;
		width: 1.15em;
	}

	strong {
		color: var(--color-text);
		font-weight: 600;
	}
}

/* Being unable to change something is worth more than a grey aside. */
.form-note--locked {
	background: var(--color-accent-tint);
	color: var(--color-accent);

	strong {
		color: inherit;
	}
}

/* Sits between the form and the actions, which carry the modal's inset in
   their own padding.

   Zero width, then filled back out: a modal is shrink-to-fit, so anything with
   a max-content width of its own gets a vote on how wide it comes out — and a
   sentence about where an item came from would win that vote, leaving imported
   items in a wider modal than everything else. At width: 0 the note abstains,
   and min-width lays it back across whatever the rest of the form settled on,
   which is what it should be wrapping to. */
.form-container > .form-note {
	/* In rem, not em: the inset has to match the 1.5em the form and the actions
	   take at the modal's own font size, and an em here would be measured
	   against this note's smaller one — three pixels adrift of the fields. */
	margin: 0 1.5rem 1rem;
	min-width: calc(100% - 3rem);
	width: 0;
}

.form-container > .form-note + .form-note {
	margin-top: -0.5em;
}

.form-actions {
	align-items: center;
	display: flex;
	justify-content: space-between;

	.form-actions-right {
		display: flex;
		align-items: center;
		gap: var(--space-sm);
		margin-left: auto;
	}

	a {
		color: var(--color-text-muted);
		font-size: 0.9rem;
		font-weight: 500;
		text-decoration: none;
		padding: 0.6em 0.85em;

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

.form-container > .form-actions {
	padding: 0 1.5em 1.5em;
}

/* A confirmation is a modal with no form in it, so nothing else is holding the
   inset the fields would have had. */
.form-container > .form-hint {
	margin: 0;
	padding: 1.5em 1.5em 1.25em;
}

.form-container form:not(.button_to) .form-actions {
	padding-top: 0.25em;
	margin-top: 0.25em;
	justify-content: flex-end;
}

.form-submit,
input[type="submit"] {
	appearance: none;
	border: none;
	border-radius: var(--radius-pill);
	background: var(--gradient-accent);
	color: #fff;
	cursor: pointer;
	font-family: "Hanken Grotesk", sans-serif;
	font-feature-settings: "c2sc", "smcp";
	font-size: 0.9rem;
	font-weight: 600;
	letter-spacing: -0.0125em;
	padding: 0.6em 1.4em;
	transition: transform 0.15s ease;

	&:hover {
		transform: translateY(-1px);
	}
}

/* Built the way the accented modal builds its submit, so a destructive confirm
   is the same button in the colour the app already uses for deleting. Named
   twice because the base rule reaches these through input[type="submit"], which
   a lone class would lose to. */
.form-submit--destroy,
input[type="submit"].form-submit--destroy {
	background: linear-gradient(
		135deg,
		var(--color-destroy) 0%,
		color-mix(in srgb, var(--color-destroy) 70%, var(--color-text)) 100%
	);
}

/* The doors in ------------------------------------------------------------
 *
 * The waiting list on the home page, signing in, and setting up from the launch
 * email are the same act seen three times, so they get one field treatment
 * rather than three that nearly match. Kept apart from the .form-container
 * rules above, which dress the modals inside the app: those sit on a modal's
 * white and read as part of a tool, while these sit on the page and are the
 * only thing on it.
 *
 * The card around them is still each page's own — tinted on the public pages,
 * white and lifted on the sign-in ones. It is the fields and the button that
 * line up. */

.entry-form {
	display: flex;
	flex-direction: column;
	gap: 0.9em;
	width: 100%;

	input[type="text"],
	input[type="email"] {
		background-color: var(--color-surface);
		border: 1px solid color-mix(in srgb, var(--color-text-subtle) 35%, transparent);
		border-radius: var(--radius-pill);
		color: var(--color-text);
		font-family: "Hanken Grotesk", sans-serif;
		font-size: 0.95rem;
		/* Full width, so every field and the button below them share one right
		   edge — the thing that made these three look like three designs was
		   fields of three different widths. */
		min-width: 0;
		padding: 0.7em 1.1em;
		width: 100%;

		&::placeholder {
			color: var(--color-text-subtle);
		}

		&:focus-visible {
			border-color: var(--color-accent);
			outline: 2px solid var(--color-accent-tint);
			outline-offset: 1px;
		}
	}

	/* The end of the form, and read as such: the fields run down the left, and
	   the one thing left to do sits at the end of the last line. */
	input[type="submit"] {
		align-self: flex-end;
		appearance: none;
		background: var(--gradient-accent);
		border: none;
		border-radius: var(--radius-pill);
		color: #fff;
		cursor: pointer;
		font-family: "Hanken Grotesk", sans-serif;
		font-size: 0.95rem;
		font-weight: 600;
		letter-spacing: -0.01em;
		padding: 0.7em 1.3em;
		transition: transform 0.15s ease;

		&:hover {
			transform: translateY(-1px);
		}
	}
}

/* A label and its field. Sentence case in the muted colour, not the small caps
   the modals use: these are questions being asked of somebody who has never
   seen the app, not the labels on a tool they know. */
.entry-field {
	display: flex;
	flex-direction: column;
	gap: 0.35em;

	label {
		color: var(--color-text-muted);
		font-size: 0.85rem;
	}
}

/* Enough of a container to read as the thing to do next, and no more: a wash
   of the accent, a hairline, no shadow. The page is still type. */
.entry-card {
	background: color-mix(in srgb, var(--color-accent) 5%, var(--color-surface));
	border: 1px solid color-mix(in srgb, var(--color-accent) 18%, transparent);
	border-radius: var(--radius-lg);
	display: flex;
	flex-direction: column;
	gap: 0.9em;
	max-width: 30rem;
	padding: var(--space-lg);
}

.entry-pitch {
	color: var(--color-text-muted);
	font-size: 0.95rem;
	line-height: 1.6;
	margin: 0;
	text-wrap: pretty;
}

.entry-card--done .entry-pitch {
	color: var(--color-accent);
	font-weight: 600;
	/* The box keeps its shape when the form goes, so nothing jumps. */
	margin: 0.3em 0;
}

.entry-email {
	color: var(--color-text);
	font-weight: 600;
	/* A long address breaks rather than pushing the box past the screen. */
	overflow-wrap: anywhere;
}

/* Six characters, spaced out and centred: on the verify page this one field is
   the whole screen, so it is the exception to the shared treatment above. */
.code-input {
	font-size: 1.5em;
	font-weight: 600;
	letter-spacing: 0.5em;
	/* The tracking is added to the right of the last character, so a centred
	   string sits half a space left of centre without this. */
	text-indent: 0.5em;
	text-align: center;
}

.entry-note {
	color: var(--color-text-subtle);
	font-size: 0.85rem;
	margin: 0;
}

.dev-mode {
	background: var(--color-surface-alt);
	border-radius: var(--radius-md);
	padding: var(--space-md) var(--space-lg);
	font-size: 0.875rem;
	color: var(--color-text-muted);
}

/* The sticker chip. Same shape and size as the colour chip it sits beside —
   both say what kind of thing this is rather than what it contains — but this
   one shows the artwork itself, so there is no second copy of it to keep in
   step: the picked option's own image moves up onto the chip. */
.form-sticker {
	flex-shrink: 0;
}

.form-sticker-toggle {
	align-items: center;
	background: none;
	border: none;
	border-radius: var(--radius-sm);
	color: var(--color-text-subtle);
	cursor: pointer;
	display: flex;
	justify-content: center;
	/* Padding grows the tap target; the negative pull keeps the row the height
	   it was, exactly as the colour chip does. */
	margin: -0.35em 0;
	padding: 0.35em;

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

	&:focus-visible {
		outline: 2px solid var(--form-accent, var(--color-accent));
		outline-offset: 1px;
	}
}

/* An empty chip is an offer, not a value: quiet until something is on it. The
   same size as the colour chip beside it — they are the same kind of control,
   one saying what colour and the other what sticker. */
.form-sticker-preview {
	align-items: center;
	display: flex;
	height: var(--chip-size);
	justify-content: center;
	width: var(--chip-size);

	img {
		display: block;
		height: 100%;
		width: 100%;
	}

	svg {
		display: block;
		height: 0.95rem;
		width: 0.95rem;
	}
}

