/*
 * Forms — the front-end.
 *
 * A small, opinionated design that sits inside any theme without fighting it.
 *
 * ## The type scale, and why it is not one number
 *
 * The brief is 14px on desktop, and that is what `--font` is. But form
 * CONTROLS are pinned to 16px below the tablet breakpoint, because iOS Safari
 * zooms the whole page when a focused input is under 16px — the field jumps, the
 * layout reflows, and the visitor has to pinch back out to reach the next
 * question. It is not a preference; it is the single most common mobile form
 * defect, and the fix is one media query.
 *
 * Sizes are `em` off `--font` rather than `rem`, so the whole form scales
 * from that one value and a theme can set it once to change everything.
 *
 * ## Colour
 *
 * Declared, not inherited — but every value is a custom property, so a theme
 * that wants its own accent sets `--accent` and is done. Surfaces and
 * borders are built with `color-mix` against the inherited text colour, which
 * means the form takes on the theme's ink and follows it into dark mode without
 * a second palette.
 *
 * ## Layout
 *
 * Container queries, not viewport ones. The same form is dropped into a
 * full-width page and a narrow sidebar, and a two-column layout decided by the
 * WINDOW puts two columns in 300px in the second case.
 *
 * ## Weight
 *
 * Almost nothing here is filled. A form is a column of boxes, and giving each
 * of them a background and a firm border produces a page of grey slabs that
 * reads as heavy however carefully it is spaced — so separation is done with
 * whitespace, and a hairline is added only where whitespace alone is ambiguous:
 * around a repeating instance, where two identical sets of questions sit next
 * to each other. The one fill left is the total, because that is the number
 * somebody is about to be charged and it should not look like another question.
 *
 * ## This is the DEFAULT, not the last word
 *
 * Each form carries a stylesheet of its own, written in the builder and scoped
 * to that form. So the selectors here are kept at a specificity an author can
 * beat — the control block is wrapped in `:where()` for exactly that reason,
 * and it is explained where it happens. Anything a form is likely to want
 * changed is a custom property, so most of that CSS is three lines under
 * `:root`.
 */

.form-wrap {
	/* Type */
	--font: 14px;
	--line-height: 1.55;

	/* Rhythm */
	--gap: 1.2em;
	--pad: 0.72em;

	/*
	 * Corners — one scale, so nothing is rounded by accident. Controls take the
	 * middle value, which is the one somebody means by "rounded inputs"; the
	 * large one is for the frames around a repeating instance, and it is a step
	 * up rather than a different idea.
	 */
	--radius: 12px;
	--radius-sm: 9px;
	--radius-lg: 16px;

	/*
	 * Colour, DECLARED rather than mixed from the theme's ink.
	 *
	 * This began as the Donations form's own stylesheet — one form, opting out
	 * of the default — and it is the default now because the reasoning turned
	 * out to be true of every form this plugin draws, not just that one. A form
	 * is where somebody types their name, their email and their card details.
	 * It should read the same on every theme it is dropped into, in either
	 * colour scheme, rather than inheriting whatever the page around it happens
	 * to be.
	 *
	 * NO GREY. Every surface is white, and what separates one thing from the
	 * next is a hairline and space. The mixed-from-ink palette this replaced
	 * gave each box a wash of the surrounding colour, and a column of them read
	 * as grey slabs however carefully it was spaced.
	 *
	 * ## Which means forms no longer follow a dark theme
	 *
	 * There was a `prefers-color-scheme: dark` block here that moved these
	 * values. It is gone deliberately rather than by omission, and it is the one
	 * thing to know before adopting this: a form on a dark page is now a light
	 * card on a dark page. That is a deliberate object rather than an accident —
	 * the same decision a payment provider's hosted checkout makes — but a site
	 * that wants the old behaviour restores it by redefining these tokens, per
	 * form under Forms → the form's own stylesheet, or site-wide in the theme.
	 */
	color: #101828;

	--ink: #101828;
	--muted: #667085;

	--line: #e9edf3;
	--line-strong: #d7dee9;

	--surface: #fff;
	--surface-sunk: #fff;
	--field-bg: #fff;

	--accent: #2563eb;
	--accent-ink: #fff;
	--accent-soft: #eff4ff;
	--accent-wash: #f5f8ff;
	--accent-line: #dbe5fb;
	--accent-hover: #b9c5d8;

	--error: #d3363d;
	--ok: #1a7f45;

	font-size: var(--font);
	line-height: var(--line-height);
	container-type: inline-size;
	max-width: 100%;
}

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

/*
 * `hidden` means hidden.
 *
 * The attribute is honoured by a UA rule of the lowest possible specificity, so
 * ANY `display` declaration on the same element silently defeats it — which is
 * how an error message styled `display: flex` renders its empty badge under
 * every field on the form, and how the Add button reappears at its own ceiling.
 * The runtime toggles `hidden` on half a dozen elements here; this is what makes
 * that mean what it says.
 */
.form-wrap [hidden] {
	display: none !important;
}

/*
 * …EXCEPT the amount box, when there is no JavaScript to open it again.
 *
 * The box is drawn `hidden` because with a script running the suggested amounts
 * are the control and a field repeating the chosen figure is noise. Without one
 * the buttons do nothing, and the box is the only way to answer at all — so it
 * has to come back, and it has to come back before the first paint rather than
 * after it.
 *
 * `html:not(.forms-js)` is what tells the two apart: the runtime marks the
 * document the instant it parses, ahead of the form below it, so a scripted page
 * never paints this rule and an unscripted one never stops.
 *
 * `!important` because it is undoing an `!important`, and the rule above is
 * itself defending against exactly this kind of specificity accident.
 */
html:not(.forms-js) .form-wrap .payment__box[hidden] {
	display: flex !important;
}

.form__title {
	margin: 0 0 var(--gap);
	font-size: 1.6em;
	line-height: 1.25;
	letter-spacing: -0.01em;
}

/*
 * The three stacking contexts, and they must agree.
 *
 * All of them become a grid inside the container query below. Stated here as a
 * flex column so that BELOW that width they still have a gap — an earlier
 * version set the gap only in the query, so on a phone every field inside a
 * repeat instance sat flush against the label of the next one.
 */
.form__body,
.section__body,
.repeat__item-body {
	display: flex;
	flex-direction: column;
	gap: var(--gap);
}

/* -- Fields ---------------------------------------------------------------- */

.field {
	display: flex;
	flex-direction: column;
	gap: 0.35em;
	min-width: 0;
}

.field__label {
	font-weight: 600;
	font-size: 0.95em;
	line-height: 1.35;
	letter-spacing: 0.005em;
}

.field__required {
	color: var(--error);
	font-weight: 400;
}

.field__hint {
	margin: 0;
	font-size: 0.85em;
	line-height: 1.45;
	color: var(--muted);
}

.field__error {
	display: flex;
	align-items: flex-start;
	gap: 0.35em;
	margin: 0;
	font-size: 0.85em;
	color: var(--error);
}

.field__error::before {
	content: '!';
	flex: none;
	width: 1.35em;
	height: 1.35em;
	border-radius: 999px;
	background: var(--error);
	color: #fff;
	font-size: 0.8em;
	font-weight: 700;
	line-height: 1.35em;
	text-align: center;
}

/* -- Controls -------------------------------------------------------------- */

/*
 * ## Why every control selector below is wrapped in `:where()`
 *
 * A form carries its own stylesheet, written in the builder and scoped to that
 * form — see `Css`. Which means these rules are not the last word about how a
 * control looks; they are the DEFAULT, and something more specific is meant to
 * be able to beat them.
 *
 * `.form input:not([type='checkbox']):not([type='radio']):not([type='file'])`
 * has a specificity of (0,4,1) — the three `:not()`s each count. An author who
 * scopes `input { border: 0 }` to their form gets (0,1,1) and loses, with
 * nothing on screen to say why, and no amount of re-reading their own CSS will
 * explain it. `:where()` contributes ZERO, so the whole of this block sits at
 * the specificity of the one element name inside it and any rule an author
 * writes about a control wins.
 *
 * That is the entire reason. It is not a style; it is what makes the CSS box in
 * the builder a box that works.
 */
:where(.form input:not([type='checkbox']):not([type='radio']):not([type='file'])),
:where(.form select),
:where(.form textarea) {
	width: 100%;
	max-width: 100%;
	font: inherit;
	font-size: 1em;
	color: inherit;
	background: var(--field-bg);
	border: 1px solid var(--line-strong);
	border-radius: var(--radius);
	padding: var(--pad) 0.9em;
	/* 44px at 14px base — the smallest target a finger reliably hits. */
	min-height: 3.15em;
	appearance: none;
	transition: border-color 0.14s ease, box-shadow 0.14s ease, background-color 0.14s ease;
}

:where(.form textarea) {
	min-height: 6em;
	resize: vertical;
	line-height: var(--line-height);
	/* A textarea is a block of text, not a single line: a pill-shaped one
	   pinches the first and last words in. */
	border-radius: var(--radius-sm);
}

:where(.form input, .form textarea)::placeholder {
	color: var(--muted);
	opacity: 1;
}

:where(.form input, .form select, .form textarea):hover:not(:disabled) {
	border-color: color-mix(in srgb, var(--accent) 38%, var(--line-strong));
	background: color-mix(in srgb, currentColor 3.5%, transparent);
}

/*
 * One focus treatment for everything, and it is a RING rather than a border
 * change — a border that thickens on focus shifts the control by a pixel and
 * nudges the whole column.
 */
:where(
	.form input,
	.form select,
	.form textarea,
	.form button,
	.form [type='checkbox'],
	.form [type='radio']
):focus-visible {
	outline: none;
	border-color: var(--accent);
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}

/* The native chevron goes with `appearance: none`, so here is one that scales. */
:where(.form select) {
	background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
		linear-gradient(135deg, currentColor 50%, transparent 50%);
	background-position: calc(100% - 1.25em) 55%, calc(100% - 0.9em) 55%;
	background-size: 0.38em 0.38em, 0.38em 0.38em;
	background-repeat: no-repeat;
	padding-inline-end: 2.5em;
	cursor: pointer;
}

:where(.form input[type='file']) {
	width: 100%;
	font: inherit;
	font-size: 0.92em;
	padding: 0.8em 0.9em;
	border: 1px dashed var(--line-strong);
	border-radius: var(--radius);
	background: none;
	cursor: pointer;
}

:where(.form input, .form select, .form textarea):disabled {
	opacity: 0.55;
	cursor: not-allowed;
}

.field--invalid input,
.field--invalid select,
.field--invalid textarea,
.field--invalid .choices {
	border-color: var(--error);
}

.field--invalid input:focus-visible,
.field--invalid select:focus-visible,
.field--invalid textarea:focus-visible {
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--error) 25%, transparent);
}

/* -- Choices --------------------------------------------------------------- */

.choices {
	display: flex;
	flex-direction: column;
	gap: 0.15em;
	border: 1px solid transparent;
	border-radius: var(--radius-sm);
}

/*
 * Each choice is its own tappable row rather than a bare box beside a word.
 * A 14px label with a 13px checkbox is a 13px target; the whole row is 44.
 */
.check {
	display: flex;
	align-items: flex-start;
	gap: 0.65em;
	padding: 0.55em 0.7em;
	margin: 0;
	font-weight: 400;
	line-height: 1.45;
	border: 1px solid transparent;
	border-radius: var(--radius-sm);
	cursor: pointer;
	transition: background-color 0.12s ease, border-color 0.12s ease;
	min-height: 2.9em;
}

.check:hover {
	background: var(--surface);
}

.check:has(input:checked) {
	background: color-mix(in srgb, var(--accent) 8%, transparent);
	border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}

.form .check input[type='checkbox'],
.form .check input[type='radio'] {
	flex: none;
	width: 1.15em;
	height: 1.15em;
	/* Aligned to the first LINE of a label that wraps, not to the block. */
	margin: 0.2em 0 0;
	accent-color: var(--accent);
	cursor: pointer;
}

/*
 * A lone yes/no gets a frame, because with no siblings there is nothing to say
 * where the question starts and the one above it ended. A hairline does that;
 * a fill as well would make one checkbox the heaviest thing on the form.
 */
.check--single {
	border: 1px solid var(--line);
	background: none;
}

/*
 * A choice's HINT — a line under its label for what it means: "Deposit — $100
 * to keep the place". Smaller and quieter than the label, which is the thing
 * being chosen.
 */
.check__text {
	display: flex;
	flex-direction: column;
	gap: 0.1em;
}

.check__hint {
	font-size: 0.88em;
	color: var(--muted);
}

/*
 * BOXES — a radio question drawn as a row of rounded tiles rather than a list:
 * the label bold, the hint under it, the chosen one filled. The look of the
 * amount buttons (`.payment__amount`), and for the same reason: a small set of
 * real alternatives reads better side by side than stacked.
 *
 * Still radio buttons underneath. The circle is put out of sight rather than
 * removed, so the arrow keys, `required`, and a screen reader all work exactly
 * as they do on the list.
 *
 * Every selector here starts `.form .choices.choices--boxes`, for weight: a
 * form with a stylesheet of its own carries a frozen copy of the `.check`
 * rules above, scoped under the form, and they must not outrank these.
 */
.form .choices.choices--boxes {
	flex-direction: row;
	flex-wrap: wrap;
	gap: 0.5em;
	border: 0;
}

.form .choices.choices--boxes .check {
	position: relative;
	flex: 1 1 auto;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 0;
	min-width: 5.5em;
	padding: 0.7em 1em;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius);
	background: none;
	text-align: center;
	line-height: 1.25;
}

.form .choices.choices--boxes .check:hover {
	background: var(--surface-sunk);
}

.form .choices.choices--boxes .check:has(input:checked) {
	border-color: var(--accent);
	background: color-mix(in srgb, var(--accent) 12%, transparent);
	box-shadow: inset 0 0 0 1px var(--accent);
}

/* The circle is out of sight, so the box carries the focus ring for it. */
.form .choices.choices--boxes .check:has(input:focus-visible) {
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}

.form .choices.choices--boxes .check:has(input:checked:focus-visible) {
	box-shadow: inset 0 0 0 1px var(--accent), 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}

/* Out of sight, not out of reach — see above. */
.form .choices.choices--boxes .check input[type='radio'] {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: 0;
	opacity: 0;
	pointer-events: none;
}

.form .choices.choices--boxes .check__text {
	align-items: center;
	gap: 0.15em;
}

.form .choices.choices--boxes .check__label {
	font-weight: 600;
	font-variant-numeric: tabular-nums;
}

/* The caption the amount buttons use — `.payment__amount-label`. */
.form .choices.choices--boxes .check__hint {
	font-size: 1em;
	font-weight: 400;
	color: #2ea3f2;
	opacity: 0.95;
}

.form .choices.choices--boxes .check:has(input:checked) .check__hint {
	color: inherit;
	opacity: 0.8;
}

/* -- An age, and the tick that confirms it ---------------------------------- */

/*
 * The number is the loud half, because it is the thing somebody is being asked
 * to check. The yes/no sits beside it rather than under it: the question and
 * its answer read as one line, and a form full of these stays short.
 */
/*
 * A CALCULATION: a figure, and no control at all.
 *
 * Deliberately not styled as an input. There is nothing to type into it and
 * nothing is posted from it — a box would invite somebody to try, and a
 * disabled-looking box would read as a question they are not allowed to
 * answer rather than as an answer the form worked out.
 */
.calc {
	display: block;
	font-size: 1.3em;
	font-weight: 600;
	line-height: 1.4;
	min-height: 1.4em;
}

.calc:empty::before {
	content: attr(data-placeholder);
	font-size: 0.78rem;
	font-weight: 400;
	color: var(--muted, #666);
}

.age {
	display: flex;
	align-items: center;
	gap: 0.75em;
	flex-wrap: wrap;
}

.age__input {
	width: 4.5em;
	flex: 0 0 auto;
	font-size: 1.2em;
	font-weight: 600;
	text-align: center;
	font-variant-numeric: tabular-nums;
}

/*
 * Locked, and it looks it. A read-only box drawn like an editable one invites
 * somebody to type into it and then wonder why nothing happens — the answer is
 * "press No first", and the way to say that is to stop looking like a box.
 */
.age__input:read-only {
	background: var(--tint, rgba(0, 0, 0, 0.04));
	border-color: transparent;
	cursor: default;
}

/* Nothing to work it out from yet. */
.age.is-pending .age__input {
	opacity: 0.5;
}

.age__ask {
	display: flex;
	align-items: center;
	gap: 0.6em;
	flex-wrap: wrap;
}

.age__question {
	color: var(--muted);
}

.age__choice {
	display: inline-flex;
	align-items: center;
	gap: 0.3em;
	cursor: pointer;
}

/*
 * Out of reach until there is a date. A control somebody can press that does
 * nothing is worse than one they can see is waiting for something else.
 */
.age__choice:has( input:disabled ) {
	opacity: 0.5;
	cursor: not-allowed;
}

/* Corrected by hand: the box is theirs now, and says so. */
.age.is-corrected .age__input {
	border-color: var(--accent, #2f6fed);
}

/* -- Money and fees -------------------------------------------------------- */

.money {
	display: flex;
	align-items: center;
	position: relative;
}

.money__symbol {
	position: absolute;
	inset-inline-start: 0.9em;
	color: var(--muted);
	pointer-events: none;
	font-weight: 500;
}

.money:has(.money__symbol) input {
	padding-inline-start: 2.1em;
}

/*
 * A price the AUTHOR set — one line, the figure on the right.
 *
 * Both modes of the Payment question are `.payment`; the modifier says which,
 * and the two layouts have nothing in common, so neither of them styles the
 * bare block. `.payment` on its own is a name for the pair, not a style.
 */
.payment--fixed,
.payment--asked {
	display: flex;
	align-items: center;
	gap: 0.7em;
	margin: 0;
	padding: 0.75em 0.95em;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: none;
}

/* -- The quantity stepper --------------------------------------------------- */

/*
 * A box with a button either side of it.
 *
 * The box is still the answer and is still typeable; the buttons only write
 * into it. Both are 44px, because this is the control somebody uses standing up
 * with one thumb — a native number input's spinners are a few pixels tall, only
 * appear on hover, and on iOS do not appear at all.
 */
.stepper {
	display: inline-flex;
	align-items: stretch;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius-sm);
	background: var(--field-bg);
	overflow: hidden;
}

.stepper__button {
	flex: 0 0 auto;
	width: 44px;
	min-height: 44px;
	display: grid;
	place-items: center;
	padding: 0;
	border: 0;
	background: none;
	color: inherit;
	font: inherit;
	font-size: 1.15em;
	line-height: 1;
	cursor: pointer;
}

.stepper__button:hover:not(:disabled) {
	background: var(--surface-sunk);
}

.stepper__button:disabled {
	opacity: 0.35;
	cursor: default;
}

/*
 * `:focus-visible` on the WRAPPER, not the buttons — they are `tabindex="-1"`
 * on purpose. A keyboard user tabs to the number box and types, or uses the
 * arrow keys the box already answers to; landing on three tab stops to set one
 * quantity is worse than one, and the buttons are a pointer convenience.
 */
.stepper:focus-within {
	outline: 2px solid var(--accent);
	outline-offset: 1px;
	border-color: var(--accent);
}

.stepper__input {
	width: 3.5em;
	min-height: 44px;
	border: 0;
	border-inline: 1px solid var(--line);
	border-radius: 0;
	background: none;
	text-align: center;
	font-variant-numeric: tabular-nums;
	font-weight: 600;
	/* 16px stops iOS zooming the page when the box is focused. */
	font-size: max(16px, 1em);
}

.stepper__input:focus {
	outline: none;
}

/* The native spinners are what this control replaces. */
.stepper__input::-webkit-outer-spin-button,
.stepper__input::-webkit-inner-spin-button {
	appearance: none;
	margin: 0;
}

.stepper__input[type='number'] {
	appearance: textfield;
	-moz-appearance: textfield;
}

.payment__price,
.payment__each {
	color: var(--muted);
	font-variant-numeric: tabular-nums;
	margin-inline-start: auto;
	font-weight: 600;
}

/* -- Choosing an amount ----------------------------------------------------- */

/*
 * An amount the PAYER chooses: suggested figures, and a box.
 *
 * The buttons are affordances rather than answers — every one of them writes
 * into the box below, which is the only input here — so they are styled as a
 * choice rather than as an action: a chosen one is filled the way a ticked
 * `.check` is, and none of them looks like the submit button, which is the one
 * button on this form that sends anything.
 */
.payment--chosen {
	display: flex;
	flex-direction: column;
	gap: 0.7em;
}

.payment__amounts {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5em;
}

/*
 * A LABELLED amount stacks: the figure, and under it what it is for.
 *
 * Only when there is a label — an appeal offering four bare figures gets the
 * single-line button it always had, and nothing about that row changes. The
 * whole row grows to match the tallest button, so a mixed set (three tiers and
 * an "Other") still lines up.
 */
.payment__amount {
	display: inline-flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 0.15em;
	line-height: 1.25;
}

.payment__amount-label {
	/*
	 * Quieter than the figure and not bold, because the amount is what is being
	 * chosen and the label explains it. A caption competing with the number
	 * makes a row of tiers hard to scan for price, which is what somebody is
	 * scanning for.
	 */
	font-size: 0.82em;
	font-weight: 400;
	color: var(--muted);
	opacity: 0.95;
}

/* The chosen one's caption comes with it rather than staying grey against a
   tinted button. */
.payment__amount[aria-pressed='true'] .payment__amount-label {
	color: inherit;
	opacity: 0.8;
}

/*
 * Grow, do not stretch. `1 1 auto` with a floor means four amounts sit in a
 * neat row on a wide form and wrap to two-up in a sidebar, without £5 and
 * £1,000 being drawn the same width — which reads as a price list rather than
 * as a set of choices.
 */
.payment__amount {
	flex: 1 1 auto;
	min-width: 5.5em;
	padding: 0.7em 1em;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius);
	background: none;
	color: inherit;
	font: inherit;
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	cursor: pointer;
	transition: background-color 0.12s ease, border-color 0.12s ease;
}

.payment__amount:hover {
	background: var(--surface-sunk);
}

/*
 * Keyed off `aria-pressed`, not off a class alone.
 *
 * The attribute is what a screen reader announces, so tying the appearance to
 * it makes the two impossible to disagree: a button that looks chosen and reads
 * as un-pressed is the failure this avoids, and it is invisible to whoever is
 * looking at the screen.
 */
.payment__amount[aria-pressed='true'] {
	border-color: var(--accent);
	background: color-mix(in srgb, var(--accent) 12%, transparent);
	box-shadow: inset 0 0 0 1px var(--accent);
}

.payment__box {
	display: flex;
	flex-direction: column;
	gap: 0.35em;
	max-width: 14em;
}

.payment__caption {
	color: var(--muted);
	font-size: 0.92em;
}

/* Nothing but the presets is on offer, so the box shows the choice and takes
   no typing. It is still the input, and still what is submitted. */
.payment__box--locked input {
	background: var(--surface-sunk);
}

/* -- How far along ---------------------------------------------------------- */

/*
 * A goal, drawn above the questions.
 *
 * It is the reason somebody is filling the form in, so it goes first — but it
 * is not a question, and it must not read as one. Hence no border and no fill
 * around the whole thing: a label, two figures, and a bar.
 */
.goals {
	display: flex;
	flex-direction: column;
	gap: 0.9em;
	margin-bottom: 1.6em;
}

.goal__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 0.8em;
	margin-bottom: 0.45em;
}

.goal__label {
	font-weight: 600;
}

.goal__figures {
	font-variant-numeric: tabular-nums;
	/* Pushed to the end even when there is no label beside it, so two goals
	   stacked above each other line their numbers up. */
	margin-inline-start: auto;
}

.goal__done {
	font-size: 1.2em;
}

.goal__of {
	color: var(--muted);
}

/*
 * A tally is a figure, not a measurement, so it is not drawn as one: no track,
 * no empty bar waiting to be filled to a number the form has not got. It only
 * needs the head's own spacing taken back, since nothing follows it.
 */
.goal--tally .goal__head {
	margin-bottom: 0;
}

.goal__track {
	height: 0.6em;
	border-radius: 999px;
	background: var(--surface-sunk);
	overflow: hidden;
}

.goal__fill {
	display: block;
	height: 100%;
	border-radius: inherit;
	background: var(--accent);
	/* The width is set inline from the server's figure. The transition is for
	   the case where it is not: a form that re-renders after a submission
	   without a page load should move rather than jump. */
	transition: width 0.4s ease;
}

.goal--reached .goal__fill {
	background: var(--ok);
}

.goal--reached .goal__done {
	color: var(--ok);
}

/* -- What it comes to, and how it is paid ---------------------------------- */

/*
 * The total is hidden until there IS one. A form whose fees are all behind
 * conditions opens with nothing owed, and a "Total £0.00" above the submit
 * button reads as a price rather than as an absence of one.
 */
.form__total {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1em;
	margin: 1.5em 0 0.6em;
	padding: 0.85em 1.05em;
	/* The one place a fill is earned: this is the number somebody is about to
	   be charged, and it should not look like another question. */
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--surface);
	font-size: 1.05em;
}

.form__total-value {
	font-variant-numeric: tabular-nums;
	font-size: 1.25em;
}

/*
 * What repeats, said in the payer's own reading order.
 *
 * Directly under the amount and before the pay button, because it qualifies
 * that amount: the figure above is what is taken today, and this is the part
 * that is not over when today is.
 */
.form__total-then {
	margin: -0.35em 0 0;
	padding: 0 1.05em;
	color: var(--muted);
	font-variant-numeric: tabular-nums;
	text-align: end;
}

/*
 * A question's REPEAT box sits straight under the question it repeats — a
 * sibling rather than inside it, so it is pulled up into the gap the column
 * leaves between questions and reads as part of the one above, not as the next
 * one down.
 */
.form__repeat {
	display: flex;
	flex-direction: column;
	gap: 0.5em;
	margin: calc(var(--gap) * -0.5) 0 0;
}

/* A box that cannot join the cycle another has already taken. */
.form__repeat-on:has(input:disabled) {
	color: var(--muted);
}

.form__repeat-on {
	display: flex;
	align-items: baseline;
	gap: 0.55em;
}

.form__repeat-every {
	display: flex;
	align-items: center;
	gap: 0.6em;
	/* Indented to the checkbox's text, so it reads as belonging to it rather
	   than as the next question down. */
	padding-inline-start: 1.7em;
}

.form__repeat-every label {
	/* Two words, and they belong on one line: wrapped, they stack beside a
	   full-width control and the pair reads as a question and an answer that
	   have come apart. */
	white-space: nowrap;
}

.form__repeat-every select {
	/* Sized to the longest cycle rather than to the form. It is the narrowest
	   choice on the page and a control the width of the submit button reads as
	   the more important of the two. */
	width: auto;
	min-width: 10em;
}

.form__repeat-note {
	margin: 0;
	padding-inline-start: 1.7em;
	color: var(--muted);
	font-size: 0.92em;
	line-height: 1.5;
}

.form__providers {
	border: 0;
	margin: 0.8em 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 0.6em;
}

.form__providers legend {
	padding: 0;
	margin-bottom: 0.5em;
	font-weight: 600;
}

.form__provider {
	display: flex;
	align-items: center;
	gap: 0.5em;
	padding: 0.7em 0.95em;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: none;
	cursor: pointer;
	flex: 1 1 10em;
}

.form__provider:has(input:checked) {
	border-color: currentColor;
	box-shadow: inset 0 0 0 1px currentColor;
}

/*
 * Each provider's own logo, so the two are told apart at a glance rather than
 * by reading. A provider with no logo gets a plain square the same size.
 */
.form__provider-mark {
	display: block;
	width: 1.25em;
	height: 1.25em;
	border-radius: 3px;
	background: var(--muted);
	flex: none;
}

svg.form__provider-mark {
	background: none;
}

.form__provider-mark--stripe {
	border-radius: 0.3em;
}

.form__consent {
	display: flex;
	align-items: flex-start;
	gap: 0.6em;
	margin: 1em 0;
	line-height: 1.45;
}

.form__consent input {
	margin-top: 0.2em;
	flex: none;
}

.form__receipt {
	margin: 0.6em 0 0;
	font-variant-numeric: tabular-nums;
	opacity: 0.85;
}

/* -- Sections -------------------------------------------------------------- */

.section {
	margin: 0;
	padding: 0;
	border: 0;
	min-width: 0;
}

.section__title {
	padding: 0;
	margin-bottom: 0.15em;
	font-size: 1.15em;
	font-weight: 700;
	letter-spacing: -0.005em;
}

.section__hint {
	margin: 0 0 0.9em;
	font-size: 0.9em;
	color: var(--muted);
}

/* -- Repeating groups ------------------------------------------------------ */

/*
 * The visible boundary matters more here than anywhere else on the form. This
 * is the one place where two identical sets of questions are on screen at once,
 * and without a frame around each there is nothing saying which surname belongs
 * to which child.
 */
.repeat {
	margin: 0;
	padding: 0;
	border: 0;
	min-width: 0;
}

.repeat__title {
	padding: 0;
	margin-bottom: 0.15em;
	font-size: 1.15em;
	font-weight: 700;
	letter-spacing: -0.005em;
}

.repeat__hint {
	margin: 0 0 0.9em;
	font-size: 0.9em;
	color: var(--muted);
}

.repeat__items {
	display: flex;
	flex-direction: column;
	gap: 0.75em;
}

/*
 * A frame, not a slab. What has to be legible is where one child's answers end
 * and the next child's begin, and a hairline with room inside it says that as
 * clearly as a grey fill — while leaving the page white, which is what makes a
 * form of three children look like a form rather than a stack of boxes.
 */
.repeat__item {
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: none;
	padding: 1.05em 1.15em 1.25em;
	container-type: inline-size;
	animation: in 0.18s ease both;
}

/* A new instance arrives visibly, so pressing Add is obviously what did it. */
@keyframes in {
	from {
		opacity: 0;
		transform: translateY(-4px);
	}
}

@media (prefers-reduced-motion: reduce) {
	.repeat__item {
		animation: none;
	}
}

/*
 * No rule under it. The instance already has a border of its own, and a second
 * line 2em inside the first is two frames arguing about where the boundary is.
 */
.repeat__item-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1em;
	margin-bottom: 0.9em;
}

.repeat__item-title {
	display: flex;
	align-items: baseline;
	gap: 0.35em;
	font-weight: 700;
	font-size: 0.95em;
}

.repeat__ordinal {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 1.7em;
	height: 1.7em;
	padding: 0 0.4em;
	border-radius: 999px;
	background: var(--surface-sunk);
	color: var(--muted);
	font-size: 0.8em;
	font-weight: 500;
	font-variant-numeric: tabular-nums;
}

.repeat__remove {
	flex: none;
	font: inherit;
	font-size: 0.82em;
	padding: 0.4em 0.75em;
	border: 1px solid transparent;
	border-radius: 999px;
	background: none;
	color: var(--muted);
	cursor: pointer;
	transition: color 0.12s ease, background-color 0.12s ease, border-color 0.12s ease;
}

.repeat__remove:hover {
	color: var(--error);
	border-color: color-mix(in srgb, var(--error) 30%, transparent);
	background: color-mix(in srgb, var(--error) 8%, transparent);
}

.repeat__actions {
	display: flex;
	align-items: center;
	gap: 0.75em;
	margin-top: 0.75em;
	flex-wrap: wrap;
}

/*
 * The Add button is deliberately NOT a primary button. It is not the form's main
 * action — Submit is — and a group offering something that outranks the button
 * which finishes the form is a group people press by mistake.
 */
.repeat__add {
	display: inline-flex;
	align-items: center;
	gap: 0.45em;
	font: inherit;
	font-size: 0.92em;
	font-weight: 600;
	cursor: pointer;
	padding: 0.6em 1.1em;
	min-height: 2.9em;
	border: 1px dashed var(--line-strong);
	border-radius: 999px;
	background: none;
	color: inherit;
	transition: border-color 0.14s ease, background-color 0.14s ease, color 0.14s ease;
}

.repeat__add::before {
	content: '+';
	font-size: 1.15em;
	line-height: 1;
	font-weight: 400;
}

.repeat__add:hover {
	border-style: solid;
	border-color: var(--accent);
	color: var(--accent);
	background: color-mix(in srgb, var(--accent) 7%, transparent);
}

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

/*
 * Nesting: the inner frame is drawn on a dashed line and rounded one step less,
 * so depth reads as "inside" rather than as two boxes of equal weight touching.
 */
.repeat__item .repeat__item {
	border-style: dashed;
	border-radius: var(--radius);
}

/* -- Layout ---------------------------------------------------------------- */

/*
 * Two columns, and a CONTAINER query rather than a viewport one. Below 34rem
 * everything is one column, which is the right answer on a phone AND in a
 * sidebar — a layout decided by the WINDOW puts two columns in 300px there.
 *
 * The grid was six columns while thirds existed. It is two now because that is
 * what it means: a row holds one question or two.
 */
@container (min-width: 34rem) {
	.form__body,
	.section__body,
	.repeat__item-body {
		display: grid;
		grid-template-columns: repeat(2, 1fr);
		gap: var(--gap);
		align-items: start;
	}

	.form__body > *,
	.section__body > *,
	.repeat__item-body > * {
		grid-column: span 2;
	}

	.field--half {
		grid-column: span 1;
	}
}

/*
 * HIDDEN — on the form, and not drawn. A Calculation is still worked out and a
 * price still counts towards the total; the page just does not show them.
 *
 * Not the `hidden` attribute, which the runtime reads as "not asked" and would
 * stop computing. And `!important`, because a form's own stylesheet is scoped
 * under the form and its `.field { display: flex }` would otherwise outrank
 * this one-class rule and draw the question anyway.
 */
.field--hidden {
	display: none !important;
}

/* -- Messages and actions -------------------------------------------------- */

.form__actions {
	display: flex;
	align-items: center;
	gap: 0.75em;
	margin-top: var(--gap);
	flex-wrap: wrap;
}

.form__submit {
	font: inherit;
	font-size: 1em;
	font-weight: 600;
	letter-spacing: 0.01em;
	padding: 0.85em 1.9em;
	min-height: 3.15em;
	border: 0;
	border-radius: 999px;
	background: var(--accent);
	color: var(--accent-ink);
	cursor: pointer;
	box-shadow: 0 1px 2px color-mix(in srgb, var(--accent) 35%, transparent);
	transition: filter 0.14s ease, transform 0.06s ease, box-shadow 0.14s ease;
}

.form__submit:hover:not(:disabled) {
	filter: brightness(1.08);
}

.form__submit:active:not(:disabled) {
	transform: translateY(1px);
}

.form__submit:disabled {
	opacity: 0.6;
	cursor: progress;
}

.form__messages:empty {
	display: none;
}

.form__messages {
	margin: var(--gap) 0 0;
	padding: 0.8em 1em;
	border-radius: var(--radius);
	background: var(--surface);
	border-inline-start: 3px solid var(--line-strong);
	font-size: 0.92em;
}

.form__messages--error {
	border-inline-start-color: var(--error);
	background: color-mix(in srgb, var(--error) 7%, transparent);
	color: var(--error);
}

.form__messages--success {
	border-inline-start-color: var(--ok);
	background: color-mix(in srgb, var(--ok) 7%, transparent);
}

.form-notice {
	padding: 1em 1.2em;
	border-radius: var(--radius);
	background: var(--surface);
	border-inline-start: 3px solid var(--line-strong);
	font-size: 0.95em;
}

.form-notice--warning {
	border-inline-start-color: #d9a441;
	background: color-mix(in srgb, #d9a441 8%, transparent);
}

.form-notice--success {
	border-inline-start-color: var(--ok);
	background: color-mix(in srgb, var(--ok) 8%, transparent);
}

.form__spinner {
	width: 1.1em;
	height: 1.1em;
	border: 2px solid var(--line);
	border-top-color: var(--accent);
	border-radius: 50%;
	animation: spin 0.7s linear infinite;
}

@keyframes spin {
	to {
		transform: rotate(360deg);
	}
}

@media (prefers-reduced-motion: reduce) {
	.form__spinner {
		animation-duration: 3s;
	}
}

/*
 * The honeypot. Positioned away rather than `display: none`, because a bot that
 * reads styles skips a hidden input and fills a positioned one.
 */
.form__hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.hidden {
	display: none !important;
}

/* -- Mobile ---------------------------------------------------------------- */

/*
 * Below the tablet breakpoint, controls go to 16px.
 *
 * NOT a preference: iOS Safari zooms the page whenever a focused input's font is
 * under 16px, which throws the layout sideways mid-typing and leaves the visitor
 * to pinch back out before they can reach the next question. Everything else
 * stays on the 14px scale; only the things you type into grow.
 */
@media (max-width: 782px) {
	.form-wrap {
		--gap: 1.05em;
	}

	.form input:not([type='checkbox']):not([type='radio']):not([type='file']),
	.form select,
	.form textarea {
		font-size: 16px;
		/* The em-based min-height would grow with the font and make every field
		   taller than it needs to be; 46px is the touch target, stated once. */
		min-height: 46px;
	}

	.repeat__item {
		padding: 0.85em 0.85em 1em;
		border-radius: var(--radius);
	}

	/* Not a zoom problem — a button is never focused for typing — but the same
	   touch target, because a row of amounts is the first thing a thumb hits on
	   a donation form and a near-miss chooses the wrong figure. */
	.payment__amount {
		min-height: 46px;
	}

	/* A full-width primary action is easier to hit with a thumb, and there is
	   nothing to sit beside it on a phone. */
	.form__submit {
		width: 100%;
		min-height: 48px;
	}

	.form__actions {
		gap: 0.5em;
	}
}

@media (max-width: 480px) {
	.repeat__item-head {
		/* The title and the Remove button stop competing for one line. */
		align-items: flex-start;
	}

	.repeat__add {
		width: 100%;
		justify-content: center;
	}
}


/* -- Signing ---------------------------------------------------------------
   A column of four things in the order the law reads them: told, agreed,
   understood, signed. Almost nothing is filled, like the rest of the form —
   the one exception is the drawing surface, which needs an edge because it is
   a place to put something rather than a place to read something. */

.sign {
	display: flex;
	flex-direction: column;
	gap: 0.75em;
}

.sign__intent {
	margin: 0;
	color: var(--muted);
	font-size: 0.92em;
}

.sign__pad {
	position: relative;
}

/* `touch-action: none` is not decoration. Without it a finger dragged across
   the canvas scrolls the page instead of drawing, which on a phone means the
   pad cannot be signed in at all. */
.sign__canvas {
	display: block;
	width: 100%;
	height: 10em;
	border: 1px dashed var(--line-strong);
	border-radius: var(--radius);
	background: var(--surface);
	touch-action: none;
	cursor: crosshair;
}

.sign--drawn .sign__canvas {
	border-style: solid;
	border-color: var(--line-strong);
}

.sign__clear {
	position: absolute;
	top: 0.5em;
	right: 0.5em;
	appearance: none;
	background: none;
	border: 1px solid var(--line);
	border-radius: var(--radius-sm);
	padding: 0.25em 0.7em;
	font: inherit;
	font-size: 0.85em;
	color: var(--muted);
	cursor: pointer;
}

.sign__clear:hover {
	color: inherit;
	border-color: var(--line-strong);
}

/* Signing one way at a time. The typed name is still the answer while the pad
   is open — it is asked for both ways round — but showing it back in a hand
   beneath a drawing is two signatures for one question. */
/* Said only when a question asks for a drawing and nothing else — the one
   arrangement with no usable control behind it without JavaScript. */
.sign__noscript {
	margin: 0;
	padding: 0.6em 0.75em;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius-sm);
	color: var(--muted);
	font-size: 0.92em;
}

/* The name in a hand belongs to a question that is signed BY TYPING — there, it
   is the signature. Beside a drawing it is the same name written twice, so it is
   put away wherever a mark is being made instead. */
.sign:not([data-methods='type']) .sign__preview {
	display: none;
}

/* The typed name, in a hand. A stack rather than one face, because a signature
   that falls back to the body font on a machine without cursive still reads as
   a name written out — which is what it is. */
.sign__preview:not(:empty) {
	margin: 0;
	padding: 0.2em 0.1em 0.3em;
	border-bottom: 1px solid var(--line-strong);
	font-family: 'Segoe Script', 'Bradley Hand', 'Snell Roundhand', 'Apple Chancery', cursive;
	font-size: 1.7em;
	line-height: 1.3;
	/* A very long name must not push the column wider than the form. */
	overflow-wrap: anywhere;
}

/* -- Print ----------------------------------------------------------------- */

@media print {
	.form__actions,
	.repeat__actions,
	.repeat__remove,
	.sign__clear {
		display: none;
	}

	/* A signed form on paper keeps the mark and the wording it was made under,
	   and loses the control that made it. The canvas prints as what is drawn on
	   it, so it keeps its box. */
	.sign__canvas {
		display: block;
	}

	.repeat__item {
		break-inside: avoid;
		border-color: #999;
	}
}

/* -- How a control behaves while it is being used --------------------------- */

/*
 * The other half of the Donations form's look, adopted as the default alongside
 * its palette: white at rest, white on hover, white on focus, with the border
 * and a ring doing the work a tint used to. Nothing on the page ever turns grey
 * under a pointer.
 */

input,
select,
textarea,
.form-wrap .stepper {
	transition:
		border-color 0.18s ease,
		box-shadow 0.18s ease,
		background-color 0.18s ease;
}

.form-wrap input:hover:not(:disabled),
.form-wrap select:hover:not(:disabled),
.form-wrap textarea:hover:not(:disabled),
.form-wrap .stepper:hover:not(:focus-within) {
	background-color: #fff;
	border-color: var(--accent-hover);
}

/*
 * BRIGHT ON FOCUS, and `:focus` rather than `:focus-visible` — this is a form
 * somebody is filling in with a mouse or a thumb, and the field they are typing
 * into should light up whether or not the browser thinks the focus was
 * "visible".
 *
 * A ring rather than a thicker border: a border that grows on focus moves the
 * control by a pixel and nudges the whole column down.
 */
.form-wrap input:focus,
.form-wrap select:focus,
.form-wrap textarea:focus,
.form-wrap .stepper:focus-within {
	outline: none;
	background-color: #fff;
	border-color: var(--accent);
	box-shadow:
		0 0 0 4px rgb(37 99 235 / 0.14),
		0 1px 2px rgb(16 24 40 / 0.05);
}

.form-wrap ::placeholder {
	color: #98a2b3;
}

/* The stepper's box has no border of its own — the wrapper carries the ring. */
.form-wrap .stepper__input:focus,
.form-wrap .stepper__input:hover:not(:disabled) {
	background-color: #fff;
	border-color: var(--line);
	box-shadow: none;
}

.form-wrap .stepper__button {
	color: var(--accent);
	font-weight: 600;
}

.form-wrap .stepper__button:hover:not(:disabled) {
	background-color: var(--accent-wash);
}

/* Disabled says "this end of the range", so it fades rather than greys. */
.form-wrap .stepper__button:disabled {
	color: var(--accent-hover);
	opacity: 1;
}

/* -- Choosing an amount ----------------------------------------------------- */

.form-wrap .payment__amount {
	background-color: #fff;
	border-color: #dde4ee;
	transition:
		border-color 0.18s ease,
		background-color 0.18s ease,
		box-shadow 0.18s ease,
		transform 0.12s ease;
}

/* The lift is one pixel. Anything more and a row of four amounts jitters as the
   pointer crosses it. */
.form-wrap .payment__amount:hover {
	background-color: #fff;
	border-color: #9db6ea;
	box-shadow: 0 1px 3px rgb(16 24 40 / 0.07);
	transform: translateY(-1px);
}

.form-wrap .payment__amount:active {
	transform: translateY(0);
}

.form-wrap .payment__amount[aria-pressed='true'] {
	border-color: var(--accent);
	background: var(--accent-soft);
	box-shadow: inset 0 0 0 1px var(--accent);
	transform: none;
}

/* -- What it comes to ------------------------------------------------------- */

/*
 * The total keeps a fill, because it is the number somebody is about to be
 * charged and it must not look like another question — but a wash of the accent
 * rather than a wash of grey.
 */
.form-wrap .form__total {
	background: var(--accent-wash);
	border-color: var(--accent-line);
}

.form-wrap .form__submit {
	box-shadow:
		0 1px 2px rgb(37 99 235 / 0.24),
		0 6px 16px rgb(37 99 235 / 0.16);
	transition:
		transform 0.16s ease,
		box-shadow 0.2s ease,
		filter 0.18s ease;
}

.form-wrap .form__submit:hover:not(:disabled) {
	filter: brightness(1.04);
	transform: translateY(-1px);
	box-shadow:
		0 2px 4px rgb(37 99 235 / 0.26),
		0 10px 24px rgb(37 99 235 / 0.22);
}

.form-wrap .form__submit:active:not(:disabled) {
	transform: translateY(0);
	box-shadow: 0 1px 2px rgb(37 99 235 / 0.24);
}

/* -- Moving, for the people who want it ------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	.form-wrap .payment__amount,
	.form-wrap .payment__amount:hover,
	.form-wrap .form__submit,
	.form-wrap .form__submit:hover {
		transform: none;
		transition-duration: 0.01ms;
	}
}
