html,
body {
	/* min-height, not height: a fixed height clamps the box to one viewport
	   even when children overflow past it, which starves position: sticky
	   descendants of the scroll room they need to ever engage. */
	min-height: 100%;
	margin: 0;
	padding: 0;
}

main {
	margin: 5em auto;
	/* centers horizontally and adds top margin */
	max-width: 80%;
	/*prevents stretching on wide screens */
}

h1 {
	margin: 0 auto;
	width: fit-content;
	/* ensures it's only as wide as its content */
}

/* Upload stays visually primary, with a compact offer beside the form. */
.upload-offer-layout {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(14rem, .7fr);
	align-items: center;
	gap: clamp(1rem, 3vw, 2rem);
	max-width: 64rem;
	margin: 2.5rem auto 0;
}

.upload-offer-layout > form {
	margin: 0;
}

.upload-offer {
	padding: 1rem 1.25rem;
	border: 1px solid var(--pico-muted-border-color);
	border-radius: .75rem;
	font-size: .95rem;
}

.upload-offer p:last-of-type {
	margin-bottom: .75rem;
}

.pricing-link {
	font-weight: 700;
}

.public-pricing {
	max-width: 64rem;
}

.public-pricing-grid {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 1rem;
	margin-top: 2rem;
}

.public-pricing-card {
	padding: 1.5rem;
	text-align: center;
	border: 1px solid var(--pico-muted-border-color);
	border-radius: .75rem;
}

.public-pricing-card h2 {
	margin-bottom: .75rem;
}

.public-pricing-card p,
.pricing-cta {
	margin-bottom: 0;
}

.pricing-cta {
	margin-top: 2rem;
	text-align: center;
	font-weight: 700;
}

/* The guide explains the upload workflow below. */
.home-guide {
	max-width: 64rem;
	margin: 4.5rem auto 0;
}

.home-guide > h2 {
	text-align: center;
	margin-bottom: 2rem;
}

.guide-steps {
	list-style: none;
	margin: 0;
	padding: 0;
	counter-reset: guide;
}

.guide-steps > li {
	counter-increment: guide;
	display: grid;
	grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
	align-items: center;
	gap: clamp(1rem, 3vw, 3rem);
	position: relative;
	margin: 0 0 4.5rem;
	padding: clamp(1rem, 2.5vw, 2rem);
	border: 1px solid var(--pico-muted-border-color);
	border-radius: 1rem;
}

.guide-steps > li:last-child {
	margin-bottom: 0;
}

.guide-steps > li:not(:last-child)::after {
	content: "↓";
	position: absolute;
	bottom: -4rem;
	left: 50%;
	transform: translateX(-50%);
	font-size: 2.5rem;
	line-height: 3.5rem;
	color: var(--pico-primary);
}

.guide-steps picture {
	display: block;
	min-width: 0;
}

.guide-steps img {
	width: 100%;
	height: auto;
	border: 1px solid var(--pico-muted-border-color);
	border-radius: .5rem;
}

.guide-copy h3::before {
	content: counter(guide, decimal-leading-zero) "  ";
	color: var(--pico-primary);
}

.guide-copy p {
	margin-bottom: 0;
}

.guide-credit {
	margin: 1.5rem 0 0;
	text-align: center;
	font-size: .85rem;
	color: var(--pico-muted-color);
}

@media (max-width: 44rem) {
	.upload-offer-layout,
	.public-pricing-grid {
		grid-template-columns: 1fr;
	}

	.upload-offer-layout {
		margin-top: 1.5rem;
	}

	/* Turnstile's 300px widget must fit even at a 320px viewport. */
	main.upload-page {
		max-width: 100%;
	}

	/* Tutorial screenshots need room for
	   the app's real control labels to be readable on a phone. */
	.home-guide {
		width: calc(100vw - 1rem);
		max-width: none;
		margin-left: calc(50% - 50vw + .5rem);
	}

	.guide-steps > li {
		grid-template-columns: 1fr;
		padding: .75rem;
	}
}

/* The page's one piece of persistent chrome. Keep the language menu, image
   gallery link, and account actions together at the top of every page. */
.top-bar {
	display: flex;
	align-items: center;
	justify-content: flex-end;
	flex-wrap: wrap;
	gap: .5rem 1rem;
	padding: .75rem 1rem 0;
}

.language-switcher {
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: .25rem;
	margin: 0;
}

.language-icon {
	width: 1.25rem;
	height: 1.25rem;
	fill: none;
	stroke: currentColor;
	stroke-linecap: round;
	stroke-linejoin: round;
	stroke-width: 1.75;
}

.language-label {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

.language-switcher select {
	width: 4.75rem;
	min-width: 0;
	margin: 0;
}

.account-controls,
.account-controls form {
	display: flex;
	align-items: center;
	gap: .65rem;
	margin: 0;
}

.account-login-link:focus-visible {
	outline: 3px solid currentColor;
	outline-offset: 2px;
}

.account-controls [role="button"],
.account-controls button {
	width: auto;
	margin: 0;
	padding: .35rem .8rem;
	font-size: .9rem;
	white-space: nowrap;
}

.auth-card {
	max-width: 30rem;
}

.auth-card section p {
	overflow-wrap: anywhere;
}

/* Browser chrome leaves little vertical room on short phones. Keep the
   authentication form and its follow-up links in view by reclaiming the
   generous page margins while preserving the normal desktop rhythm. */
@media (max-width: 30rem) and (max-height: 50rem) {
	main.auth-card {
		margin-block: 2rem;
	}
}

.auth-card h1 {
	margin-bottom: 2rem;
}

.auth-card small {
	display: block;
	margin: -.75rem 0 1rem;
}

.auth-field-error {
	/* Keep the localized message's own wrapped height reserved. Otherwise a
	   blur immediately before a submit click moves the button out from under
	   the pointer, which makes mobile registration look unresponsive. */
	display: block;
	min-height: 1.5em;
	margin: -.75rem 0 1rem;
	visibility: hidden;
	color: var(--pico-del-color);
	font-weight: var(--pico-font-weight);
	overflow-wrap: anywhere;
}

.auth-field-error.is-visible {
	visibility: visible;
}

.auth-form-error {
	margin: -.75rem 0 1rem;
	color: var(--pico-del-color);
	font-weight: var(--pico-font-weight);
	overflow-wrap: anywhere;
}

.auth-card .remember-device {
	display: flex;
	align-items: flex-start;
	gap: .65rem;
	margin: 0 0 1rem;
}

.auth-card .remember-device input {
	flex: 0 0 auto;
	margin-top: .2rem;
}

.auth-card .remember-device small {
	display: block;
	margin: .1rem 0 0;
	color: var(--pico-muted-color);
	font-weight: normal;
}

.password-field {
	position: relative;
}

.password-field input {
	padding-inline-end: 3.25rem;
}

.password-visibility-toggle {
	all: unset;
	box-sizing: border-box;
	position: absolute;
	top: .25rem;
	right: .25rem;
	display: none;
	place-items: center;
	width: 2.5rem;
	height: 2.5rem;
	border-radius: var(--pico-border-radius);
	color: var(--pico-muted-color);
	cursor: pointer;
}

.password-visibility-ready .password-visibility-toggle {
	display: grid;
}

.password-visibility-toggle:hover {
	color: var(--pico-color);
}

.password-visibility-toggle:focus-visible {
	outline: var(--pico-outline-width) solid var(--pico-primary-focus);
	outline-offset: 1px;
}

.password-visibility-toggle svg {
	width: 1.25rem;
	height: 1.25rem;
	fill: none;
	stroke: currentColor;
	stroke-linecap: round;
	stroke-linejoin: round;
	stroke-width: 1.75;
}

.password-hidden-icon {
	display: none;
}

.password-visibility-toggle.is-visible .password-visible-icon {
	display: none;
}

.password-visibility-toggle.is-visible .password-hidden-icon {
	display: block;
}

.password-strength {
	margin: -.5rem 0 1rem;
}

.password-strength[hidden] {
	display: none;
}

.password-strength-bar {
	display: grid;
	grid-template-columns: repeat(5, minmax(0, 1fr));
	gap: .35rem;
}

.password-strength-bar span {
	height: .35rem;
	border-radius: 999px;
	background: var(--pico-muted-border-color);
}

.password-strength[data-score="0"] {
	--password-strength-color: #b42318;
}

.password-strength[data-score="1"] {
	--password-strength-color: #d93526;
}

.password-strength[data-score="2"] {
	--password-strength-color: #b86e00;
}

.password-strength[data-score="3"] {
	--password-strength-color: #398712;
}

.password-strength[data-score="4"] {
	--password-strength-color: #147a55;
}

.password-strength[data-score] .password-strength-bar span:first-child,
.password-strength[data-score="1"] .password-strength-bar span:nth-child(-n+2),
.password-strength[data-score="2"] .password-strength-bar span:nth-child(-n+3),
.password-strength[data-score="3"] .password-strength-bar span:nth-child(-n+4),
.password-strength[data-score="4"] .password-strength-bar span {
	background: var(--password-strength-color);
}

.password-strength-status {
	margin: .35rem 0 0;
	font-size: .875rem;
	font-weight: var(--pico-font-weight);
	overflow-wrap: anywhere;
}

/* JavaScript controls its marked forms explicitly, so browser timing cannot
   conflict with touched/submitted presentation. Unenhanced forms retain a
   native :user-invalid fallback for all adjacent auth field messages. */
.auth-card form.auth-validation-active .auth-invalid {
	border-color: var(--pico-form-element-invalid-border-color);
}

.auth-card form:not(.auth-validation-active) input:required:user-invalid {
	border-color: var(--pico-form-element-invalid-border-color);
}

.auth-card form:not(.auth-validation-active) input:required:user-invalid + .auth-field-error,
.auth-card form:not(.auth-validation-active) .password-field:has(input:required:user-invalid) + .auth-field-error {
	visibility: visible;
}

@supports not selector(input:user-invalid) {
	.auth-card form:not(.auth-validation-active) input:required:invalid {
		border-color: var(--pico-form-element-invalid-border-color);
	}

	.auth-card form:not(.auth-validation-active) input:required:invalid + .auth-field-error,
	.auth-card form:not(.auth-validation-active) .password-field:has(input:required:invalid) + .auth-field-error {
		visibility: visible;
	}
}

.processing-limit-note {
	max-width: 42rem;
	margin: 1rem auto;
	text-align: center;
}

.account-required-actions {
	display: flex;
	justify-content: center;
	gap: 1rem;
	flex-wrap: wrap;
}

/* Credits --------------------------------------------------------------- */
.credits-page {
	max-width: 58rem;
}

.credits-balance {
	margin: -1rem 0 1.5rem;
	text-align: center;
	color: var(--pico-muted-color);
}

.credits-alert {
	padding: .8rem 1rem;
	border: 1px solid var(--pico-del-color);
	border-radius: var(--pico-border-radius);
	color: var(--pico-del-color);
}

.credits-intro {
	text-align: center;
}

.credits-heading {
	margin-top: 2rem;
	font-size: 1.25rem;
}

.credits-grid {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 1rem;
}

.credit-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	min-height: 12rem;
	padding: 1.5rem 1rem 1rem;
	border: 1px solid var(--pico-muted-border-color);
	border-radius: calc(var(--pico-border-radius) * 2);
	background: var(--pico-card-background-color);
	text-align: center;
}

.credit-card h3 {
	margin: 0;
	font-size: 1.8rem;
}

.credit-card-amount {
	margin: .75rem 0 .15rem;
	font-size: 1.2rem;
	font-weight: var(--pico-font-weight);
}

.credit-card-detail {
	margin: 0;
	color: var(--pico-muted-color);
}

.credit-card-discount {
	margin: 0 0 .75rem;
	padding: .2rem .65rem;
	border: 1px solid var(--pico-primary);
	border-radius: 999px;
	color: var(--pico-primary);
	font-size: .85rem;
	font-weight: 600;
}

.credit-card-status {
	margin-top: auto;
	padding: .25rem .7rem;
	border-radius: 999px;
	background: var(--pico-muted-border-color);
	color: var(--pico-muted-color);
	font-size: .85rem;
}

.credit-purchase-form {
	width: 100%;
	margin: 1rem 0 0;
}

.credit-purchase-form button {
	width: 100%;
	margin: 0;
}

/* Account settings ------------------------------------------------------
 *
 * The page is a list of setting groups rather than a run of headings and
 * links: each group is a full-width row with its value on the left and its
 * action on the right, and the account-level actions sit below a divider so
 * logging out and deleting read as separate from the settings themselves.
 * Only --pico-* tokens are used, so both colour schemes come for free.
 */
.settings-list {
	margin: 0 0 2rem;
	border: 1px solid var(--pico-muted-border-color);
	border-radius: calc(var(--pico-border-radius) * 2);
	background: var(--pico-card-background-color);
	overflow: hidden;
}

.settings-item {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: 1rem 1.25rem;
}

.settings-item + .settings-item {
	border-top: 1px solid var(--pico-muted-border-color);
}

.settings-item-copy {
	/* A long email has to wrap inside the row rather than widen the card. */
	min-width: 0;
}

.settings-item-title {
	margin: 0;
	font-size: 1.05rem;
}

.settings-item-value {
	margin: .15rem 0 0;
	color: var(--pico-muted-color);
	overflow-wrap: anywhere;
}

/* Doubled class (via the list ancestor) so this beats Pico's own
 * `[role=button]` padding/font-size: styles.css is linked before
 * pico.classless.css, so a single-class selector ties and loses on order. */
.settings-list .settings-item-action {
	flex: 0 0 auto;
	margin: 0;
	padding: .35rem .8rem;
	font-size: .9rem;
	white-space: nowrap;
}

.settings-item-badge {
	flex: 0 0 auto;
	padding: .2rem .65rem;
	border-radius: 999px;
	background: var(--pico-muted-border-color);
	color: var(--pico-muted-color);
	font-size: .8rem;
	font-weight: var(--pico-font-weight);
	white-space: nowrap;
}

.settings-actions {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: .75rem;
	flex-wrap: wrap;
	padding-top: 1.5rem;
	border-top: 1px solid var(--pico-muted-border-color);
}

.settings-actions form {
	margin: 0;
}

.settings-actions button,
.settings-actions [role="button"] {
	width: auto;
	margin: 0;
	padding: .5rem 1rem;
}

/* The classless build ships no `.secondary`, so the logout control has to
 * name Pico's own secondary tokens. */
.settings-actions .secondary {
	--pico-background-color: var(--pico-secondary-background);
	--pico-border-color: var(--pico-secondary-border);
	--pico-color: var(--pico-secondary-inverse);
}

.settings-actions .secondary:is(:hover, :active, :focus) {
	--pico-background-color: var(--pico-secondary-hover-background);
	--pico-border-color: var(--pico-secondary-hover-border);
	--pico-color: var(--pico-secondary-inverse);
}

/* The same red as a selected line and the gallery's delete action, rather
 * than Pico's muted --pico-del-color, so destructive intent is unmistakable. */
.settings-actions .settings-danger {
	--pico-background-color: transparent;
	--pico-border-color: #d93526;
	--pico-color: #d93526;
}

.settings-actions .settings-danger:is(:hover, :active, :focus) {
	--pico-background-color: #d93526;
	--pico-border-color: #d93526;
	--pico-color: #fff;
}

@media (max-width: 42rem) {
	.credits-grid {
		grid-template-columns: 1fr;
	}
}

@media (max-width: 30rem) {
	.settings-item {
		flex-direction: column;
		align-items: stretch;
		gap: .75rem;
	}

	.settings-list .settings-item-action {
		text-align: center;
	}

	.settings-item-badge {
		/* The stacked row stretches its children; keep the badge hugging its
		 * own label rather than spanning the card. */
		align-self: flex-start;
	}

	.settings-actions {
		flex-direction: column;
		align-items: stretch;
	}

	.settings-actions button,
	.settings-actions [role="button"] {
		width: 100%;
	}
}

/* For images */
img {
	display: block;
	margin: 0 auto;
	padding: 0;
	max-height: 100%;
	max-width: 100%;
}


.alert-error {
	border-color: #D93526;
	background-color: #D93526;
	text-align: center;
	color: white;

}

/* Credit purchases are successes, not full-width error banners. */
.credit-notifications {
	position: fixed;
	top: 6rem;
	right: 1rem;
	width: min(24rem, calc(100% - 2rem));
	z-index: 20;
	display: grid;
	gap: .75rem;
	pointer-events: none;
}

.credit-notification {
	display: flex;
	align-items: flex-start;
	gap: .75rem;
	padding: 1rem;
	border: 1px solid #315547;
	border-left: 4px solid #4ade80;
	border-radius: .75rem;
	background: #192721;
	color: #f0fdf4;
	box-shadow: 0 .5rem 1.5rem #0003;
	pointer-events: auto;
}

.credit-notification-icon {
	color: #4ade80;
	font-size: 1.2rem;
	line-height: 1.5;
}

.credit-notification-copy {
	flex: 1;
	min-width: 0;
	overflow-wrap: anywhere;
}

.credit-notification-copy strong,
.credit-notification-copy small {
	display: block;
}

.credit-notification-copy small {
	margin-top: .25rem;
	color: #c4d6cb;
}

.credit-notification .credit-notification-dismiss {
	flex: none;
	width: 2rem;
	min-height: 2rem;
	margin: -.25rem -.25rem 0 0;
	padding: 0;
	border: 0;
	border-radius: .25rem;
	background: transparent;
	color: #c4d6cb;
	font-size: 1.5rem;
	line-height: 1;
	box-shadow: none;
}

.credit-notification-dismiss:hover,
.credit-notification-dismiss:focus-visible {
	background: #2d4438;
	color: #fff;
}

.credit-notification-dismiss:focus-visible {
	outline: 2px solid #4ade80;
	outline-offset: 2px;
}

/* Pico makes every loading element nowrap so a spinner and short label stay
   together. The processing card contains paragraphs and a user-provided
   filename, so that rule would push its content past the card on phones.
   Keep the override important because Pico's loading selector is deliberately
   more specific than the component selector. */
.processing-card[aria-busy="true"] {
	white-space: normal !important;
	overflow-wrap: anywhere;
}

/* Editable text overlay ---------------------------------------------------
 *
 * The overlay is positioned in source-image pixels, so it has to scale with
 * the image when max-width shrinks it. --px is one source pixel expressed in
 * rendered units: container queries make that exact with no JavaScript.
 */
.image-stage {
	position: relative;
	/* Keep every per-line z-index inside the image's own stacking context;
	   sticky controls can then sit above the whole stage with one z-index. */
	z-index: 0;
	container-type: inline-size;
	width: calc(var(--img-w) * 1px);
	max-width: 100%;
	aspect-ratio: var(--img-w) / var(--img-h);
	margin: 2em auto;
	--px: calc(100cqw / var(--img-w));
}

/* max-height must be re-set: the global img rule above would otherwise
 * letterbox the image out of sync with the overlay coordinates. */
.image-stage img {
	display: block;
	width: 100%;
	height: auto;
	max-height: none;
}

.text-layer {
	position: absolute;
	margin: 0;
	padding: 0;
	line-height: 1;
	/* Required: the server's ink offsets assume a line box exactly one em
	   tall. Any other line-height moves every box down by half the excess. */
	white-space: pre;
	/* One visual line: typing past the original length grows the box
	   rightward instead of reflowing it onto a second line. */
	/* Keep every editable region visible, including before the user hovers it.
	   The offset makes the boundary a little larger than the text itself.
	   Solid red, matching the selected-state color from the selection
	   screen: these are the lines the user picked to edit. */
	outline: 2px solid #d93526;
	outline-offset: 2px;
	cursor: text;
	transition: outline-color .1s, outline-width .1s;
}

.text-layer:hover,
.text-layer:focus {
	outline-color: #a8281c;
	outline-width: 3px;
}

/* Selection overlay -------------------------------------------------------
 *
 * One box per OCR line, in the same source-pixel coordinate system as the
 * editable layers above -- the two stages are the same page in two modes, so
 * a box and the layer that replaces it cannot drift apart.
 */
.text-box {
	position: absolute;
	box-sizing: border-box;
	cursor: pointer;
	/* Two-tone stroke: a flat outline disappears over a busy photo, and the
	   white halo sits in the gap outline-offset opens up. Blue by default --
	   boxes start unselected, and a faint gray outline read as decoration
	   rather than something clickable. */
	outline: 2px solid #0878d1;
	outline-offset: 1px;
	box-shadow: 0 0 0 1px rgba(255, 255, 255, .9);
	background-color: rgba(8, 120, 209, .12);
	transition: background-color .1s, outline-color .1s;
	/* A tilted line's box is rotated about the corner its text starts from,
	   the same origin image.html rotates a .text-layer about, so the two
	   stages put the same box in the same place. */
	transform-origin: 0 0;
}

.text-box[aria-checked="true"] {
	outline-color: #d93526;
	background-color: rgba(217, 53, 38, .18);
}

.text-box:hover {
	background-color: rgba(8, 120, 209, .3);
}

.text-box[aria-checked="true"]:hover {
	background-color: rgba(217, 53, 38, .35);
}

.text-box:focus-visible {
	outline-style: dashed;
	outline-width: 3px;
}

.export-bar,
.select-bar {
	display: flex;
	align-items: center;
	gap: 1em;
	/* Three buttons, a count and a message do not fit one phone-width row;
	   without wrapping the row simply overflowed the viewport and pushed the
	   message off-screen, where it could not do the job it was added for. */
	flex-wrap: wrap;
	justify-content: center;
	/* Match .image-stage's centring so the buttons sit under (or over) the
	   image rather than under the full-width main. */
	width: fit-content;
	max-width: 100%;
	margin: 2em auto 0;
	/* A tall image can render taller than the viewport, and this bar is the
	   only way off the editor -- pin it to the screen so it never scrolls
	   out of reach the way it used to. Each bar sticks to whichever edge it
	   starts nearest: .select-bar sits above the image, so it has to hold
	   the top edge as the page scrolls down past it; .export-bar sits below
	   the image, so a bottom edge sticks it is what pulls it into view
	   while the page is still scrolled to the top. Sticky only ever holds
	   an element within its own static-flow range, so the wrong edge here
	   is a silent no-op, not a visible bug -- each rule below has to match
	   where its bar actually lives in the page.
	*/
	position: sticky;
	padding: .75em 1em;
	background: var(--pico-background-color, #fff);
}

.select-bar {
	top: 0;
	/* The image follows this sticky bar in document order, so its stacking
	   context otherwise paints over the bar and intercepts clicks. */
	z-index: 1;
	border-bottom: 1px solid var(--pico-muted-border-color, #ccc);
}

.export-bar {
	margin: 0 auto 3em;
	bottom: 0;
	border-top: 1px solid var(--pico-muted-border-color, #ccc);
}

/* A phone's on-screen keyboard shrinks the visual viewport, not the layout
   one sticky positioning sticks against, so `bottom: 0` re-anchors to a point
   that can land mid-page -- directly over the .text-layer being edited,
   blocking the caret and selection handles it opened for (BUG-037).
   image.html toggles this class from focus/blur on .text-layer so the bar
   only ever floats when there's no keyboard fighting it for the same space;
   `position: static` (not display: none) returns it to its normal-flow spot
   below the image without changing the page's scroll height. */
.export-bar.is-editing {
	position: static;
}

.export-upload-row {
	flex-basis: 100%;
	text-align: center;
}

/* The attribute selector is load-bearing, not decoration: pico's
   `button[type=submit] { width: 100% }` ties with a plain `.select-bar
   button` on specificity and is linked after this file, so it won the
   cascade and stretched the submit button to the bar's full width. */
.export-bar button,
.export-bar [role="button"],
.select-bar button,
.select-bar button[type="submit"] {
	width: auto;
	margin: 0;
	white-space: nowrap;
}

/* Looks like Pico's [disabled] button, but carries no disabled state of any
   kind: it still has to receive the click it answers with an explanation. */
.select-bar button.needs-selection {
	opacity: .5;
}

.select-hint {
	color: var(--pico-del-color, #D93526);
	font-size: .9rem;
}

.export-hint {
	max-width: min(70ch, 92vw);
	margin: -2em auto 3em;
	font-size: .9rem;
	color: var(--muted-color);
	text-align: center;
}

/* My images -------------------------------------------------------------- */

/* Colour is pico's own [role=button] default -- the same primary blue as
   Select all, Clear and the export buttons -- so nothing here touches
   --pico-*; only the size is set, because a bar that rides above every page
   wants a smaller control than the ones inside a page do. Pico already
   paints [aria-current] like a hover, which marks the gallery's own tab. */
.top-bar .my-images-link {
	padding: .35rem .8rem;
	font-size: .9rem;
	white-space: nowrap;
}

/* The four controls can otherwise exceed a phone-width viewport and push the
   account group onto a separate row. Compact them as a group so a signed-in
   user sees the username and logout action alongside the other top controls. */
@media (max-width: 30rem) {
	.top-bar {
		gap: .35rem .5rem;
		padding: .5rem .5rem 0;
	}

	.language-switcher {
		gap: .15rem;
	}

	.language-icon {
		width: 1.25rem;
		height: 1.25rem;
	}

	/* Keep enough inline room for the two-letter locale code after Pico's
	   reserved chevron space. The narrower width clipped EN to its first
	   glyph in mobile Chrome. */
	.language-switcher select {
		width: 4.75rem;
		padding: .25rem .5rem;
		font-size: .8rem;
	}

	.top-bar .my-images-link,
	.account-controls [role="button"],
	.account-controls button {
		padding: .25rem .5rem;
		font-size: .8rem;
	}

	.account-controls,
	.account-controls form {
		gap: .35rem;
	}
}

/* main's 5em is sized for a single centred form; a grid starts far enough
   down the page without it. */
main.gallery {
	margin-top: 2em;
}

/* The one-sentence-and-a-button pages (expired, deleted). Centred with the
   heading above them: left-aligned body text under a centred h1 reads as a
   layout accident, which is what it was on the empty gallery (BUG-032). */
.notice {
	text-align: center;
}

/* Long translated limit/account headings must fit narrow phone viewports. */
.account-required h1,
.account-settings h1 {
	max-width: 100%;
	overflow-wrap: anywhere;
}

.notice [role="button"] {
	margin-top: var(--pico-spacing, 1rem);
}

.gallery-note {
	text-align: center;
	font-size: .9rem;
	color: var(--pico-muted-color);
}

/* Centred like the heading above it and the button below it. Not muted the
   way .gallery-note is: with no tiles to introduce, this sentence is the
   page's content rather than a caption on it. */
.gallery-empty {
	text-align: center;
}

.gallery-actions {
	margin-top: 2em;
	text-align: center;
}

.image-grid {
	display: grid;
	/* auto-fill, not auto-fit: a single image keeps its tile size instead of
	   stretching one thumbnail across the whole row. */
	grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
	gap: 1.5rem 1rem;
	margin: 2em 0 0;
	padding: 0;
	list-style: none;
}

/* pico's `ul li { list-style: square }` is linked after this file and beats
   list-style on the ul alone, which left a marker beside every tile. */
.image-grid li {
	list-style: none;
}

/* A column so the delete button sits at the bottom of every tile: grid
   items stretch to their row's height, and without this a short filename in
   one column would leave its button floating above its neighbours'. */
.image-tile {
	display: flex;
	flex-direction: column;
}

.image-card {
	display: block;
	flex: 1;
	/* A named colour rather than `color: inherit`: pico rewrites --pico-color
	   to the link colour on every anchor, so the tile's text has to name a
	   token of its own, and an inherited colour is not always re-resolved
	   when the viewer's colour scheme changes under an already-loaded page. */
	color: var(--pico-contrast);
	text-decoration: none;
}

/* The global img rule centres and caps by viewport height, which would
   letterbox a tile inside its own cell. A gallery wants every tile the same
   shape, so crop to a fixed ratio instead. */
.image-card img {
	width: 100%;
	max-width: none;
	max-height: none;
	aspect-ratio: 4 / 3;
	/* contain, not cover: these are pictures chosen for the words in them,
	   and a crop to fill the tile takes the ends off the very lines the
	   user is picking this image out by. */
	object-fit: contain;
	border: 1px solid var(--pico-muted-border-color, #ccc);
	border-radius: var(--pico-border-radius, .25rem);
	background: var(--pico-card-sectioning-background-color);
}

.image-card:hover img,
.image-card:focus-visible img {
	border-color: var(--pico-primary);
}

.image-card-name {
	display: block;
	margin-top: .5rem;
	/* A long filename must not widen its column and pull the grid apart. */
	overflow-wrap: anywhere;
	font-size: .9rem;
}

.image-card-meta {
	display: block;
	font-size: .8rem;
	color: var(--pico-muted-color);
}

.image-delete {
	margin: .5rem 0 0;
}

/* The same red as a selected line and the error banner, rather than pico's
   muted --pico-del-color, which loses too much contrast against white text
   in the dark scheme. The descendant selectors out-specify pico's own
   `button` rules, which are linked after this file and set these same
   tokens; the hover pair needs one more class than pico's :hover rule. */
.image-grid .image-delete button {
	width: 100%;
	margin: 0;
	padding: .3rem .6rem;
	font-size: .85rem;
	--pico-background-color: #d93526;
	--pico-border-color: #d93526;
	--pico-color: #fff;
}

.image-grid .image-delete button:is(:hover, :active, :focus) {
	--pico-background-color: #a8281c;
	--pico-border-color: #a8281c;
	--pico-color: #fff;
}

/* Confirmation dialog ------------------------------------------------------
 *
 * pico styles `dialog` as a full-viewport overlay around an `article`, and
 * has a `dialog:not([open])` rule, so showModal() works with it untouched.
 * Only the two buttons need saying: the classless build ships no `.secondary`
 * or destructive variant, so both would otherwise be primary blue.
 */
#delete-dialog #delete-dialog-title {
	margin: 0;
	font-size: 1.25rem;
}

#delete-dialog .delete-dialog-name {
	margin: calc(var(--pico-spacing) * .5) 0 0;
	color: var(--pico-muted-color);
	/* A long filename must not push the dialog wider than the viewport. */
	overflow-wrap: anywhere;
}

#delete-dialog .delete-dialog-confirm {
	--pico-background-color: #d93526;
	--pico-border-color: #d93526;
	--pico-color: #fff;
}

#delete-dialog .delete-dialog-confirm:is(:hover, :active, :focus) {
	--pico-background-color: #a8281c;
	--pico-border-color: #a8281c;
	--pico-color: #fff;
}

#delete-dialog .delete-dialog-cancel {
	--pico-background-color: var(--pico-secondary-background);
	--pico-border-color: var(--pico-secondary-border);
	--pico-color: var(--pico-secondary-inverse);
}

#delete-dialog .delete-dialog-cancel:is(:hover, :active, :focus) {
	--pico-background-color: var(--pico-secondary-hover-background);
	--pico-border-color: var(--pico-secondary-hover-border);
	--pico-color: var(--pico-secondary-inverse);
}

/* Report issue or feedback ----------------------------------------------- */

.page-footer {
	padding: 2rem 1rem;
	text-align: center;
}

.footer-nav {
	display: inline-flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: .25rem .75rem;
	font-size: .9rem;
}

.footer-nav a {
	color: var(--pico-primary);
	text-decoration: underline;
	text-underline-offset: .2em;
}

.footer-nav a:is(:hover, :active, :focus) {
	color: var(--pico-primary-hover);
}

.footer-nav a[aria-current="page"] {
	font-weight: 600;
	text-decoration: none;
}

.footer-separator {
	color: var(--pico-muted-color, #888);
	user-select: none;
}

@media (max-width: 480px) {
	.footer-nav {
		flex-direction: column;
		gap: .6rem;
	}
	.footer-separator {
		display: none;
	}
}

.page-footer #feedback-open {
	width: auto;
	margin: 0;
	padding: 0;
	--pico-background-color: transparent;
	--pico-border-color: transparent;
	--pico-color: var(--pico-primary);
	text-decoration: underline;
	text-underline-offset: .2em;
	font-size: inherit;
	font-weight: inherit;
	line-height: inherit;
}

.page-footer #feedback-open:is(:hover, :active, :focus) {
	--pico-background-color: transparent;
	--pico-border-color: transparent;
	--pico-color: var(--pico-primary-hover);
}

.legal-page {
	max-width: 48rem;
	margin: 0 auto;
	padding: 1.5rem 1rem 3rem;
}

.legal-page article {
	margin-bottom: 0;
}

.legal-page .legal-date {
	color: var(--pico-muted-color, #888);
	margin-bottom: 1.5rem;
}

.legal-page h2 {
	margin-top: 1.5rem;
	margin-bottom: .5rem;
}

.legal-page h3 {
	margin-top: 1rem;
	margin-bottom: .25rem;
}

#feedback-dialog .feedback-cancel {
	--pico-background-color: var(--pico-secondary-background);
	--pico-border-color: var(--pico-secondary-border);
	--pico-color: var(--pico-secondary-inverse);
}

#feedback-dialog .feedback-cancel:is(:hover, :active, :focus) {
	--pico-background-color: var(--pico-secondary-hover-background);
	--pico-border-color: var(--pico-secondary-hover-border);
	--pico-color: var(--pico-secondary-inverse);
}

#feedback-dialog article {
	max-width: 36rem;
	max-height: calc(100dvh - 2rem);
}

#feedback-dialog #feedback-dialog-title {
	margin: 0;
	font-size: 1.25rem;
}

#feedback-dialog .feedback-demo-note {
	color: var(--pico-muted-color);
}

#feedback-dialog textarea {
	min-height: 8rem;
	resize: vertical;
}

#feedback-dialog .feedback-share-project {
	margin-top: var(--pico-spacing, 1rem);
}

#feedback-dialog .feedback-dialog-actions {
	display: flex;
	justify-content: flex-end;
	gap: .5rem;
}

#feedback-dialog .feedback-dialog-actions button {
	width: auto;
	margin: 0;
}

@media (max-width: 575px) {
	#feedback-dialog article {
		max-height: calc(100dvh - 1rem);
		margin: .5rem;
	}

	#feedback-dialog .feedback-dialog-actions {
		display: grid;
		grid-template-columns: 1fr 1fr;
	}

	#feedback-dialog .feedback-dialog-actions button {
		width: 100%;
	}

	#feedback-confirmation .feedback-dialog-actions {
		grid-template-columns: 1fr;
	}
}
