/* greentea web interface. The Content-Security-Policy allows no inline
   styles, so every style lives here, HTMX's included.

   The design tokens come first: colours for light and dark, the type
   scale, spacing, radii, shadows and the focus ring. Everything below
   uses them, so a change of colour or size is made once, here. The
   colours keep WCAG 2.2 AA contrast: text and its background at least
   4.5:1, the borders of form controls and the focus ring against the
   page at least 3:1.

   Which colours apply is the user's choice, which the server writes
   into the page as data-theme on <html>: light or dark, or, without
   the attribute, the device's own setting (prefers-color-scheme). The
   dark colours are therefore set twice, for the device's setting and
   for the choice, and TestThemeTokens keeps the two blocks equal. A
   part of a page may set data-theme too, as the style guide does to
   show both themes. */

:root {
	color-scheme: light dark;

	--font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	--font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
	--text-xs: 0.75rem;
	--text-sm: 0.8125rem;
	--text-base: 0.875rem;
	--text-md: 0.9375rem;
	--text-lg: 1.0625rem;
	--text-xl: 1.3125rem;
	--text-2xl: 1.5rem;

	--space-1: 0.25rem;
	--space-2: 0.5rem;
	--space-3: 0.75rem;
	--space-4: 1rem;
	--space-5: 1.25rem;
	--space-6: 1.5rem;
	--space-7: 1.75rem;
	--space-8: 2.5rem;
	/* Between the blocks of a page: header, cards, callouts, tables. */
	--block-gap: 1.125rem;

	--radius-sm: 6px;
	--radius: 8px;
	--radius-lg: 10px;
	--radius-xl: 12px;
	--radius-pill: 999px;
	--control-height: 2.5rem;
	--sidebar-width: 15rem;

	font-family: var(--font-sans);
	line-height: 1.45;
}

/* Light: the default, and the choice of a user who wants it. */
:root,
[data-theme="light"] {
	--bg: #f4f6f4;
	--surface: #ffffff;
	--surface-2: #eef2ef;
	--border: #d8dfda;
	--control-border: #7d8a82;
	--text: #16201a;
	--muted: #4a574f;
	--accent: #1d6b47;
	--accent-hover: #155637;
	--on-accent: #ffffff;
	--accent-soft: #e1efe7;
	--accent-text: #155637;
	--ok-bg: #e1efe7;
	--ok: #155637;
	--warn-bg: #fbeed3;
	--warn: #7f4c00;
	--danger-bg: #fae4e1;
	--danger: #9f2318;
	--on-danger: #ffffff;
	--info-bg: #e2edf8;
	--info: #1b538a;
	--focus: #2b8659;
	--scrim: rgba(5, 10, 7, 0.55);
	--shadow-sm: 0 1px 2px rgba(16, 24, 20, 0.06);
	--shadow-lg: 0 12px 32px rgba(5, 10, 7, 0.25);
}

/* Dark, as the device asks unless the user chose light... */
@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) {
		--bg: #0e1310;
		--surface: #151c18;
		--surface-2: #1b2520;
		--border: #2b3730;
		--control-border: #6b7a71;
		--text: #e5ebe7;
		--muted: #a6b2aa;
		--accent: #5cbf8d;
		--accent-hover: #7ad0a5;
		--on-accent: #0a1a11;
		--accent-soft: #183428;
		--accent-text: #8fdcb4;
		--ok-bg: #183428;
		--ok: #8fdcb4;
		--warn-bg: #33270f;
		--warn: #f1c27a;
		--danger-bg: #3b1713;
		--danger: #f3a196;
		--on-danger: #2a0d09;
		--info-bg: #132a40;
		--info: #93c3f1;
		--focus: #7ad0a5;
		--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
		--shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.5);
	}
}

/* ...and as the user chose: the same colours. */
[data-theme="dark"] {
	--bg: #0e1310;
	--surface: #151c18;
	--surface-2: #1b2520;
	--border: #2b3730;
	--control-border: #6b7a71;
	--text: #e5ebe7;
	--muted: #a6b2aa;
	--accent: #5cbf8d;
	--accent-hover: #7ad0a5;
	--on-accent: #0a1a11;
	--accent-soft: #183428;
	--accent-text: #8fdcb4;
	--ok-bg: #183428;
	--ok: #8fdcb4;
	--warn-bg: #33270f;
	--warn: #f1c27a;
	--danger-bg: #3b1713;
	--danger: #f3a196;
	--on-danger: #2a0d09;
	--info-bg: #132a40;
	--info: #93c3f1;
	--focus: #7ad0a5;
	--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
	--shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.5);
}

/* Form controls and scroll bars follow the chosen theme too. */
[data-theme="light"] {
	color-scheme: light;
}

[data-theme="dark"] {
	color-scheme: dark;
}

/* Tokens made of others, set wherever a theme is, so that they take
   its colours: a custom property's var() is resolved where it is set. */
:root,
[data-theme] {
	--focus-ring: 2px solid var(--focus);

	/* The names the pages written before the tokens use. */
	--fg: var(--text);
	--line: var(--border);
	--panel: var(--surface-2);
	--error-bg: var(--danger-bg);
	--error-fg: var(--danger);
	--accent-fg: var(--on-accent);
}

/* Base elements. */
*,
*::before,
*::after {
	box-sizing: border-box;
}

body {
	margin: 0;
	background: var(--bg);
	color: var(--text);
	font-size: var(--text-base);
}

a {
	color: var(--accent-text);
}

h1 {
	font-size: var(--text-2xl);
	line-height: 1.2;
	letter-spacing: -0.015em;
}

h2 {
	font-size: var(--text-lg);
	line-height: 1.3;
}

h3 {
	font-size: var(--text-md);
}

code,
pre,
kbd {
	font-family: var(--font-mono);
	font-size: 0.92em;
}

:focus-visible {
	outline: var(--focus-ring);
	outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		transition: none !important;
		animation: none !important;
		scroll-behavior: auto !important;
	}
}

.skip {
	position: absolute;
	left: -10000px;
	z-index: 100;
}

.skip:focus {
	left: var(--space-2);
	top: var(--space-2);
	padding: var(--space-2) var(--space-4);
	border-radius: var(--radius);
	background: var(--surface);
	box-shadow: var(--shadow-lg);
}

/* Read by screen readers, not shown. */
.visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

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

.mono {
	font-family: var(--font-mono);
	font-size: var(--text-sm);
}

label {
	font-weight: 600;
}

input,
select,
textarea {
	font: inherit;
	padding: 0.5rem 0.75rem;
	border: 1px solid var(--control-border);
	border-radius: var(--radius);
	background: var(--surface);
	color: var(--text);
}

input[type="checkbox"],
input[type="radio"] {
	padding: 0;
	accent-color: var(--accent);
}

button {
	font: inherit;
	font-weight: 600;
	padding: 0.5rem 1rem;
	border: 1px solid var(--accent);
	border-radius: var(--radius);
	background: var(--accent);
	color: var(--on-accent);
	cursor: pointer;
}

button:hover {
	border-color: var(--accent-hover);
	background: var(--accent-hover);
}

button.link {
	padding: 0;
	border: none;
	background: none;
	color: var(--accent-text);
	font-weight: inherit;
	text-decoration: underline;
}

/* Icons (ui.Icon): symbols of static/icons.svg, stroked in the colour
   of the text around them. */
.icon {
	flex: none;
	width: 1.125rem;
	height: 1.125rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
	vertical-align: -0.2em;
}

/* Components (internal/web/ui/components.templ). Blocks of a page are
   spaced by their bottom margin, which collapses with the margins of
   the elements around them, so they sit between the pages' older
   markup as well as between each other. */

/* Buttons: .btn on a button or a link, with one variant at most. */
.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2);
	min-height: var(--control-height);
	padding: 0 0.875rem;
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface);
	color: var(--text);
	font: inherit;
	font-weight: 600;
	line-height: 1.2;
	text-decoration: none;
	white-space: nowrap;
	cursor: pointer;
}

.btn:hover {
	border-color: var(--border);
	background: var(--surface-2);
	color: var(--text);
}

.btn-primary {
	border-color: var(--accent);
	background: var(--accent);
	color: var(--on-accent);
}

.btn-primary:hover {
	border-color: var(--accent-hover);
	background: var(--accent-hover);
	color: var(--on-accent);
}

.btn-danger,
.btn-danger:hover {
	color: var(--danger);
}

.btn-danger-solid,
.btn-danger-solid:hover {
	border-color: var(--danger);
	background: var(--danger);
	color: var(--on-danger);
}

.btn-danger-solid:hover {
	filter: brightness(1.1);
}

.btn-quiet,
.btn-quiet:hover {
	border-color: transparent;
}

.btn-quiet {
	background: transparent;
}

.btn-icon {
	width: 2.25rem;
	min-height: 2.25rem;
	padding: 0;
	border-color: transparent;
	background: transparent;
	color: var(--muted);
}

.btn-icon:hover {
	border-color: transparent;
	color: var(--text);
}

.btn-sm {
	min-height: 2rem;
	padding: 0 0.625rem;
	font-size: var(--text-sm);
}

.btn[disabled],
.btn[aria-disabled="true"] {
	opacity: 0.55;
	cursor: not-allowed;
	pointer-events: none;
}

.actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
}

.actions:empty {
	display: none;
}

/* The page header. */
.page-head {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: 0.875rem;
	margin: 0 0 var(--space-6);
}

.page-head h1 {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-3);
	margin: 0;
	overflow-wrap: anywhere;
}

.page-head p {
	margin: var(--space-1) 0 0;
	color: var(--muted);
}

/* Cards, and a grid of them. */
.card {
	margin: 0 0 var(--block-gap);
	padding: 1.125rem var(--space-5);
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	background: var(--surface);
}

.card > :first-child {
	margin-top: 0;
}

.card > :last-child {
	margin-bottom: 0;
}

.card-head {
	margin-bottom: var(--space-3);
}

.card-head h2 {
	margin: 0;
	font-size: var(--text-md);
}

.card-head p {
	margin: var(--space-1) 0 0;
	color: var(--muted);
}

.grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 21rem), 1fr));
	gap: var(--space-4);
	margin: 0 0 var(--block-gap);
}

.grid > * {
	margin: 0;
}

/* Badges, neutral unless a tone (ui.Badge) says more. */
.badge {
	display: inline-flex;
	align-items: center;
	gap: 0.375rem;
	padding: 0.125rem 0.5625rem;
	border-radius: var(--radius-pill);
	background: var(--surface-2);
	color: var(--muted);
	font-size: var(--text-xs);
	font-weight: 600;
	line-height: 1.4;
	letter-spacing: normal;
	white-space: nowrap;
	vertical-align: 0.1em;
}

.badge::before {
	content: "";
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: currentColor;
}

.badge.ok {
	background: var(--ok-bg);
	color: var(--ok);
}

.badge.info {
	background: var(--info-bg);
	color: var(--info);
}

.badge.warn {
	background: var(--warn-bg);
	color: var(--warn);
}

.badge.danger {
	background: var(--danger-bg);
	color: var(--danger);
}

.badge.plain::before {
	display: none;
}

/* Messages: a flash above the content, a callout within it. The older
   pages' .notice and .alert look the same. */
.flash,
.callout,
.notice,
.alert {
	margin: 0 0 var(--block-gap);
	padding: var(--space-3) 0.875rem;
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	background: var(--info-bg);
	color: var(--text);
}

.flash,
.callout {
	display: flex;
	align-items: flex-start;
	gap: var(--space-3);
}

.flash p,
.callout-body > :first-child {
	margin-top: 0;
}

.flash p,
.callout-body > :last-child {
	margin-bottom: 0;
}

.flash .icon,
.callout .icon {
	margin-top: 0.125rem;
	color: var(--info);
}

.flash.ok,
.callout.ok,
.notice {
	background: var(--ok-bg);
}

.flash.ok .icon,
.callout.ok .icon {
	color: var(--ok);
}

.flash.warn,
.callout.warn {
	background: var(--warn-bg);
}

.flash.warn .icon,
.callout.warn .icon {
	color: var(--warn);
}

.flash.danger,
.callout.danger {
	background: var(--danger-bg);
}

.flash.danger .icon,
.callout.danger .icon {
	color: var(--danger);
}

.flash.neutral,
.callout.neutral {
	background: var(--surface-2);
}

.flash.neutral .icon,
.callout.neutral .icon {
	color: var(--muted);
}

.callout-title {
	font-weight: 700;
}

.alert {
	border-color: transparent;
	background: var(--danger-bg);
	color: var(--danger);
	font-weight: 600;
}

/* Empty states. */
.empty {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-2);
	margin: 0 0 var(--block-gap);
	padding: var(--space-8) var(--space-5);
	border: 1px dashed var(--border);
	border-radius: var(--radius-lg);
	color: var(--muted);
	text-align: center;
}

.empty p {
	margin: 0;
	max-width: 32rem;
}

.empty > .icon {
	width: 2rem;
	height: 2rem;
	stroke-width: 1.5;
}

.empty .empty-title {
	color: var(--text);
	font-size: var(--text-md);
	font-weight: 600;
}

.empty .actions {
	justify-content: center;
	margin-top: var(--space-2);
}

/* Key-value lists. */
.kv {
	display: grid;
	grid-template-columns: max-content minmax(0, 1fr);
	gap: var(--space-2) 1.125rem;
	margin: 0 0 var(--block-gap);
}

.kv dt {
	color: var(--muted);
}

.kv dd {
	margin: 0;
	font-weight: 500;
	overflow-wrap: anywhere;
}

@media (max-width: 30rem) {
	.kv {
		grid-template-columns: minmax(0, 1fr);
		gap: 0;
	}

	.kv dd {
		margin-bottom: var(--space-2);
	}
}

/* Tabs. */
.tabs ul {
	display: flex;
	gap: 2px;
	margin: 0 0 var(--block-gap);
	padding: 0;
	overflow-x: auto;
	border-bottom: 1px solid var(--border);
	list-style: none;
}

.tabs a {
	display: flex;
	align-items: center;
	min-height: 2.625rem;
	margin-bottom: -1px;
	padding: 0 0.875rem;
	border-bottom: 2px solid transparent;
	color: var(--muted);
	font-weight: 600;
	text-decoration: none;
	white-space: nowrap;
}

.tabs a:hover {
	color: var(--text);
}

.tabs a[aria-current="page"] {
	border-bottom-color: var(--accent);
	color: var(--accent-text);
}

/* A section navigation: links to the parts of a long page, all of
   which stay on it. */
.section-nav ul {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin: 0 0 var(--block-gap);
	padding: 0;
	list-style: none;
}

.section-nav a {
	display: inline-flex;
	align-items: center;
	min-height: 2rem;
	padding: 0 var(--space-3);
	border: 1px solid var(--border);
	border-radius: var(--radius-pill);
	background: var(--surface);
	color: var(--text);
	font-size: var(--text-sm);
	font-weight: 600;
	text-decoration: none;
	white-space: nowrap;
}

.section-nav a:hover {
	background: var(--surface-2);
}

/* A part of a page that a link leads to (#id) comes to rest a little
   below the top of the window, the title of a card a little more, as
   the card begins above it; on a phone below the top bar, which stays
   over the page. */
.page [id] {
	scroll-margin-top: var(--space-4);
}

.page .card-head [id] {
	scroll-margin-top: var(--space-8);
}

@media (max-width: 60rem) {
	.page [id] {
		scroll-margin-top: 4.5rem;
	}

	.page .card-head [id] {
		scroll-margin-top: 5.75rem;
	}
}

/* A user's name (ui.UserName): the avatar, then the username over the
   display name. */
.user-name {
	display: inline-flex;
	align-items: center;
	gap: 0.625rem;
	max-width: 100%;
	vertical-align: middle;
}

.user-name-text {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.user-name-text .muted {
	font-size: var(--text-sm);
	font-weight: 400;
}

.avatar[data-initials]::before {
	content: attr(data-initials);
}

/* Pagination. */
.pager {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-3);
	margin: 0 0 var(--block-gap);
	color: var(--muted);
	font-size: var(--text-sm);
}

/* Forms: fields with their label, control, error and help. */
.form {
	max-width: 35rem;
}

.field {
	display: flex;
	flex-direction: column;
	gap: 0.375rem;
	margin: 0 0 var(--space-4);
}

.field-help,
.field-error {
	margin: 0;
	font-size: var(--text-sm);
}

.field-help {
	color: var(--muted);
}

.field-error {
	display: flex;
	align-items: center;
	gap: 0.375rem;
	color: var(--danger);
	font-weight: 600;
}

.input,
.select,
.textarea {
	width: 100%;
	min-height: var(--control-height);
}

.textarea {
	min-height: 6rem;
}

[aria-invalid="true"] {
	border-color: var(--danger);
	box-shadow: 0 0 0 1px var(--danger);
}

.check {
	display: flex;
	align-items: flex-start;
	gap: 0.625rem;
}

.check input {
	flex: none;
	width: 1.125rem;
	height: 1.125rem;
	margin: 0.125rem 0 0;
}

.check + .field-help,
.check + .field-error {
	padding-left: 1.75rem;
}

.form-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
	padding-top: var(--space-1);
}

/* Confirmation of an action. */
.confirm {
	max-width: 40rem;
}

.confirm.danger {
	border-color: var(--danger);
}

/* Usage of a limit: a progress bar, styled per engine. */
.usage {
	display: flex;
	flex-direction: column;
	gap: 0.375rem;
	margin: 0 0 var(--space-3);
}

.usage-row {
	display: flex;
	justify-content: space-between;
	gap: 0.625rem;
	font-size: var(--text-sm);
}

.usage progress {
	width: 100%;
	height: 0.5rem;
	border: none;
	border-radius: var(--radius-pill);
	overflow: hidden;
	background: var(--surface-2);
	color: var(--accent);
	appearance: none;
}

.usage progress::-webkit-progress-bar {
	background: var(--surface-2);
}

.usage progress::-webkit-progress-value {
	border-radius: var(--radius-pill);
	background: var(--accent);
}

.usage progress::-moz-progress-bar {
	border-radius: var(--radius-pill);
	background: var(--accent);
}

.usage.high progress {
	color: var(--warn);
}

.usage.high progress::-webkit-progress-value {
	background: var(--warn);
}

.usage.high progress::-moz-progress-bar {
	background: var(--warn);
}

/* Tables (ui.Table in table.templ): the look, the kinds of columns,
   rows of details, the menu of a row's actions, and the cards a phone
   shows in their place. The frame scrolls both ways within the
   window's height, so the header, sticky at its top, stays in view
   along a long list; a page's own scrolling would not keep it there,
   since a frame that scrolls sideways keeps its sticky cells within
   itself. The frame is positioned so that it holds the boxes placed
   absolutely in its rows (the checkbox of DetailsToggle, the hidden
   names of buttons): held by the page instead, they would stay where
   the rows were before the frame scrolled, stretch the page far below
   the frame and, once focused by a click, scroll it there. */
.tbl-wrap {
	position: relative;
	max-height: calc(100vh - 4rem);
	margin: 0 0 var(--block-gap);
	overflow: auto;
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	background: var(--surface);
}

table.tbl {
	width: 100%;
	border-collapse: separate;
	border-spacing: 0;
	font-size: var(--text-sm);
}

.tbl th {
	padding: 0.5625rem 0.875rem;
	border-bottom: 1px solid var(--border);
	background: var(--surface-2);
	color: var(--muted);
	font-size: var(--text-xs);
	font-weight: 600;
	text-align: left;
	white-space: nowrap;
}

.tbl td {
	padding: 0.6875rem 0.875rem;
	border-bottom: 1px solid var(--border);
	vertical-align: middle;
}

.tbl tbody tr:last-child td {
	border-bottom: 0;
}

.tbl tbody tr:hover > * {
	background: var(--surface-2);
}

.tbl thead th {
	position: sticky;
	top: 0;
	z-index: 2;
}

/* A cell that heads its row looks like the others. */
.tbl tbody th {
	padding: 0.6875rem 0.875rem;
	border-bottom: 1px solid var(--border);
	background: var(--surface);
	color: var(--text);
	font-size: var(--text-sm);
	font-weight: 600;
	text-align: left;
	vertical-align: middle;
}

.tbl tbody tr:last-child > * {
	border-bottom: 0;
}

/* The kinds of columns. */
.c-short,
.c-id,
.c-check,
.c-toggle,
.c-actions {
	white-space: nowrap;
}

/* The time of a security record (WhenExact): to the second with its
   zone, which wraps under the time where the column is narrow rather
   than widen it, even in a column that never wraps. */
.when-exact {
	white-space: normal;
	overflow-wrap: break-word;
}

.when-at {
	white-space: nowrap;
}

.when-zone {
	color: var(--muted);
	font-weight: 400;
}

.tbl td.c-id,
.tbl tbody th.c-id {
	font-family: var(--font-mono);
	font-size: var(--text-sm);
}

.c-text {
	min-width: 12rem;
	overflow-wrap: anywhere;
}

.tbl .c-num {
	text-align: right;
	white-space: nowrap;
	font-variant-numeric: tabular-nums;
}

.c-check,
.c-toggle,
.tbl .c-actions {
	width: 1%;
}

.tbl .c-actions {
	text-align: right;
}

.trunc {
	display: inline-block;
	max-width: 18rem;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	vertical-align: bottom;
}

.clamp {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 3;
	line-clamp: 3;
	overflow: hidden;
}

.tbl .btn-icon {
	width: 2rem;
	min-height: 2rem;
}

.tbl form {
	margin: 0;
}

.tbl th abbr {
	text-decoration: none;
	cursor: help;
}

/* A wide table's first column stays in view as the frame scrolls
   sideways. */
.tbl-wide tr > :first-child {
	position: sticky;
	left: 0;
	z-index: 1;
	border-right: 1px solid var(--border);
}

.tbl-wide tbody tr > :first-child {
	background: var(--surface);
}

.tbl-wide tbody tr:hover > :first-child {
	background: var(--surface-2);
}

.tbl-wide thead tr > :first-child {
	z-index: 3;
}

/* Rows of details, opened by the checkbox of DetailsToggle. Where the
   browser lacks :has, every row of details shows. */
.tbl-toggle {
	position: absolute;
	width: 1px;
	height: 1px;
	opacity: 0;
}

.tbl-toggle:focus-visible + label {
	outline: var(--focus-ring);
	outline-offset: 2px;
}

.tbl-toggle + label .icon {
	transition: transform 0.15s;
}

.tbl-toggle:checked + label .icon {
	transform: rotate(90deg);
}

.tbl tbody tr.tbl-details > td {
	background: var(--surface-2);
}

@supports selector(:has(*)) {
	.tbl-details {
		display: none;
	}

	.tbl tr:has(.tbl-toggle:checked) + .tbl-details {
		display: table-row;
	}

	.tbl tr:has(.tbl-toggle:checked) > * {
		border-bottom: 0;
		background: var(--surface-2);
	}
}

.tbl-details .kv {
	max-width: 56rem;
	margin: 0;
}

.tbl-details dd {
	overflow-wrap: anywhere;
	font-family: var(--font-mono);
	font-size: var(--text-sm);
}

/* The menu of a row's actions (ui.RowActions): a <details>. While one
   is open, its table's frame stops clipping, so that the menu shows in
   full below the last rows too. */
.tbl-wrap:has(.menu[open]) {
	max-height: none;
	overflow: visible;
}

.menu {
	position: relative;
	display: inline-block;
	text-align: left;
}

.menu > summary {
	list-style: none;
}

.menu > summary::-webkit-details-marker {
	display: none;
}

.menu[open] > summary {
	background: var(--surface-2);
	color: var(--text);
}

.menu-list {
	position: absolute;
	top: 100%;
	right: 0;
	z-index: 4;
	display: grid;
	min-width: 12rem;
	padding: var(--space-1);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface);
	box-shadow: var(--shadow-lg);
}

.menu-item {
	width: 100%;
	padding: var(--space-2) var(--space-3);
	border: 0;
	border-radius: var(--radius-sm);
	background: transparent;
	color: var(--text);
	font-size: var(--text-sm);
	text-align: left;
	white-space: nowrap;
}

.menu-item:hover {
	border: 0;
	background: var(--surface-2);
}

.menu-item.btn-danger {
	color: var(--danger);
}

/* Pagination with a filter above the list, which HTMX sends as it is
   typed into. */
.tbl-filter {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: var(--space-3);
	margin: 0 0 var(--block-gap);
}

.tbl-filter .field {
	flex: 0 1 18rem;
	min-width: 10rem;
	margin: 0;
}

/* On a phone each row is a card: the first cell the title, the others
   a label (the column's, from data-label) beside the value, which wraps
   rather than being cut short. */
@media (max-width: 40rem) {
	.tbl-wrap {
		max-height: none;
		overflow: visible;
		border: 0;
		background: none;
	}

	.tbl,
	.tbl thead,
	.tbl tbody,
	.tbl tr,
	.tbl th,
	.tbl td {
		display: block;
	}

	.tbl thead {
		position: absolute;
		width: 1px;
		height: 1px;
		overflow: hidden;
		clip: rect(0 0 0 0);
	}

	.tbl tbody tr {
		position: relative;
		margin: 0 0 var(--space-3);
		padding: var(--space-3) var(--space-4);
		border: 1px solid var(--border);
		border-radius: var(--radius-xl);
		background: var(--surface);
	}

	.tbl tbody tr > * {
		padding: var(--space-1) 0;
		text-align: left;
		white-space: normal;
	}

	.tbl tbody tr > *,
	.tbl tbody tr:hover > *,
	.tbl tbody tr:has(.tbl-toggle:checked) > * {
		border: 0;
		background: none;
	}

	.tbl tbody tr > [data-label] {
		position: relative;
		padding-left: calc(35% + var(--space-3));
	}

	/* A number lines up with the other values, its label with the
	   other labels, not right of them. */
	.tbl tbody tr > .c-num {
		text-align: left;
	}

	.tbl tbody tr > [data-label]::before {
		content: attr(data-label);
		position: absolute;
		left: 0;
		width: 35%;
		overflow: hidden;
		color: var(--muted);
		font-family: var(--font-sans);
		font-size: var(--text-xs);
		font-weight: 600;
		line-height: 1.8;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	/* The first cell is the card's title, beside the checkbox or the
	   button of details, which sit in its corner. */
	.tbl tbody tr > :first-child,
	.tbl tbody tr > .c-check + *,
	.tbl tbody tr > .c-toggle + * {
		display: block;
		padding-left: 0;
		padding-right: 2.5rem;
		font-size: var(--text-base);
		font-weight: 600;
	}

	.tbl tbody tr > :first-child::before,
	.tbl tbody tr > .c-check + *::before,
	.tbl tbody tr > .c-toggle + *::before {
		content: none;
	}

	.tbl tbody tr > .c-check,
	.tbl tbody tr > .c-toggle {
		position: absolute;
		top: var(--space-2);
		right: var(--space-2);
		z-index: 1;
		width: auto;
		padding: 0;
	}

	.tbl td:empty {
		display: none;
	}

	.tbl .trunc {
		display: inline;
		max-width: none;
		white-space: normal;
		overflow-wrap: anywhere;
	}

	/* A card shows a long text whole too: a phone has no title to
	   hover for the lines Clamp leaves out. */
	.tbl .clamp {
		display: inline;
		overflow: visible;
		-webkit-line-clamp: none;
		line-clamp: none;
	}

	.tbl .c-actions {
		display: flex;
		justify-content: flex-start;
	}

	.tbl .menu-list {
		left: 0;
		right: auto;
	}

	@supports selector(:has(*)) {
		.tbl tbody tr.tbl-details {
			display: none;
		}

		.tbl tr:has(.tbl-toggle:checked) + .tbl-details {
			display: block;
		}
	}

	.tbl tbody tr.tbl-details {
		margin-top: calc(-1 * var(--space-3) - 1px);
		border-top: 0;
		border-top-left-radius: 0;
		border-top-right-radius: 0;
		background: var(--surface-2);
	}

	.tbl tbody tr.tbl-details > td {
		padding-right: 0;
		font-size: var(--text-sm);
		font-weight: 400;
	}

	.tbl tbody tr:has(.tbl-toggle:checked) {
		border-bottom-left-radius: 0;
		border-bottom-right-radius: 0;
	}

	.tbl-details .kv {
		grid-template-columns: 1fr;
	}

	.tbl-wide tr > :first-child,
	.tbl-wide tbody tr:hover > :first-child {
		position: static;
		border-right: 0;
		background: none;
	}
}

/* An avatar: initials in a circle. */
.avatar {
	display: grid;
	flex: none;
	place-items: center;
	width: 2rem;
	height: 2rem;
	border-radius: 50%;
	background: var(--accent-soft);
	color: var(--accent-text);
	font-size: var(--text-xs);
	font-weight: 700;
}

/* The frame (ui.Layout): before login, the name above a card in the
   middle of the window; after it, the sidebar with the navigation, the
   top bar with the breadcrumbs, and the page. */
.brand {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	padding: 0 var(--space-2);
	color: var(--text);
	font-size: var(--text-lg);
	font-weight: 700;
	letter-spacing: -0.01em;
	text-decoration: none;
}

.brand .icon {
	width: 1.375rem;
	height: 1.375rem;
	color: var(--accent);
}

.bare {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-6);
	min-height: 100vh;
	padding: var(--space-8) var(--space-4);
}

.bare .brand {
	font-size: var(--text-xl);
}

.bare-main {
	width: 100%;
	max-width: 30rem;
	padding: var(--space-7);
	border: 1px solid var(--border);
	border-radius: var(--radius-xl);
	background: var(--surface);
}

.bare-main:focus {
	outline: none;
}

.bare-main > :first-child {
	margin-top: 0;
}

.bare-main > :last-child {
	margin-bottom: 0;
}

/* The sidebar keeps to the window's height as the page scrolls; the
   shell's background continues its column below it. */
.shell {
	display: grid;
	grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
	min-height: 100vh;
	background: linear-gradient(to right, var(--surface) calc(var(--sidebar-width) - 1px), var(--border) 0 var(--sidebar-width), transparent 0);
}

.side {
	position: sticky;
	top: 0;
	display: flex;
	flex-direction: column;
	gap: 1.375rem;
	height: 100vh;
	padding: 1.125rem var(--space-3);
	overflow-y: auto;
	border-right: 1px solid var(--border);
	background: var(--surface);
}

.side-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
}

.nav-title {
	margin: 0;
	padding: 0 0.625rem var(--space-1);
	color: var(--muted);
	font-size: var(--text-xs);
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
}

.nav-list {
	display: flex;
	flex-direction: column;
	gap: 2px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.nav-list li {
	position: relative;
}

.nav-list a {
	display: flex;
	align-items: center;
	min-height: 2.25rem;
	padding: 0 0.625rem 0 2.375rem;
	border-radius: var(--radius);
	color: var(--text);
	font-weight: 500;
	text-decoration: none;
}

.nav-list a:hover {
	background: var(--surface-2);
}

.nav-list a[aria-current] {
	background: var(--accent-soft);
	color: var(--accent-text);
	font-weight: 650;
}

/* The icon follows its link in the markup and is drawn over the
   link's start; clicks go through it to the link. */
.nav-list .icon {
	position: absolute;
	top: 50%;
	left: 0.625rem;
	margin-top: -0.5625rem;
	color: var(--muted);
	pointer-events: none;
}

.nav-list a[aria-current] + .icon {
	color: var(--accent-text);
}

@media (forced-colors: active) {
	.nav-list a[aria-current] {
		outline: 2px solid;
	}
}

.side-foot {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	margin-top: auto;
	padding: 0.875rem var(--space-2) 0;
	border-top: 1px solid var(--border);
}

.who {
	flex: 1;
	min-width: 0;
}

.who b,
.who span {
	display: block;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.who span {
	color: var(--muted);
	font-size: var(--text-xs);
}

.side-foot form {
	margin: 0;
}

/* The theme (ui.themeSwitch, ui.AppearancePage): in the sidebar's foot
   a row of its own above who is logged in, three buttons in a track,
   the chosen one raised; on the appearance page a choice per row, the
   chosen one in the accent. */
.side-foot {
	flex-wrap: wrap;
}

.side-foot .theme-switch {
	display: flex;
	flex-basis: 100%;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-2);
	padding: 0 0 var(--space-1);
}

.theme-switch-label {
	color: var(--muted);
	font-size: var(--text-xs);
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
}

.theme-switch-options {
	display: inline-flex;
	gap: 2px;
	padding: 2px;
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface-2);
}

.theme-switch .btn-icon {
	width: 2rem;
	min-height: 1.75rem;
	border-radius: var(--radius-sm);
}

.theme-switch .btn-icon[aria-pressed="true"] {
	border-color: var(--border);
	background: var(--surface);
	box-shadow: var(--shadow-sm);
	color: var(--accent-text);
}

.theme-choices {
	display: grid;
	gap: var(--space-2);
	margin: 0 0 var(--space-4);
	padding: 0;
	border: 0;
}

.theme-choice {
	display: flex;
	align-items: flex-start;
	gap: var(--space-3);
	padding: var(--space-3) var(--space-4);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface);
	font-weight: 400;
	cursor: pointer;
}

.theme-choice:has(input:checked) {
	border-color: var(--accent);
	background: var(--accent-soft);
}

.theme-choice input {
	flex: none;
	width: 1.125rem;
	height: 1.125rem;
	margin: 0.125rem 0 0;
}

.theme-choice .icon {
	flex: none;
	margin-top: 0.0625rem;
	color: var(--muted);
}

.theme-choice-text b,
.theme-choice-text span {
	display: block;
}

.theme-choice-text span {
	color: var(--muted);
	font-size: var(--text-sm);
}

/* The time zone's list on the appearance page (static/timezone.js): its
   search field is hidden until the script runs, as without it typing
   into the field would do nothing; the list shows ten zones and
   scrolls. */
.zone-filter {
	display: none;
}

.zone-ready .zone-filter {
	display: block;
}

.select.zone-list {
	height: auto;
	padding: var(--space-1);
}

/* The style guide's colors of each theme, side by side: each part sets
   its own data-theme, and so its tokens. */
.sg-themes {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
	gap: var(--space-4);
}

.sg-theme {
	display: grid;
	gap: var(--space-4);
	padding: var(--space-4);
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	background: var(--bg);
	color: var(--text);
}

.sg-theme-name {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	margin: 0;
	font-weight: 600;
}

.content {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.topbar {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	min-height: 3.5rem;
	padding: var(--space-2) var(--space-7);
	border-bottom: 1px solid var(--border);
	background: var(--surface);
}

.crumbs ol {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin: 0;
	padding: 0;
	color: var(--muted);
	font-size: var(--text-sm);
	list-style: none;
}

.crumbs li + li::before {
	content: "/";
	margin-right: var(--space-2);
}

.crumbs a {
	color: var(--muted);
	text-decoration: none;
}

.crumbs a:hover {
	color: var(--text);
	text-decoration: underline;
}

.crumbs [aria-current] {
	color: var(--text);
	font-weight: 600;
}

.menu-open,
.side-close,
.topbar-brand,
.topbar-account,
.scrim {
	display: none;
}

.page {
	width: 100%;
	max-width: 77.5rem;
	padding: 1.625rem var(--space-7) var(--space-8);
}

.page:focus {
	outline: none;
}

.page > h1:first-child {
	margin-top: 0;
}

.page.wide {
	max-width: none;
}

/* On a phone or a narrow window the sidebar becomes a drawer over the
   page, shown while it is the URL's target (#nav), and the top bar
   carries the menu button, the name and the account. */
@media (max-width: 60rem) {
	.shell {
		display: block;
		background: none;
	}

	.side {
		position: fixed;
		top: 0;
		bottom: 0;
		left: 0;
		z-index: 30;
		width: min(18.75rem, 85vw);
		height: auto;
		padding: var(--space-2) var(--space-3) var(--space-4);
		box-shadow: var(--shadow-lg);
		transform: translateX(-100%);
		visibility: hidden;
		transition: transform 0.2s ease, visibility 0.2s;
	}

	.side:target {
		transform: none;
		visibility: visible;
	}

	.side:target + .scrim {
		position: fixed;
		inset: 0;
		z-index: 20;
		display: block;
		background: var(--scrim);
	}

	.nav-list a {
		min-height: 2.75rem;
	}

	.side-close,
	.menu-open,
	.topbar-account {
		display: inline-flex;
		width: 2.75rem;
		min-height: 2.75rem;
		color: var(--text);
	}

	.topbar-brand {
		display: flex;
		flex: 1;
		padding: 0;
	}

	.topbar {
		position: sticky;
		top: 0;
		z-index: 10;
		padding: var(--space-2) var(--space-2) var(--space-2) var(--space-1);
	}

	.crumbs {
		display: none;
	}

	.page {
		padding: var(--space-4) var(--space-4) var(--space-6);
	}
}

/* The menu search (internal/web/ui/search.templ). In the top bar a
   field at its end, with the list of suggestions that HTMX fills under
   it and search.js moves through; on a phone a link to /search in its
   place. /search and its choice of a domain list their links plainly. */
.topsearch {
	position: relative;
	display: flex;
	flex: 0 1 20rem;
	align-items: center;
	min-width: 12rem;
	margin: 0 0 0 auto;
}

.topsearch > .icon {
	position: absolute;
	left: 0.75rem;
	color: var(--muted);
	pointer-events: none;
}

.topsearch input {
	width: 100%;
	min-height: 2.375rem;
	padding: 0 2.25rem 0 2.375rem;
	background: var(--bg);
}

.topsearch-key {
	position: absolute;
	right: 0.625rem;
	padding: 0 0.375rem;
	border: 1px solid var(--border);
	border-radius: var(--radius-sm);
	color: var(--muted);
	font-size: var(--text-xs);
	pointer-events: none;
}

.topsearch input:focus + .topsearch-key,
.topsearch input:not(:placeholder-shown) + .topsearch-key {
	display: none;
}

.topsearch-options {
	position: absolute;
	top: calc(100% + var(--space-1));
	right: 0;
	z-index: 40;
	width: max(100%, 24rem);
	max-width: calc(100vw - 2 * var(--space-4));
	max-height: min(28rem, 70vh);
	overflow-y: auto;
	padding: var(--space-1);
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	background: var(--surface);
	box-shadow: var(--shadow-lg);
}

.search-option,
.search-none {
	display: flex;
	flex-direction: column;
	gap: 2px;
	padding: var(--space-2) var(--space-3);
	border-radius: var(--radius);
	color: var(--text);
	text-decoration: none;
}

.search-option:hover,
.search-option[aria-selected="true"] {
	background: var(--accent-soft);
	color: var(--accent-text);
}

.search-option-title {
	font-weight: 600;
}

.search-option-place {
	color: var(--muted);
	font-size: var(--text-xs);
}

.search-none {
	color: var(--muted);
}

@media (forced-colors: active) {
	.search-option[aria-selected="true"] {
		outline: 2px solid;
	}
}

.topbar-search {
	display: none;
}

@media (max-width: 60rem) {
	.topsearch {
		display: none;
	}

	.topbar-search {
		display: inline-flex;
		width: 2.75rem;
		min-height: 2.75rem;
		color: var(--text);
	}
}

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

ul.search-list li {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--space-1) var(--space-3);
	padding: var(--space-3) 0;
	border-bottom: 1px solid var(--border);
}

ul.search-list li:last-child {
	border-bottom: 0;
}

ul.search-list a {
	font-weight: 600;
}

/* The shared pages: the list of mailboxes (dashboard, account) and the
   error page. */
ul.mailboxes {
	margin: 0;
	padding: 0;
	list-style: none;
}

ul.mailboxes li {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-3);
	padding: var(--space-3) 0;
	border-bottom: 1px solid var(--border);
	overflow-wrap: anywhere;
}

ul.mailboxes li:last-child {
	border-bottom: 0;
}

ul.mailboxes .webmail-open {
	margin-left: auto;
}

.error-page {
	display: flex;
	flex-direction: column;
	align-items: center;
	max-width: 36rem;
	margin: var(--space-8) auto;
	text-align: center;
}

.bare-main .error-page {
	margin: 0 auto;
}

.error-page > .icon {
	width: 2.5rem;
	height: 2.5rem;
	color: var(--muted);
	stroke-width: 1.5;
}

.error-page h1 {
	margin: var(--space-2) 0;
}

.error-page p {
	margin: var(--space-2) 0;
}

.error-page .error-status {
	margin-bottom: 0;
	color: var(--muted);
	font-size: var(--text-sm);
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
}

/* The admin pages: the title over each group of a list, and a group
   of choices (radio buttons) that is a field. */
.list-title {
	margin: var(--space-6) 0 var(--space-3);
	font-size: var(--text-md);
}

fieldset.choices {
	min-width: 0;
	padding: 0;
	border: 0;
}

fieldset.choices legend {
	margin-bottom: 0.375rem;
	padding: 0;
	font-weight: 600;
}

/* A file to upload, its button as the others. */
.input[type="file"] {
	padding: 0.375rem;
}

.input[type="file"]::file-selector-button {
	margin-right: var(--space-3);
	padding: 0.25rem 0.75rem;
	border: 1px solid var(--border);
	border-radius: var(--radius-sm);
	background: var(--surface-2);
	color: var(--text);
	font: inherit;
	font-weight: 600;
	cursor: pointer;
}

/* Text that is code to copy, such as a zone file's lines, which wrap
   anywhere rather than widen the page. */
.code-block {
	margin: 0 0 var(--space-2);
	padding: var(--space-3);
	white-space: pre-wrap;
	overflow-wrap: anywhere;
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface-2);
	font-size: var(--text-sm);
}

/* What a cell of a table says of its value, under it. */
.cell-note {
	margin: var(--space-1) 0 0;
	color: var(--muted);
	font-size: var(--text-sm);
}

/* The filter of a review page, many fields that share the rows, two
   even on a phone, rather than each taking one. */
.tbl-filter.filter-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 10rem), 1fr));
}

.tbl-filter.filter-grid .field {
	min-width: 0;
}

.tbl-filter.filter-grid .btn {
	justify-self: start;
}

/* A part of a card shown when asked for, such as a message's header. */
.disclosure {
	margin: 0 0 var(--space-3);
}

.disclosure > summary {
	color: var(--accent-text);
	font-weight: 600;
	cursor: pointer;
}

/* A part of a card under a title of its own, such as the open order
   under a certificate. */
.card-part {
	margin: var(--space-6) 0 var(--space-3);
	font-size: var(--text-base);
}

/* The user a card is about, above what it says of them. */
.card-user {
	margin: 0 0 var(--space-4);
}

/* An empty list within a card takes less room than one on the page. */
.card .empty {
	padding: var(--space-6) var(--space-4);
}

/* A second form under a card's first, which does the same another way,
   such as mailing a reset code in place of setting a password. */
.form-aside {
	margin-top: var(--space-5);
	padding-top: var(--space-4);
	border-top: 1px solid var(--border);
}

.form-aside p {
	margin-top: 0;
}

/* The forms in a row of a table, such as those that set and remove a
   limit: a short field beside its small button. */
.row-forms {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
}

.row-form {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
}

.row-forms.form-aside {
	gap: var(--space-3) var(--space-6);
}

.row-form .input {
	flex: 0 1 9rem;
	width: 9rem;
	min-width: 4rem;
	min-height: 2rem;
	padding: 0.25rem 0.5rem;
}

/* A bar of a limit's use in a table's row. */
.tbl .usage {
	min-width: 12rem;
	margin: 0;
}

/* The field of a new address's name with the choice of its domain
   after it, as a new catch pattern's has its domain. */
.pattern-local .select {
	flex: 1;
	width: auto;
	min-width: 0;
}

/* The style guide (ui.StyleGuidePage). */
.sg-swatches,
.sg-icons {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
	gap: var(--space-3);
	margin: 0;
	padding: 0;
	list-style: none;
}

.sg-swatches li,
.sg-icons li {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	font-size: var(--text-sm);
}

.sg-icons {
	grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
}

.sg-swatch {
	flex: none;
	width: 2rem;
	height: 2rem;
	border: 1px solid var(--border);
	border-radius: var(--radius-sm);
}

.swatch-bg {
	background: var(--bg);
}

.swatch-surface {
	background: var(--surface);
}

.swatch-surface-2 {
	background: var(--surface-2);
}

.swatch-border {
	background: var(--border);
}

.swatch-control-border {
	background: var(--control-border);
}

.swatch-text {
	background: var(--text);
}

.swatch-muted {
	background: var(--muted);
}

.swatch-accent {
	background: var(--accent);
}

.swatch-accent-hover {
	background: var(--accent-hover);
}

.swatch-accent-soft {
	background: var(--accent-soft);
}

.swatch-accent-text {
	background: var(--accent-text);
}

.swatch-ok-bg {
	background: var(--ok-bg);
}

.swatch-ok {
	background: var(--ok);
}

.swatch-warn-bg {
	background: var(--warn-bg);
}

.swatch-warn {
	background: var(--warn);
}

.swatch-danger-bg {
	background: var(--danger-bg);
}

.swatch-danger {
	background: var(--danger);
}

.swatch-info-bg {
	background: var(--info-bg);
}

.swatch-info {
	background: var(--info);
}

.swatch-focus {
	background: var(--focus);
}

.sg-type-2xl {
	font-size: var(--text-2xl);
}

.sg-type-xl {
	font-size: var(--text-xl);
}

.sg-type-lg {
	font-size: var(--text-lg);
}

.sg-type-md {
	font-size: var(--text-md);
}

.sg-type-sm {
	font-size: var(--text-sm);
}

.sg-type-xs {
	font-size: var(--text-xs);
}

.card > .actions + .actions {
	margin-top: var(--space-3);
}

/* The older pages' own classes, until they move to the components. */
form.inline {
	display: inline-flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	margin: 0 0.5rem 0.25rem 0;
}

/* What HTMX would otherwise insert as an inline style. */
.htmx-indicator {
	opacity: 0;
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
	opacity: 1;
	transition: opacity 200ms ease-in;
}

/* A boosted page on its way (static/navigation.js). */
body[aria-busy="true"] {
	cursor: progress;
}

/* The permission pages: a grant in a cell of a matrix, beside the
   button that revokes it. */
.perm-cell {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
}

/* The pages before login (the bare frame's card) and the account
   pages. In the card, the header is smaller and the form's buttons as
   wide as the card, as the approved login shows; a link may sit in a
   field's label row (the login's "Forgot password?"), and the links
   that lead elsewhere follow the form. */
.bare-main .page-head {
	margin-bottom: var(--space-5);
}

.bare-main .page-head h1 {
	font-size: var(--text-xl);
}

.bare-main .form {
	max-width: none;
}

.bare-main .form-actions > .btn {
	flex: 1;
}

.field-row {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-1) var(--space-3);
}

.field-row a {
	font-size: var(--text-sm);
}

/* The login's choice of staying logged in: the days typed in a short
   field within its choice's line, which wraps on a phone. */
.keep-days {
	flex-wrap: wrap;
	align-items: center;
}

.keep-days input[type="radio"] {
	margin: 0;
}

.keep-days .input {
	width: 5rem;
	height: auto;
	margin: 0;
}

/* The Security page's web sessions: how long each lasts, under its
   device. */
.session-lasts {
	display: block;
	color: var(--muted);
	font-size: var(--text-sm);
}

.bare-links {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--space-2) var(--space-5);
	margin: var(--space-5) 0 0;
	font-size: var(--text-sm);
}

.steps {
	margin: 0 0 var(--space-4);
	padding-left: 1.25rem;
}

.steps li + li {
	margin-top: var(--space-1);
}

/* A mailbox's page: the setting of keeping a copy of forwarded mail,
   a line of text with its button, above the form that adds an
   address. */
.keep-copy {
	margin: 0 0 var(--space-4);
}

/* Setting TOTP up: the QR code beside the key, or above it in a narrow
   column. The code stays dark on light, as scanners expect, whatever
   the theme. */
.totp-setup {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-4) var(--space-6);
	margin: 0 0 var(--space-5);
}

svg.qr {
	display: block;
	flex: none;
	width: 12rem;
	max-width: 100%;
	height: auto;
	border: 1px solid var(--border);
	border-radius: var(--radius);
}

/* Secrets shown once (a TOTP key, recovery codes, an app password),
   large and in the monospace font, to be copied or typed: a value
   with its label and copy button, or a list of codes in columns. */
.secret-field {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-3);
	min-width: 0;
}

.secret-label {
	flex-basis: 100%;
	color: var(--muted);
	font-size: var(--text-sm);
	font-weight: 600;
}

.secret,
.secret-list code {
	padding: var(--space-1) var(--space-2);
	border: 1px solid var(--border);
	border-radius: var(--radius-sm);
	background: var(--surface-2);
	font-size: var(--text-md);
	letter-spacing: 0.06em;
	overflow-wrap: anywhere;
}

.secret span + span {
	margin-left: 0.5em;
}

/* A value to type into a mail client (ui.setupValue), such as its
   server: a field of its own with its copy button. */
.setup-value {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
	margin: 0 0 var(--space-2);
}

.setup-value:last-child {
	margin-bottom: 0;
}

.setup-value code {
	min-width: 0;
	padding: var(--space-1) var(--space-2);
	border: 1px solid var(--border);
	border-radius: var(--radius-sm);
	background: var(--surface-2);
	overflow-wrap: anywhere;
}

.secret-list {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(13.5rem, 1fr));
	gap: var(--space-2) var(--space-4);
	max-width: 32rem;
	margin: 0 0 var(--space-4);
	padding-left: 1.75rem;
	font-family: var(--font-mono);
}

.secret-list li::marker {
	color: var(--muted);
	font-size: var(--text-sm);
}

/* On paper (the print buttons of copy.js, or the browser's own) a page
   is its content alone, dark on white whatever the theme: without the
   frame, the tabs, the forms and the buttons, which do nothing there,
   and where it shows a secret, without the cards around it. */
@media print {
	.side,
	.topbar,
	.tabs,
	.skip,
	form,
	.btn,
	button {
		display: none !important;
	}

	.shell {
		display: block;
		background: none;
	}

	.page,
	.bare {
		padding: 0;
	}

	*,
	*::before,
	*::after,
	li::marker {
		border-color: #999 !important;
		background: transparent !important;
		color: #000 !important;
		box-shadow: none !important;
	}

	html,
	[data-theme] {
		color-scheme: light !important;
	}

	html {
		background: #fff !important;
	}

	/* A page that shows a secret prints the secret's card alone. */
	body:has(.secret, .secret-list) .card:not(:has(.secret, .secret-list)) {
		display: none !important;
	}
}

/* The webmail's mailbox (ui.WebmailPage; design.md section 3, Webmail:
   mobile). On a phone it shows one view at a time, the folders, the
   list or the message, with links between them, in the page's own
   scrolling. From 48rem it fills the window below the top bar, and its
   panes, cards side by side, scroll on their own: the list beside the
   message once one is open, else the folders beside the list, and from
   80rem all three.

   The message's HTML is in a sandboxed frame. No script runs in it,
   and the page cannot read its document, which has an origin of its
   own, so nothing can size the frame to its content: it takes what is
   left of its pane on a wide screen, and on a phone the screen's
   height between the top bar and the bar of actions, so that once
   scrolled to, it is the only thing that scrolls. The sent archive
   shows a message in the same frame, outside the mailbox. */
.webmail-frame {
	display: block;
	width: 100%;
	height: 70vh;
	margin: 1rem 0;
	border: 1px solid var(--line);
	background: #ffffff;
}

pre.webmail-text {
	margin: 1rem 0;
	white-space: pre-wrap;
	overflow-wrap: anywhere;
	font-size: 0.95rem;
}

.webmail {
	/* What the frame of the page takes above and below the mailbox:
	   the top bar and the page's padding (ui.Layout). */
	--webmail-above: calc(3.5rem + 1.625rem + 2.5rem);
	/* The top bar on a phone, which stays at the top of the screen,
	   and the bar of a message's actions at its foot. */
	--webmail-topbar: calc(3.75rem + 1px);
	--webmail-bar: 3.75rem;
}

.webmail-head .page-head {
	margin-bottom: var(--space-4);
}

.webmail-pane {
	min-width: 0;
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	background: var(--surface);
}

.webmail-pane > h2,
.webmail-pane-head {
	margin: 0;
	padding: var(--space-3) var(--space-4);
	border-bottom: 1px solid var(--border);
}

.webmail-pane h2 {
	margin: 0;
	font-size: var(--text-md);
	overflow-wrap: anywhere;
}

.webmail-pane-head {
	display: flex;
	align-items: center;
	gap: var(--space-2);
}

.webmail-back {
	display: none;
}

.webmail-pane-head .webmail-back {
	margin-left: calc(-1 * var(--space-2));
}

.webmail .flash,
.webmail .callout,
.webmail .empty {
	margin: var(--space-3) var(--space-4);
}

.webmail-head .flash,
.webmail-head .callout {
	margin: 0 0 var(--space-3);
}

/* The folders: each a link as a whole, the folder shown marked as the
   navigation's page is, the unseen messages counted in a pill. */
.webmail-folder-nav {
	display: flex;
	flex-direction: column;
	gap: 2px;
	margin: 0;
	padding: var(--space-2);
	list-style: none;
}

.webmail-folder {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	min-height: 2.25rem;
	padding: 0 var(--space-3);
	border-radius: var(--radius);
	color: var(--text);
	font-weight: 500;
	text-decoration: none;
}

a.webmail-folder:hover {
	background: var(--surface-2);
}

.webmail-folder[aria-current] {
	background: var(--accent-soft);
	color: var(--accent-text);
	font-weight: 650;
}

@media (forced-colors: active) {
	.webmail-folder[aria-current] {
		outline: 2px solid;
	}
}

.webmail-folder-name,
.webmail-folder-use {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.webmail-folder-use {
	color: var(--muted);
	font-size: var(--text-xs);
	font-weight: 400;
}

.webmail-count {
	flex: none;
	margin-left: auto;
	padding: 0 0.4375rem;
	border-radius: var(--radius-pill);
	background: var(--accent);
	color: var(--on-accent);
	font-size: var(--text-xs);
	font-weight: 700;
	line-height: 1.5;
}

.webmail-folder-links {
	margin: 0;
	padding: var(--space-3) var(--space-4);
	border-top: 1px solid var(--border);
	list-style: none;
	font-size: var(--text-sm);
}

.webmail-folder-links li + li {
	margin-top: var(--space-2);
}

/* The list: the search, the orders, the bar that changes the messages
   chosen, and the messages, each a row whose link covers it, but for
   the box that chooses it. */
.webmail-search {
	margin: var(--space-3) var(--space-4) 0;
}

.webmail-search > summary {
	list-style: none;
}

.webmail-search > summary::-webkit-details-marker {
	display: none;
}

.webmail-search[open] > summary {
	background: var(--surface-2);
}

.webmail-search-form {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
	gap: 0 var(--space-3);
	max-width: none;
	margin-top: var(--space-3);
}

.webmail-search-form .form-actions {
	grid-column: 1 / -1;
	margin-bottom: var(--space-2);
}

.webmail-clear {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-1) var(--space-3);
	margin: var(--space-2) var(--space-4) 0;
	font-size: var(--text-sm);
}

.webmail-searched {
	overflow-wrap: anywhere;
}

.webmail-sorts {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-1) var(--space-3);
	margin: var(--space-3) var(--space-4);
	font-size: var(--text-sm);
}

.webmail-sorts a[aria-current] {
	font-weight: 650;
}

/* The bars of actions on the messages chosen and on the open message.
   A narrow pane keeps the actions used less in a menu (.webmail-narrow),
   a wide one shows them in the row (.webmail-wide). A browser without
   container queries keeps the menu. */
.webmail-bar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
}

.webmail-list {
	container: webmail-list / inline-size;
}

.webmail .webmail-message {
	container: webmail-message / inline-size;
}

.webmail-wide {
	display: none;
}

@container webmail-list (min-width: 32rem) {
	.webmail-list-bar .webmail-wide {
		display: inline-flex;
	}

	.webmail-list-bar .webmail-narrow {
		display: none;
	}
}

@container webmail-message (min-width: 40rem) {
	.webmail-message-bar .webmail-wide {
		display: inline-flex;
	}

	.webmail-message-bar .webmail-narrow {
		display: none;
	}
}

.webmail-more a.menu-item {
	text-decoration: none;
}

.webmail-list-bar {
	position: sticky;
	top: 0;
	z-index: 3;
	padding: var(--space-2) var(--space-4);
	border-top: 1px solid var(--border);
	background: var(--surface);
}

.webmail-move .menu-list {
	gap: var(--space-2);
	min-width: 14rem;
	padding: var(--space-3);
}

.webmail-move label {
	font-size: var(--text-sm);
}

ul.webmail-messages {
	margin: 0;
	padding: 0;
	border-top: 1px solid var(--border);
	list-style: none;
}

.webmail-msg {
	position: relative;
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto;
	align-items: start;
	gap: 0 var(--space-3);
	padding: var(--space-2) var(--space-4);
	border-bottom: 1px solid var(--border);
}

.webmail-msg:last-child {
	border-bottom: 0;
}

.webmail-msg.webmail-choosable {
	grid-template-columns: auto minmax(0, 1fr) auto;
	padding-left: 0;
}

.webmail-msg:hover {
	background: var(--surface-2);
}

.webmail-msg.webmail-current {
	background: var(--accent-soft);
	box-shadow: inset 3px 0 var(--accent);
}

/* The box that chooses a message is in a label as high as its row and
   wide enough for a thumb, above the link that covers the row. */
.webmail-check {
	position: relative;
	z-index: 1;
	display: flex;
	align-items: center;
	align-self: stretch;
	justify-content: center;
	width: 2.75rem;
	margin: calc(-1 * var(--space-2)) 0;
	cursor: pointer;
}

.webmail-check input {
	width: 1.125rem;
	height: 1.125rem;
	margin: 0;
}

.webmail-msg-link {
	display: grid;
	min-width: 0;
	color: var(--text);
	text-decoration: none;
}

.webmail-msg-link::after {
	content: "";
	position: absolute;
	inset: 0;
}

.webmail-msg-link:focus-visible {
	outline: none;
}

.webmail-msg-link:focus-visible::after {
	outline: var(--focus-ring);
	outline-offset: -2px;
}

.webmail-from,
.webmail-subject {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.webmail-from {
	font-weight: 500;
}

.webmail-subject {
	color: var(--muted);
}

.webmail-unseen .webmail-from,
.webmail-unseen .webmail-subject {
	color: var(--text);
	font-weight: 700;
}

/* A dot in the margin before the sender marks a message not seen yet:
   absolute, it stays where the sender starts, in its row. */
.webmail-unseen .webmail-from::before {
	content: "";
	position: absolute;
	width: 0.5rem;
	height: 0.5rem;
	margin: 0.45em 0 0 -0.875rem;
	border-radius: 50%;
	background: var(--accent);
}

@media (forced-colors: active) {
	.webmail-unseen .webmail-from::before {
		background: CanvasText;
	}
}

.webmail-meta {
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	color: var(--muted);
	font-size: var(--text-sm);
	line-height: 1.6;
	white-space: nowrap;
}

.webmail-date {
	font-variant-numeric: tabular-nums;
}

.webmail-flags {
	display: inline-flex;
	align-items: center;
	gap: var(--space-1);
}

.webmail-flags .badge {
	padding: 0 0.4375rem;
}

.webmail-total,
.webmail-list .pager,
.webmail-empty {
	margin: var(--space-3) var(--space-4);
}

/* The message: its head, with the bar of actions, the header fields,
   what is to know about its text and the attachments, above the text. */
.webmail-message-head {
	padding: var(--space-3) var(--space-4) 0;
}

.webmail-message-head > :first-child {
	margin-top: 0;
}

.webmail .webmail-message h2 {
	margin: 0 0 var(--space-3);
	font-size: var(--text-lg);
}

.webmail .webmail-message .webmail-back {
	margin: 0 0 var(--space-2) calc(-1 * var(--space-2));
}

.webmail .webmail-message .flash,
.webmail .webmail-message .callout {
	margin: 0 0 var(--space-3);
}

.webmail-message-bar {
	margin: 0 0 var(--space-3);
}

.webmail-headers {
	grid-template-columns: max-content minmax(0, 1fr);
	gap: var(--space-1) var(--space-3);
	margin: 0 0 var(--space-3);
	font-size: var(--text-sm);
}

.webmail-headers dd {
	margin: 0;
	font-weight: 500;
}

.webmail-alt {
	margin: 0 0 var(--space-3);
	font-size: var(--text-sm);
}

ul.webmail-attachments {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin: 0 0 var(--space-3);
	padding: 0;
	list-style: none;
}

ul.webmail-attachments li {
	display: inline-flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0 var(--space-2);
	max-width: 100%;
	min-height: 2.25rem;
	padding: var(--space-1) var(--space-3);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface-2);
	font-size: var(--text-sm);
}

ul.webmail-attachments a[download] {
	display: inline-flex;
	min-width: 0;
	max-width: calc(100% - 1.75rem);
	overflow: hidden;
	font-weight: 600;
	white-space: nowrap;
}

/* A file's name too long for its line is cut before its extension
   (fileName in webmail.templ), which stays in view. The name before it
   takes its direction from its first letter, so that one in Hebrew
   shows its start and is cut at its end; the extension still follows
   it on the right. */
.file-stem {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	unicode-bidi: plaintext;
}

.file-ext {
	flex: none;
}

ul.webmail-attachments .muted {
	white-space: nowrap;
}

.webmail .webmail-frame {
	margin: 0;
	border: 0;
	border-top: 1px solid var(--border);
	border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

.webmail pre.webmail-text {
	margin: 0;
	padding: var(--space-3) var(--space-4) var(--space-4);
	border-top: 1px solid var(--border);
}

/* The panes side by side, filling the window below the top bar. */
@media (min-width: 48rem) {
	.webmail {
		display: grid;
		grid-template-columns: minmax(9rem, 12rem) minmax(0, 1fr);
		grid-template-rows: auto minmax(0, 1fr);
		gap: 0 var(--space-4);
		height: calc(100vh - var(--webmail-above));
		height: calc(100dvh - var(--webmail-above));
		min-height: 28rem;
	}

	.webmail-head {
		grid-column: 1 / -1;
	}

	/* Each pane scrolls on its own, and is positioned so that it holds
	   the boxes placed absolutely in it (the hidden name of a folder's
	   count of unseen mail), as a table's frame does: held by the page,
	   they would stay where they were before the pane scrolled and
	   stretch the page below a long list of folders. */
	.webmail-pane {
		position: relative;
		overflow-y: auto;
	}

	.webmail-view-message {
		grid-template-columns: minmax(14rem, 18rem) minmax(0, 1fr);
	}

	.webmail-view-message .webmail-folders {
		display: none;
	}

	.webmail-view-message .webmail-list .webmail-back {
		display: inline-flex;
	}

	.webmail .webmail-message {
		display: flex;
		flex-direction: column;
	}

	.webmail-message-head {
		flex: none;
	}

	.webmail .webmail-frame {
		flex: 1 0 18rem;
		height: auto;
	}
}

/* A list wide enough shows each message on one line. */
@container webmail-list (min-width: 56rem) {
	.webmail-msg-link {
		grid-template-columns: minmax(8rem, 14rem) minmax(0, 1fr);
		gap: var(--space-4);
		align-items: baseline;
	}

	.webmail-meta {
		flex-direction: row-reverse;
		align-items: baseline;
		gap: var(--space-3);
	}
}

/* Without the sidebar, the top bar is higher and the padding less. */
@media (min-width: 48rem) and (max-width: 60rem) {
	.webmail {
		--webmail-above: calc(3.75rem + 1px + 1rem + 1.5rem);
	}
}

@media (min-width: 80rem) {
	.webmail-view-message {
		grid-template-columns: minmax(9rem, 11rem) minmax(15rem, 20rem) minmax(0, 1fr);
	}

	.webmail-view-message .webmail-folders {
		display: block;
	}

	.webmail-view-message .webmail-list .webmail-back {
		display: none;
	}
}

/* A phone: one view at a time, controls a thumb can hit, and the bars
   of actions at the foot of the screen. */
@media (max-width: 47.99rem) {
	.webmail-folders,
	.webmail-list,
	.webmail .webmail-message {
		display: none;
	}

	.webmail-view-folders .webmail-folders,
	.webmail-view-list .webmail-list,
	.webmail-view-message .webmail-message {
		display: block;
	}

	.webmail-back {
		display: inline-flex;
	}

	.webmail-head .page-head {
		flex-wrap: nowrap;
		align-items: center;
		margin-bottom: var(--space-3);
	}

	.webmail-head .page-head-text {
		flex: 1;
		min-width: 0;
	}

	.webmail-head h1 {
		font-size: var(--text-lg);
	}

	.webmail .btn,
	.webmail-folder {
		min-height: 2.75rem;
	}

	.webmail-folder-links li + li {
		margin-top: 0;
	}

	.webmail-folder-links a {
		display: flex;
		align-items: center;
		min-height: 2.75rem;
	}

	.webmail-msg {
		padding-top: var(--space-3);
		padding-bottom: var(--space-3);
	}

	.webmail-check {
		margin: calc(-1 * var(--space-3)) 0;
	}

	/* The message reaches the edges of the screen, which leaves its
	   text all of the width. */
	.webmail-view-message .webmail-message {
		margin: 0 calc(-1 * var(--space-4));
		border-right: 0;
		border-left: 0;
		border-radius: 0;
	}

	.webmail .webmail-frame {
		height: calc(100vh - var(--webmail-topbar) - var(--webmail-bar));
		height: calc(100dvh - var(--webmail-topbar) - var(--webmail-bar));
		min-height: 16rem;
		border-radius: 0;
	}

	ul.webmail-attachments li {
		width: 100%;
		min-height: 2.75rem;
	}

	.webmail-message-acts {
		padding-bottom: var(--webmail-bar);
	}

	.webmail-message-bar {
		position: fixed;
		right: 0;
		bottom: 0;
		left: 0;
		z-index: 15;
		flex-wrap: nowrap;
		gap: var(--space-1);
		margin: 0;
		padding: var(--space-2) var(--space-2) calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
		border-top: 1px solid var(--border);
		background: var(--surface);
		box-shadow: var(--shadow-lg);
	}

	/* The bars at the foot of the screen share it out among their
	   actions, and their menus open upwards. */
	.webmail-message-bar > .btn,
	.webmail-message-bar > .menu,
	.webmail-list-bar > .btn,
	.webmail-list-bar > .menu {
		flex: 1 1 0;
		min-width: 0;
	}

	.webmail-bar .menu > summary {
		width: 100%;
	}

	.webmail .menu-item {
		display: flex;
		align-items: center;
		min-height: 2.75rem;
	}

	.webmail-bar .webmail-more .icon {
		display: none;
	}

	.webmail-message-bar .menu-list,
	.webmail-list-bar .menu-list {
		top: auto;
		bottom: calc(100% + var(--space-1));
	}

	.webmail-bar > .webmail-move .menu-list {
		right: auto;
		left: 0;
	}
}

/* On a phone, the bar that changes the messages chosen shows once one
   is chosen, at the foot of the screen; a browser without :has() keeps
   it above the list. */
@supports selector(:has(*)) {
	@media (max-width: 47.99rem) {
		.webmail-list-bar {
			display: none;
		}

		.webmail-select:has(input[name="uid"]:checked) {
			padding-bottom: 7rem;
		}

		.webmail-select:has(input[name="uid"]:checked) .webmail-list-bar {
			position: fixed;
			top: auto;
			right: 0;
			bottom: 0;
			left: 0;
			z-index: 15;
			display: flex;
			padding: var(--space-2) var(--space-3) calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
			border-top: 1px solid var(--border);
			box-shadow: var(--shadow-lg);
		}

	}
}

/* The webmail's filters and vacation reply (ui.WebmailFiltersPage) and
   the form of a rule (ui.WebmailRulePage). */
.webmail-state {
	margin: 0 0 var(--block-gap);
}

.webmail-activate {
	margin: 0 0 var(--block-gap);
}

.callout .webmail-activate {
	margin: var(--space-3) 0 0;
}

.webmail-scripts {
	margin: 0;
	padding: 0;
	list-style: none;
}

.webmail-scripts li {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
	padding: var(--space-2) 0;
	border-bottom: 1px solid var(--border);
}

.webmail-scripts li:last-child {
	border-bottom: 0;
}

.webmail-dates {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
	gap: 0 var(--space-3);
}

.webmail-rules {
	margin: 0;
	padding: 0 0 0 1.5rem;
}

.webmail-rules li {
	padding: var(--space-3) 0;
	border-bottom: 1px solid var(--border);
}

.webmail-rule-text {
	margin: 0 0 var(--space-2);
	overflow-wrap: anywhere;
}

.webmail-rules form {
	margin: 0;
}

.webmail-new-rule {
	margin: var(--space-4) 0 0;
}

form.webmail-rule {
	max-width: 40rem;
}

.webmail-fieldset {
	margin: 0 0 var(--space-4);
	padding: var(--space-2) var(--space-4) var(--space-1);
	border: 1px solid var(--border);
	border-radius: var(--radius);
}

.webmail-fieldset legend {
	padding: 0 var(--space-1);
	font-weight: 600;
}

.webmail-fieldset > .check {
	margin: 0 0 var(--space-3);
}

.webmail-fieldset .field {
	margin-bottom: var(--space-3);
}

.webmail-condition {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr));
	gap: var(--space-2);
	padding-bottom: var(--space-4);
}

@media (max-width: 47.99rem) {
	.webmail-rules .btn,
	.webmail-new-rule .btn {
		min-height: 2.75rem;
	}
}

/* The page of folders (ui.WebmailFoldersPage): each folder with what
   it holds, the field that renames it, and what deletes or empties it. */
.webmail-manage {
	margin: 0;
	padding: 0;
	list-style: none;
}

.webmail-manage li {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-4);
	padding: var(--space-3) 0;
	border-bottom: 1px solid var(--border);
}

.webmail-manage li:last-child {
	border-bottom: 0;
}

.webmail-manage-name {
	display: flex;
	flex: 1 1 12rem;
	flex-wrap: wrap;
	gap: 0 var(--space-2);
	margin: 0;
	overflow-wrap: anywhere;
}

.webmail-rename {
	display: flex;
	flex: 1 1 18rem;
	gap: var(--space-2);
	margin: 0;
}

.webmail-rename .input {
	flex: 1;
	min-width: 0;
	min-height: 2rem;
	padding-top: 0;
	padding-bottom: 0;
}

.webmail-manage form {
	margin: 0;
}

@media (max-width: 47.99rem) {
	.webmail-manage .btn,
	.webmail-rename .input {
		min-height: 2.75rem;
	}
}

/* The webmail's compose form (ui.WebmailComposePage): as wide as the
   text it holds, each field the form's width. Its buttons stay at the
   foot of the screen while the form goes on below it, so that sending
   never takes a scroll to the end. */
form.webmail-compose {
	max-width: 48rem;
}

form.webmail-compose #compose-text {
	font-family: var(--font-mono);
}

.webmail-kept {
	margin: 0 0 var(--space-4);
	padding: var(--space-2) var(--space-4) 0;
	border: 1px solid var(--border);
	border-radius: var(--radius);
}

.webmail-kept legend {
	padding: 0 var(--space-1);
	font-weight: 600;
}

.webmail-kept .field {
	margin-bottom: var(--space-3);
}

.webmail-forward {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	margin: 0 0 var(--space-4);
}

.webmail-compose-actions {
	position: sticky;
	bottom: 0;
	z-index: 5;
	margin: 0 calc(-1 * var(--space-2));
	padding: var(--space-3) var(--space-2) calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
	border-top: 1px solid var(--border);
	background: var(--bg);
}

/* The compose form's editor of formatted text (webmail-compose.js): the
   switch between plain and formatted text, the toolbar, and the
   editable region, whose content looks like the elements it is made
   of, with quotes behind a bar as mail clients show them. The script
   shows what it runs; without it they stay hidden. */
form.webmail-compose [hidden],
.webmail-compose-default[hidden] {
	display: none;
}

.compose-switch,
.compose-toolbar {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-1);
}

.compose-switch {
	margin-bottom: var(--space-1);
}

.compose-switch [aria-pressed="true"],
.compose-toolbar [aria-pressed="true"] {
	border-color: var(--accent);
	background: var(--accent-soft);
	color: var(--accent-text);
}

.compose-editor {
	border: 1px solid var(--control-border);
	border-radius: var(--radius);
	background: var(--surface);
}

.compose-editor:focus-within {
	outline: var(--focus-ring);
	outline-offset: 2px;
}

.compose-toolbar {
	padding: var(--space-1);
	border-bottom: 1px solid var(--border);
	border-radius: var(--radius) var(--radius) 0 0;
	background: var(--surface);
}

.compose-toolbar .compose-bold {
	font-weight: 800;
}

.compose-toolbar .compose-italic {
	font-style: italic;
}

.compose-area {
	min-height: 18rem;
	max-height: 70vh;
	overflow-y: auto;
	padding: var(--space-2) var(--space-3);
	color: var(--text);
	overflow-wrap: anywhere;
}

.compose-area:focus {
	outline: none;
}

.compose-area > :first-child {
	margin-top: 0;
}

.compose-area p {
	margin: 0 0 var(--space-2);
}

.compose-area blockquote {
	margin: 0 0 var(--space-2);
	padding-left: var(--space-3);
	border-left: 3px solid var(--control-border);
	color: var(--muted);
}

.compose-area pre {
	padding: var(--space-2);
	border-radius: var(--radius-sm);
	background: var(--surface-2);
	white-space: pre-wrap;
}

.compose-area code {
	padding: 0 0.2em;
	border-radius: 3px;
	background: var(--surface-2);
}

.compose-area pre code {
	padding: 0;
	background: none;
}

.compose-area table {
	border-collapse: collapse;
}

.compose-area th,
.compose-area td {
	padding: 0.125rem 0.375rem;
	border: 1px solid var(--border);
}

.webmail-compose-default {
	max-width: 48rem;
	margin-top: var(--space-4);
	color: var(--muted);
}

/* On a phone the toolbar is one row that scrolls sideways and stays
   below the top bar (ui.Layout) while the text goes on; the text grows
   with what is written, in the page's own scrolling, which the
   keyboard shares; the controls are a thumb's size. */
@media (max-width: 47.99rem) {
	form.webmail-compose .btn,
	.webmail-compose-default .btn {
		min-height: 2.75rem;
	}

	.compose-toolbar {
		position: sticky;
		top: calc(3.75rem + 1px);
		z-index: 4;
		flex-wrap: nowrap;
		overflow-x: auto;
		overscroll-behavior-x: contain;
	}

	.compose-toolbar .btn {
		flex: none;
	}

	.compose-area {
		min-height: 14rem;
		max-height: none;
	}

	.webmail-compose-actions {
		margin: 0 calc(-1 * var(--space-4));
		padding-right: var(--space-4);
		padding-left: var(--space-4);
	}

	.webmail-compose-actions .btn {
		flex: 1;
	}
}

/* Copy and print buttons (copy.js): hidden until the script runs, so
   that a browser without scripts shows no button that does nothing. */
button[data-copy],
button[data-print] {
	display: none;
}

.copy-ready button[data-copy],
.copy-ready button[data-print] {
	display: inline-flex;
}

/* The button that shows and hides a password (ui.PasswordInput,
   static/password.js), drawn inside the input's right end so that the
   field keeps its width; hidden until the script runs, so that a
   browser without scripts shows the input alone. The input leaves it
   room only then. Edge's own button would make a second one. */
.password-input {
	position: relative;
}

.password-reveal {
	display: none;
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	align-items: center;
	justify-content: center;
	width: var(--control-height);
	padding: 0;
	border: none;
	border-radius: var(--radius);
	background: transparent;
	color: var(--muted);
}

.password-reveal:hover {
	background: transparent;
	color: var(--text);
}

.password-reveal:focus-visible {
	outline-offset: -3px;
}

.password-ready .password-reveal {
	display: flex;
}

.password-ready .password-input .input {
	padding-right: var(--control-height);
}

.password-input .input::-ms-reveal {
	display: none;
}

.password-reveal .reveal-off {
	display: flex;
}

.password-reveal .reveal-on,
.password-reveal[aria-pressed="true"] .reveal-off {
	display: none;
}

.password-reveal[aria-pressed="true"] .reveal-on {
	display: flex;
}

/* In a row of a form, the input's box is the row's item. */
.row-form .password-input {
	flex: 0 1 9rem;
	width: 9rem;
	min-width: 4rem;
}

.row-form .password-input .input {
	width: 100%;
	min-width: 0;
}

.row-form .password-reveal {
	width: 2rem;
}

.password-ready .row-form .password-input .input {
	padding-right: 2rem;
}

/* Text with its copy button after it (ui.copyText), such as a DNS
   record's value, which is long and breaks anywhere. */
.copy-text code {
	margin-right: var(--space-2);
	overflow-wrap: anywhere;
}

.copy-text .btn {
	vertical-align: middle;
}

/* The DKIM keys on a domain's page, each a part of the card of its
   own. */
.dkim-key {
	margin: 0 0 var(--space-4);
	padding: var(--space-4);
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	background: var(--surface-2);
}

.dkim-key h3 {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-3);
	margin: 0 0 var(--space-2);
	font-size: var(--text-md);
}

.dkim-key .kv {
	margin-bottom: var(--space-3);
}

/* A short list on one line, such as the domains of an account. */
ul.inline-list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-1) var(--space-3);
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Notes under a card's buttons, one about each. */
ul.notes {
	margin: var(--space-3) 0;
	padding-left: 1.25rem;
	color: var(--muted);
	font-size: var(--text-sm);
}

/* The catch pattern pages: the field of a new pattern's name, with its
   domain after it. */
.pattern-local {
	display: flex;
	align-items: center;
	gap: var(--space-2);
}

.pattern-local .input {
	flex: 1;
	min-width: 0;
}

.pattern-local .mono {
	overflow-wrap: anywhere;
}

/* The tester's answer, under its form. */
.pattern-test > :first-child {
	margin-top: var(--space-4);
}

/* The server settings pages: a value beside its badges, and long
   values (lists, URLs), which break anywhere rather than widen the
   table. */
.setting-value {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
}

.setting-text {
	overflow-wrap: anywhere;
}

.setting-related {
	margin: 0 0 var(--space-3);
}

.setting-unset {
	margin-top: var(--space-2);
}

.setting-mismatches {
	margin: 0;
	padding-left: 1.25rem;
}

/* The deliverability page: each check's card, the line with its last
   outcome, the findings' text, which holds records and errors that
   break anywhere rather than widen the table, and the form that runs
   the check, apart from what it found. */
.check-card {
	scroll-margin-top: var(--space-4);
}

.check-ran {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
	margin: 0 0 var(--space-2);
}

.check-text {
	overflow-wrap: anywhere;
}

.check-run {
	margin-top: var(--space-4);
}

/* The setup wizard's steps (ui.setupSteps), pills like the section
   navigation's, each numbered until done, when a check stands for the
   number: the step shown in the accent, one skipped dashed, one not
   reachable yet no link. The buttons that pass a step follow it. */
.setup-steps ol {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin: 0 0 var(--block-gap);
	padding: 0;
	list-style: none;
}

.setup-step > a,
.setup-step > span {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	min-height: 2rem;
	padding: 0 var(--space-3);
	border: 1px solid var(--border);
	border-radius: var(--radius-pill);
	background: var(--surface);
	color: var(--muted);
	font-size: var(--text-sm);
	font-weight: 600;
	text-decoration: none;
	white-space: nowrap;
}

.setup-step > a {
	color: var(--text);
}

.setup-step > a:hover {
	background: var(--surface-2);
}

.setup-step.done > * {
	color: var(--ok);
}

.setup-step.skipped > * {
	border-style: dashed;
}

.setup-step > [aria-current="step"] {
	border-color: var(--accent);
	background: var(--accent-soft);
	color: var(--accent-text);
}

.setup-step .icon {
	width: 1rem;
	height: 1rem;
}

.setup-step-n {
	font-variant-numeric: tabular-nums;
}

.setup-pass {
	margin-top: var(--block-gap);
}

/* Profile photos (ui.Avatar, the photo pages). The photo is drawn over
   the initials, which show while it loads, where there is none and where
   it cannot be fetched. */
.avatar {
	display: inline-grid;
	position: relative;
	overflow: hidden;
	vertical-align: middle;
}

.avatar img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.avatar-lg {
	width: 6rem;
	height: 6rem;
	font-size: var(--text-2xl);
}

.photo-shown {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-4);
}

.photo-shown-text {
	flex: 1;
	min-width: 12rem;
}

.photo-shown-text > :first-child {
	margin-top: 0;
}

.photo-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
	gap: var(--space-4);
	margin: var(--space-4) 0;
	padding: 0;
	list-style: none;
}

.photo-item {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
}

.photo-item img,
.photo-confirm {
	width: 100%;
	max-width: 16rem;
	height: auto;
	aspect-ratio: 1;
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface);
}

.photo-item-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
	font-size: var(--text-sm);
}

/* The trash button that deletes a photo of the history, as high as the
   small Use button beside it and red like the other ways to delete. */
.photo-item .btn-icon {
	width: 2rem;
	min-height: 2rem;
}

.photo-item .btn-icon,
.photo-item .btn-icon:hover {
	color: var(--danger);
}

/* The sender's avatar in the webmail (ui.WebmailSender): the photo of a
   sender proven to be a user of the server, the initials of any other.
   In the list it comes first on the row's link, as high as the sender
   and the subject below each other, or beside them on one line in a
   wide list, whose row then centers what it holds; the dot of a
   message not seen yet stays in the margin before it. In the open
   message it stands before the sender. */
.webmail-msg-link {
	grid-template-columns: auto minmax(0, 1fr);
	column-gap: var(--space-3);
}

.webmail-msg-link > .avatar {
	grid-row: span 2;
	align-self: center;
}

/* The open message's row has the initials' color. */
.webmail-current .webmail-msg-link > .avatar {
	background: var(--surface);
}

.webmail-unseen .webmail-from::before {
	margin-left: calc(-0.875rem - 2rem - var(--space-3));
}

@container webmail-list (min-width: 56rem) {
	.webmail-msg {
		align-items: center;
	}

	.webmail-msg-link {
		grid-template-columns: auto minmax(8rem, 14rem) minmax(0, 1fr);
		align-items: center;
	}

	.webmail-msg-link > .avatar {
		grid-row: auto;
	}

	.webmail-unseen .webmail-from::before {
		margin-left: calc(-0.875rem - 2rem - var(--space-4));
	}
}

.webmail-headers dd > .avatar {
	margin-right: var(--space-2);
}

/* The avatar makes the sender's line higher than its label's. */
.webmail-headers dt:has(+ dd > .avatar) {
	align-self: center;
}

/* Contacts (contacts.templ, contact_*.templ): the card that turns
   collecting off and on, and the addresses a confirmation lists. */

.contact-collect {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-3);
}

.contact-collect p {
	flex: 1 1 100%;
	margin: 0;
}

.contact-export {
	margin-top: var(--space-4);
	padding-top: var(--space-4);
	border-top: 1px solid var(--border);
}

.contact-confirm-list {
	margin: 0 0 var(--space-4);
	padding-left: var(--space-5);
}

/* The suggestions of the compose form's address fields
   (contact_suggest.templ, contacts.js): a list under the field, as the
   menu search's. */

.contact-combo {
	position: relative;
}

.contact-options {
	position: absolute;
	top: calc(100% + var(--space-1));
	left: 0;
	z-index: 40;
	width: 100%;
	max-height: min(20rem, 50vh);
	overflow-y: auto;
	padding: var(--space-1);
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	background: var(--surface);
	box-shadow: var(--shadow-lg);
}

.contact-option {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0 var(--space-2);
	padding: var(--space-2) var(--space-3);
	border-radius: var(--radius);
	color: var(--text);
	cursor: pointer;
}

.contact-option:hover,
.contact-option[aria-selected="true"] {
	background: var(--accent-soft);
	color: var(--accent-text);
}

.contact-option-name {
	font-weight: 600;
}

.contact-option-address {
	min-width: 0;
	overflow-wrap: anywhere;
	color: var(--muted);
	font-family: var(--font-mono);
	font-size: var(--text-sm);
}

@media (forced-colors: active) {
	.contact-option[aria-selected="true"] {
		outline: 2px solid;
	}
}

/* The offer to add a message's sender to the contacts, under the
   message's header (contact_add.templ). */

.contact-add {
	margin: 0 0 var(--space-2);
}

/* As high as the button, so that the line does not move when the
   offer becomes the word that the sender was added. */
.contact-add p,
.contact-add form {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-1) var(--space-2);
	min-height: 2rem;
	margin: 0;
	font-size: var(--text-sm);
}

.contact-add .icon {
	color: var(--ok);
}

/* The webmail's threads (ui.WebmailPage with a thread; design.md
   section 3, Webmail: threads). An open thread is the message's pane
   (.webmail-message) with its messages one under the other, each a
   <details> whose line, the sender's avatar, the sender and the date,
   expands it: its
   header fields and links, then its text or its frame, which takes the
   pane's width. A frame cannot be sized to its message (see the
   message's frame above), so each takes most of the pane on a wide
   screen, and on a phone the screen between the top bar and the bar of
   actions, as the open message's. */
.webmail-thread-total {
	margin: 0 0 var(--space-3);
	font-size: var(--text-sm);
}

ol.webmail-thread-messages {
	flex: none;
	margin: 0;
	padding: 0;
	border-top: 1px solid var(--border);
	list-style: none;
}

ol.webmail-thread-messages > li {
	border-bottom: 1px solid var(--border);
}

ol.webmail-thread-messages > li:last-child {
	border-bottom: 0;
}

.webmail-thread-line {
	position: relative;
	display: flex;
	align-items: center;
	gap: var(--space-2) var(--space-3);
	padding: var(--space-2) var(--space-4) var(--space-2) var(--space-5);
	list-style: none;
	cursor: pointer;
}

.webmail-thread-line::-webkit-details-marker {
	display: none;
}

.webmail-thread-line:hover {
	background: var(--surface-2);
}

.webmail-thread-line:focus-visible {
	outline: var(--focus-ring);
	outline-offset: -2px;
}

.webmail-thread-msg[open] > .webmail-thread-line {
	background: var(--surface-2);
}

.webmail-thread-line .webmail-from {
	flex: 1;
	min-width: 0;
}

.webmail-thread-line .webmail-flags,
.webmail-thread-line .webmail-date {
	flex: none;
	color: var(--muted);
	font-size: var(--text-sm);
	white-space: nowrap;
}

.webmail-thread-line .webmail-flags .icon {
	width: 1rem;
	height: 1rem;
	align-self: center;
}

.webmail-thread-meta {
	padding: var(--space-3) var(--space-4) 0;
}

.webmail-thread-skipped {
	margin: 0;
	padding-bottom: var(--space-3);
	font-size: var(--text-sm);
}

.webmail-thread-skipped a {
	margin-left: var(--space-2);
}

.webmail-thread-links {
	margin: 0 0 var(--space-3);
}

.webmail .webmail-thread .webmail-frame {
	flex: none;
	height: 70vh;
	min-height: 16rem;
	border-radius: 0;
}

/* The threads in the list: each a row as a message's, with the number
   of its messages beside its date; and below the orders, the switch
   between messages and threads, the choice made pressed. */
.webmail-thread-count {
	padding: 0 0.4375rem;
	border: 1px solid var(--border);
	border-radius: var(--radius-pill);
	background: var(--surface-2);
	color: var(--text);
	font-size: var(--text-xs);
	font-variant-numeric: tabular-nums;
	font-weight: 600;
	line-height: 1.5;
}

.webmail-threads-switch {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-1) var(--space-2);
	margin: calc(-1 * var(--space-1)) var(--space-4) var(--space-3);
	font-size: var(--text-sm);
}

.webmail-threads-options {
	display: inline-flex;
	gap: var(--space-1);
}

.webmail-threads-options .btn[aria-pressed="true"] {
	border-color: var(--accent);
	background: var(--accent-soft);
	color: var(--accent-text);
}

@media (max-width: 47.99rem) {
	.webmail-thread-line {
		min-height: 2.75rem;
	}

	.webmail .webmail-thread .webmail-frame {
		height: calc(100vh - var(--webmail-topbar) - var(--webmail-bar));
		height: calc(100dvh - var(--webmail-topbar) - var(--webmail-bar));
	}
}

/* On a phone, the bar that changes the threads chosen shows once one
   is chosen, at the foot of the screen, as for the messages chosen
   (above). */
@supports selector(:has(*)) {
	@media (max-width: 47.99rem) {
		.webmail-select:has(input[name="thread"]:checked) {
			padding-bottom: 7rem;
		}

		.webmail-select:has(input[name="thread"]:checked) .webmail-list-bar {
			position: fixed;
			top: auto;
			right: 0;
			bottom: 0;
			left: 0;
			z-index: 15;
			display: flex;
			padding: var(--space-2) var(--space-3) calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
			border-top: 1px solid var(--border);
			box-shadow: var(--shadow-lg);
		}
	}
}

/* A message's original (ui.WebmailOriginalPage): its source as text,
   in the monospace font on a panel of its own. Long lines wrap rather
   than scroll the page sideways on a phone, and copied text keeps the
   lines as they are; a tab is as wide as mail programs show it. */
pre.webmail-original {
	margin: var(--space-4) 0;
	padding: var(--space-3) var(--space-4);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface-2);
	font-size: var(--text-sm);
	line-height: 1.5;
	white-space: pre-wrap;
	overflow-wrap: anywhere;
	tab-size: 8;
}

/* The links of an open message to its original, as text and as a
   file, on a line of their own. */
.webmail-original-links {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-1) var(--space-4);
}

/* The page that crops a profile photo (ui.PhotoCropPage): the picture
   of the upload, as wide as the card and at most most of the window's
   height, then the square's fields side by side and, without a script,
   the crops as buttons. The stage is exactly as large as the picture,
   so that what is drawn over it lines up with its pixels. */
.crop-stage {
	position: relative;
	display: inline-block;
	max-width: 100%;
	margin: 0 0 var(--space-4);
	line-height: 0;
}

.crop-image {
	display: block;
	width: auto;
	max-width: 100%;
	height: auto;
	max-height: 70vh;
	background: var(--surface-2);
}

.crop-fields {
	min-width: 0;
	margin: 0 0 var(--space-4);
	padding: 0;
	border: 0;
}

.crop-fields legend {
	margin-bottom: var(--space-2);
	padding: 0;
	font-weight: 600;
}

.crop-field-row {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 8rem));
	gap: var(--space-3);
}

.crop-field-row .field {
	margin-bottom: var(--space-2);
}

.crop-presets {
	margin-top: var(--space-5);
}

.crop-presets p {
	margin: 0 0 var(--space-2);
}

.crop-discard {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-4);
	margin-top: var(--block-gap);
}

.crop-discard p {
	margin: 0;
}

/* The square over the picture, which crop.js draws and places once it
   runs (.crop-ready): a white frame, a darker line around it for light
   pictures, the rest of the picture dimmed by its shadow, which the
   stage clips, and a dashed circle of the avatar the square becomes.
   A finger that drags it moves it rather than the page. Its colors are
   over a photo, not the page, so they are the same in either theme. */
.crop-frame,
.crop-keys {
	display: none;
}

.crop-ready .crop-stage {
	overflow: hidden;
}

.crop-ready .crop-frame {
	display: block;
	position: absolute;
	box-sizing: border-box;
	border: 2px solid #ffffff;
	box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 0 0 100vmax var(--scrim);
	cursor: move;
	touch-action: none;
	user-select: none;
}

.crop-ready .crop-frame::before {
	position: absolute;
	inset: 0;
	border: 1px dashed rgba(255, 255, 255, 0.8);
	border-radius: 50%;
	content: "";
	pointer-events: none;
}

/* The focus ring is drawn inside the frame, which the stage clips. */
.crop-ready .crop-frame:focus-visible {
	outline: 3px solid var(--focus);
	outline-offset: -5px;
}

.crop-ready .crop-keys {
	display: block;
	margin: 0 0 var(--space-4);
}

.crop-ready .crop-image {
	cursor: crosshair;
}

/* A corner's handle: a white square inside the frame's corner, whose
   touch target reaches past it. */
.crop-handle {
	position: absolute;
	width: 0.875rem;
	height: 0.875rem;
	border: 1px solid rgba(0, 0, 0, 0.6);
	background: #ffffff;
}

.crop-handle::after {
	position: absolute;
	inset: -0.75rem;
	content: "";
}

.crop-handle-nw {
	top: -1px;
	left: -1px;
	cursor: nwse-resize;
}

.crop-handle-ne {
	top: -1px;
	right: -1px;
	cursor: nesw-resize;
}

.crop-handle-sw {
	bottom: -1px;
	left: -1px;
	cursor: nesw-resize;
}

.crop-handle-se {
	right: -1px;
	bottom: -1px;
	cursor: nwse-resize;
}

.crop-ready .crop-presets {
	display: none;
}

/* The dialog of the photo page that crops the chosen file before it is
   sent (photoCropDialog, crop.js): the picture with the square's frame,
   which the rules of the crop page draw (.crop-ready), at most about
   half the window's height, and beside it, or under it where the
   dialog is narrow, the photo as the avatars show it, round, at their
   two sizes. The picture's wrapper takes the room; the stage inside it
   stays as large as the picture, so that the frame lines up. */
.photo-crop-dialog {
	width: min(48rem, calc(100vw - 2 * var(--space-4)));
	max-height: calc(100dvh - 2 * var(--space-4));
	padding: var(--space-5);
	overflow-y: auto;
	border: 1px solid var(--border);
	border-radius: var(--radius-xl);
	background: var(--surface);
	color: var(--text);
	box-shadow: var(--shadow-lg);
}

.photo-crop-dialog::backdrop {
	background: var(--scrim);
}

.photo-crop-dialog h2 {
	margin: 0 0 var(--space-3);
	font-size: var(--text-xl);
}

.photo-crop-body {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	gap: var(--space-4);
}

.photo-crop-picture {
	flex: 1 1 16rem;
	min-width: 0;
}

.photo-crop-picture .crop-stage {
	margin: 0;
}

.photo-crop-picture .crop-image {
	max-height: min(55vh, 32rem);
}

.photo-crop-preview {
	display: flex;
	flex: 0 0 auto;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: var(--space-3);
	max-width: 12rem;
}

.photo-crop-preview-title {
	flex-basis: 100%;
	margin: 0;
	font-weight: 600;
}

.photo-crop-preview canvas {
	display: block;
	border-radius: 50%;
	background: var(--surface-2);
}

.photo-crop-preview-large {
	width: 8rem;
	height: 8rem;
}

.photo-crop-preview-small {
	width: 2rem;
	height: 2rem;
}

.photo-crop-preview .field-help {
	flex-basis: 100%;
	margin: 0;
}

.photo-crop-dialog .crop-keys {
	margin: var(--space-4) 0 0;
}

.photo-crop-dialog .form-actions {
	margin-top: var(--space-4);
}

.photo-crop-dialog .form-actions p {
	margin: 0;
}

/* The checks of arriving mail (webmail_checks.templ). A mark on each
   row of the list and each line of a thread: an icon of its own for
   each verdict and its words, which a list wide enough shows with the
   spam score, and which a narrower one, as a thread's line, leaves to
   screen readers and the title. The row's link covers the row, so a
   pointer that can hover gets the mark above it, for its title. Under
   an open message's header, the warnings and a summary that opens on
   the results. */
.webmail-verdict {
	display: inline-flex;
	align-items: center;
	gap: var(--space-1);
	color: var(--muted);
	font-size: var(--text-xs);
	font-weight: 600;
	white-space: nowrap;
}

.webmail-verdict .icon {
	width: 1rem;
	height: 1rem;
}

.webmail-verdict.ok {
	color: var(--ok);
}

.webmail-verdict.info {
	color: var(--info);
}

.webmail-verdict.warn {
	color: var(--warn);
}

.webmail-verdict.danger {
	color: var(--danger);
}

.webmail-verdict-score {
	color: var(--muted);
	font-weight: 400;
	font-variant-numeric: tabular-nums;
}

.webmail-msg .webmail-verdict-label,
.webmail-msg .webmail-verdict-score,
.webmail-thread-line .webmail-verdict-label,
.webmail-thread-line .webmail-verdict-score {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
}

@container webmail-list (min-width: 56rem) {
	.webmail-msg .webmail-verdict-label,
	.webmail-msg .webmail-verdict-score {
		position: static;
		width: auto;
		height: auto;
		overflow: visible;
		clip: auto;
	}
}

@media (hover: hover) {
	.webmail-msg .webmail-verdict {
		position: relative;
		z-index: 1;
		cursor: help;
	}
}

.webmail-checks {
	margin: 0 0 var(--space-3);
}

ul.webmail-checks-warnings {
	margin: 0;
	padding-left: 1.25rem;
	list-style: disc;
}

.webmail-checks-details {
	font-size: var(--text-sm);
}

.webmail-checks-details > summary {
	width: fit-content;
	cursor: pointer;
}

.webmail-checks-name {
	color: var(--muted);
}

.webmail-checks-details .webmail-verdict {
	font-size: var(--text-sm);
	vertical-align: middle;
}

.webmail-checks-body {
	padding-top: var(--space-2);
}

.webmail-checks-list {
	gap: var(--space-1) var(--space-3);
	margin: 0 0 var(--space-2);
}

.webmail-checks-list dd {
	font-weight: 400;
}

.webmail-checks-body p {
	margin: 0 0 var(--space-2);
}

@media (max-width: 47.99rem) {
	.webmail-checks-details > summary {
		padding: var(--space-3) 0;
	}
}

/* On a phone the compose form folds away the fields most messages
   leave as they are, Your name, Cc and Bcc, behind a box ticked when
   any holds something (ui.WebmailComposePage), so that To, Subject
   and the text come first. No script does it: the box acts through
   :has(), and a browser without it shows every field and no box; a
   wider screen shows every field. */
.compose-more {
	display: none;
}

@supports selector(:has(*)) {
	@media (max-width: 47.99rem) {
		.compose-more {
			display: block;
		}

		.compose-more .check {
			align-items: center;
			min-height: 2.75rem;
		}

		.compose-more .check input {
			margin: 0;
		}

		form.webmail-compose:has(#compose-more:not(:checked)) .compose-fold {
			display: none;
		}
	}
}

/* Text taken from a message (design.md section 3, Webmail: text from a
   message), which ui.StoredOrder has rid of what changes its direction,
   each field an isolate. A subject takes its direction from its first
   letter, so that one in Hebrew reads right to left from its start,
   cut short at its end. Names, addresses and a file's name read left
   to right, as the list of them and the avatar beside them do: a name
   in Hebrew still reads in its own order, and a file's name shows
   ending in its extension. The sent archive's pages show its messages
   the same way, and each value of an entry's index in the direction of
   its first letter. */
.webmail-subject,
#webmail-subject,
.archive-subject,
#archive-subject,
.archive-index dd {
	unicode-bidi: plaintext;
}

.webmail-from,
.webmail-headers dd,
.archive-headers dd,
ul.webmail-attachments a[download] {
	direction: ltr;
	unicode-bidi: isolate;
}

/* A contact's name and notes, which greentea keeps without what changes
   their direction (checkContact), read in the direction of their first
   letter, each an isolate: a name in Hebrew from its start, cut short
   at its end, beside its address or the mark of a collected one. */
.contact-row-name .trunc,
.contact-row-notes .clamp,
.contact-option-name {
	unicode-bidi: plaintext;
}

/* The sidebar collapsed to its icons (ui.Layout, static/sidebar.js): on
   a wide screen, which side-collapsed on <html> asks for, a strip of
   icons. The labels stay the links' text, pushed out of sight, which a
   screen reader reads and the tooltip shows; the headings and who is
   logged in are hidden the same way, and the head and the foot stack
   their parts. The buttons that collapse and expand it show only when
   the script runs (side-collapsible), one at a time; on a phone,
   neither, as the drawer stays as it is. */
.side-toggle {
	display: none;
}

@media (min-width: 60.01rem) {
	.side-collapsible:not(.side-collapsed) .side-collapse,
	.side-collapsible.side-collapsed .side-expand {
		display: inline-flex;
	}

	.side-collapsed {
		--sidebar-width: 4rem;
	}

	.side-collapsed .side {
		gap: var(--space-4);
		padding-right: var(--space-3);
		padding-left: var(--space-3);
	}

	.side-collapsed .side-head,
	.side-collapsed .side-foot,
	.side-collapsed .side-foot .theme-switch,
	.side-collapsed .theme-switch-options {
		flex-direction: column;
		justify-content: flex-start;
		gap: var(--space-2);
	}

	.side-collapsed .side-foot {
		padding-right: 0;
		padding-left: 0;
	}

	.side-collapsed .side-foot .theme-switch {
		flex-basis: auto;
		padding: 0;
	}

	.side-collapsed .brand {
		padding: 0;
	}

	.side-collapsed .brand-name,
	.side-collapsed .nav-title,
	.side-collapsed .theme-switch-label,
	.side-collapsed .who {
		position: absolute;
		width: 1px;
		height: 1px;
		overflow: hidden;
		clip: rect(0 0 0 0);
		white-space: nowrap;
	}

	/* Each group but the first starts at a line, in place of its
	   heading. */
	.side-collapsed .nav-group + .nav-group {
		padding-top: var(--space-4);
		border-top: 1px solid var(--border);
	}

	/* A block, as a flex container's text would be indented by its own
	   width only, and a short label show its start. */
	.side-collapsed .nav-list a {
		display: block;
		height: 2.25rem;
		padding: 0;
		overflow: hidden;
		text-indent: 100%;
		white-space: nowrap;
	}

	.side-collapsed .nav-list .icon {
		left: 50%;
		margin-left: -0.5625rem;
	}
}

.side-tip {
	position: fixed;
	z-index: 40;
	padding: var(--space-1) var(--space-2);
	border-radius: var(--radius-sm);
	background: var(--text);
	box-shadow: var(--shadow-lg);
	color: var(--surface);
	font-size: var(--text-sm);
	font-weight: 500;
	white-space: nowrap;
	transform: translateY(-50%);
	pointer-events: none;
}

.side-tip[hidden] {
	display: none;
}

@media (forced-colors: active) {
	.side-tip {
		border: 1px solid;
	}
}

/* A notice with an action (ui.ActionNotice), such as Undo after a
   delete. Without a script it is a flash above the page with its button
   at the end; action-notice.js makes it a toast at the foot of the
   window (action-notice-toast), above the phone's bars of actions. */
.action-notice {
	align-items: center;
}

.action-notice > p {
	flex: 1 1 auto;
	min-width: 0;
}

.action-notice-acts {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	margin-left: auto;
}

.action-notice-acts form {
	margin: 0;
}

.action-notice-toast {
	position: fixed;
	bottom: calc(var(--space-5) + env(safe-area-inset-bottom, 0px));
	left: 50%;
	z-index: 25;
	width: max-content;
	max-width: min(32rem, calc(100vw - 2 * var(--space-4)));
	margin: 0;
	border-color: var(--control-border);
	box-shadow: var(--shadow-lg);
	transform: translateX(-50%);
}

@media (max-width: 47.99rem) {
	.action-notice-toast {
		right: var(--space-4);
		left: var(--space-4);
		width: auto;
		max-width: none;
		transform: none;
	}

	.webmail:has(.webmail-message-bar) .action-notice-toast,
	.webmail:has(.webmail-select input[name="uid"]:checked) .action-notice-toast {
		bottom: calc(4.5rem + env(safe-area-inset-bottom, 0px));
	}
}

/* The dialog that confirms what cannot be undone (ui.ConfirmDialog),
   such as deleting messages for good: a card over the page, red as a
   confirmation of it is, with what will be removed. */
.confirm-dialog {
	width: min(30rem, calc(100vw - 2 * var(--space-4)));
	max-height: calc(100dvh - 2 * var(--space-4));
	padding: var(--space-5);
	border: 1px solid var(--border);
	border-top: 4px solid var(--danger);
	border-radius: var(--radius-xl);
	background: var(--surface);
	color: var(--text);
	box-shadow: var(--shadow-lg);
}

.confirm-dialog::backdrop {
	background: var(--scrim);
}

.confirm-dialog h2 {
	margin: 0 0 var(--space-2);
	font-size: var(--text-xl);
}

.confirm-dialog p {
	margin: 0 0 var(--space-3);
}

.confirm-dialog-items {
	margin: 0 0 var(--space-3);
	padding-left: var(--space-5);
	overflow-wrap: anywhere;
	unicode-bidi: plaintext;
}

.confirm-dialog .form-actions {
	margin-top: var(--space-4);
}

/* The webmail's notifications of new mail (static/notifications.js):
   where this browser stands, with the buttons that change it, in a card
   of the notifications page and as a prompt above a page of the
   webmail, which the script shows only while the browser has not been
   asked. */
.notify-browser p {
	margin: 0 0 var(--space-3);
}

.notify-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
}

.notify-actions [hidden] {
	display: none;
}

/* The sounds of the notifications on their page
   (static/notification-sounds.js): each choice with the button that
   plays it, hidden until the script finds that the browser can; and
   whether this browser plays it, below where it stands on
   notifications. */
.sound-choice {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0 var(--space-2);
}

.sound-choice .theme-choice {
	flex: 1;
	min-width: 0;
}

.sound-choice [hidden] {
	display: none;
}

/* What the script says when the browser did not play a sound, on a
   line of its own under the choice; empty, it takes no room but stays
   a live region. */
.sound-note {
	flex-basis: 100%;
	margin: 0;
	color: var(--muted);
	font-size: var(--text-sm);
}

.sound-note:not(:empty) {
	margin-top: var(--space-1);
}

.notify-browser + .notify-browser {
	margin-top: var(--space-4);
}

/* The browsers notified through Web Push, listed below this browser's
   card (static/push.js). */
.notify-browser + .tbl-wrap {
	margin-top: var(--space-4);
}

.notify-prompt {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2) var(--space-4);
	margin: 0 0 var(--space-3);
	padding: var(--space-3) var(--space-4);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface);
}

.notify-prompt[hidden] {
	display: none;
}

.notify-prompt p {
	margin: 0;
}

/* A column's header that sorts its list (ui.ColumnSort): a link in the
   header's colours, with an arrow while it orders the list. A phone
   shows no header, so it chooses the order with the fields of the
   list's filter (ui.SortFields), hidden elsewhere, where they still go
   with the filter and keep the order chosen. */
.tbl-sort {
	display: inline-flex;
	align-items: center;
	gap: var(--space-1);
	color: inherit;
	text-decoration: none;
}

.tbl-sort:hover,
.tbl-sorted {
	color: var(--text);
}

.tbl-sort:hover {
	text-decoration: underline;
}

.tbl-sort .icon {
	width: 0.875rem;
	height: 0.875rem;
}

.sort-fields {
	display: none;
}

@media (max-width: 40rem) {
	.sort-fields {
		display: contents;
	}
}
