/*
 * opendesktopauthenticator.com
 *
 * ## The layout idea
 *
 * The first version set everything in one 760px column, which made a product
 * site read like a text file: no entry point, no rhythm, and a third of a
 * desktop screen left empty beside the text. This version separates two widths
 * that were being conflated.
 *
 * **`.band`** is full width. Sections sit in bands, and a band can carry its own
 * background, so the page has visible joints instead of running together.
 *
 * **`.prose`** is the reading measure, about 68 characters. Body text never gets
 * wider than that no matter how large the window, because a line you have to
 * track back across is a line people skip.
 *
 * Everything else — the hero, the signal strip, card grids, the project list —
 * is free to use the full band. That contrast between narrow text and wide
 * structure is most of what makes a page feel designed rather than typed.
 *
 * ## Constraints
 *
 * Local fonts only, matching the application: no web font means no third-party
 * request, which is what lets the content security policy stay absolute with no
 * `unsafe-inline` anywhere. Segoe UI Variable ships three optical sizes and each
 * is used at the size it was cut for.
 */

:root {
	--mint: #42f29a;
	--mint-bright: #6cffbe;
	--mint-pale: #9fe7bd;
	--emerald: #2ecc71;
	--teal: #1abc9c;

	--bg: #070a0e;
	--bg-lift: #0b1016;
	--panel: #0e141b;
	--panel-hi: #131b24;

	--line: rgba(147, 168, 158, 0.16);
	--line-strong: rgba(147, 168, 158, 0.3);

	--text: #eef3f0;
	--body: #c8d5cd;
	--muted: #93a89e;
	--faint: #71857a;

	--warn: #f5c26b;
	--danger: #ff8a8a;

	--radius: 16px;
	--radius-sm: 10px;
	--ease: cubic-bezier(0.16, 1, 0.3, 1);

	--font-display:
		'Segoe UI Variable Display', 'Segoe UI', Inter, 'SF Pro Display', system-ui, sans-serif;
	--font-ui: 'Segoe UI Variable Text', 'Segoe UI', Inter, 'SF Pro Text', system-ui, sans-serif;
	--font-small: 'Segoe UI Variable Small', 'Segoe UI', Inter, system-ui, sans-serif;
	--font-numeric: Bahnschrift, 'DIN Alternate', 'Segoe UI Variable Text', system-ui, sans-serif;
	--font-mono: 'Cascadia Mono', Consolas, ui-monospace, monospace;

	color-scheme: dark;
}

* {
	box-sizing: border-box;
}

/*
 * Long words break rather than push the page sideways.
 *
 * This site is full of URLs written out in full — that is the point of them,
 * since "check the address" is most of the advice here — and
 * `github.com/Jessecar96/SteamDesktopAuthenticator` is one unbreakable word to a
 * layout engine. On a 375px phone every page was 7px wider than the viewport
 * because of it, which means the whole document slides under the thumb and
 * every tap lands slightly wrong.
 *
 * `break-word` rather than `anywhere`: it only breaks a word that cannot fit on
 * a line of its own, so ordinary prose is untouched.
 */
p,
li,
dd,
dt,
td,
th,
figcaption,
blockquote {
	overflow-wrap: break-word;
}

html {
	scroll-behavior: smooth;
}

body {
	margin: 0;
	background: var(--bg);
	color: var(--body);
	font-family: var(--font-ui);
	font-size: 17px;
	line-height: 1.68;
	-webkit-font-smoothing: antialiased;
	font-variant-numeric: tabular-nums;
}

/* Two low emerald washes, fixed. The room the page sits in, not part of it. */
body::before {
	content: '';
	position: fixed;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background:
		radial-gradient(900px 520px at 12% -8%, rgba(46, 204, 113, 0.1), transparent 62%),
		radial-gradient(760px 460px at 96% 2%, rgba(26, 188, 156, 0.08), transparent 58%);
}

/* ------------------------------------------------------------------ shell -- */

.wrap {
	max-width: 1080px;
	margin: 0 auto;
	padding: 0 28px;
}

/* The reading measure. Applied to the text of every page; wide elements opt out
   by living outside it. */
.prose {
	max-width: 68ch;
}

.band {
	padding: 62px 0;
	border-top: 1px solid var(--line);
}

.band-tint {
	background: linear-gradient(180deg, rgba(255, 255, 255, 0.022), transparent);
}

.skip {
	position: absolute;
	left: -9999px;
	background: var(--mint);
	color: #04140c;
	padding: 10px 18px;
	border-radius: 0 0 10px 0;
	font-weight: 700;
	z-index: 20;
}
.skip:focus {
	left: 0;
	top: 0;
}

/* --------------------------------------------------------------- masthead -- */

.masthead {
	position: sticky;
	top: 0;
	z-index: 10;
	border-bottom: 1px solid var(--line);
	background: rgba(7, 10, 14, 0.78);
	backdrop-filter: blur(14px) saturate(1.3);
}

.masthead .wrap {
	display: flex;
	align-items: center;
	justify-content: space-between;
	/* 20 rather than 24: the mark and nine links came to nine pixels more than
	   the 1024px the measure leaves once its padding is taken off, and those
	   nine pixels were the whole reason the nav dropped to a second line. */
	gap: 20px;
	flex-wrap: wrap;
	padding-top: 15px;
	padding-bottom: 15px;
}

.brand {
	display: flex;
	align-items: center;
	gap: 11px;
	color: var(--text);
	text-decoration: none;
	font-size: 0.99rem;
	letter-spacing: -0.012em;
	white-space: nowrap;
}
.brand b {
	font-weight: 680;
}
.brand img {
	filter: drop-shadow(0 2px 8px rgba(46, 204, 113, 0.35));
}

/*
 * The navigation, which has to survive items being added to it.
 *
 * Nine links plus the wordmark came to a few pixels more than the 1080px
 * measure, so the whole nav dropped onto a second line — the mark alone on top
 * and the links beneath it, which reads as a mistake rather than a layout. The
 * padding is tightened enough to fit, and `flex-end` means that if a tenth item
 * ever pushes it over again it wraps to a row aligned with the right edge
 * instead of drifting to the left of the mark above it. Deliberate in both
 * states, rather than correct in one and accidental in the other.
 */
.masthead nav {
	display: flex;
	gap: 2px;
	flex-wrap: wrap;
	justify-content: flex-end;
	/* Pushes the whole nav to the right edge even when it is alone on a wrapped
	   line, where `space-between` on the parent would otherwise leave it hard
	   against the left and sitting under the middle of nothing. */
	margin-left: auto;
	font-family: var(--font-small);
	font-size: 0.88rem;
}
.masthead nav a {
	color: var(--muted);
	text-decoration: none;
	padding: 7px 9px;
	border-radius: 8px;
	transition:
		color 160ms var(--ease),
		background 160ms var(--ease);
}
.masthead nav a:hover {
	color: var(--mint);
	background: rgba(66, 242, 154, 0.09);
}
.masthead nav a[aria-current='page'] {
	color: var(--mint);
	background: rgba(66, 242, 154, 0.13);
}

/* ------------------------------------------------------------------- hero -- */

.hero {
	position: relative;
	padding: 82px 0 56px;
	text-align: center;
	overflow: hidden;
}

/* A single soft bloom behind the mark. One light source, so the page reads as
   lit rather than decorated. */
.hero::before {
	content: '';
	position: absolute;
	top: -180px;
	left: 50%;
	width: 720px;
	height: 480px;
	transform: translateX(-50%);
	pointer-events: none;
	background: radial-gradient(closest-side, rgba(46, 204, 113, 0.17), transparent 72%);
}

.hero > * {
	position: relative;
}

.hero-mark {
	filter: drop-shadow(0 12px 34px rgba(46, 204, 113, 0.4));
}

.hero h1 {
	margin: 22px auto 0;
	max-width: 17ch;
	font-size: clamp(2.2rem, 1.3rem + 3.6vw, 3.5rem);
	line-height: 1.08;
	letter-spacing: -0.032em;
}

.hero .lede {
	margin: 20px auto 0;
	max-width: 60ch;
	font-size: 1.14rem;
	color: var(--body);
}

.hero-actions {
	display: flex;
	justify-content: center;
	gap: 12px;
	flex-wrap: wrap;
	margin-top: 30px;
}

/* Short, checkable claims rather than adjectives. Each is a fact the site can
   be held to elsewhere. */
.signals {
	display: grid;
	/*
	 * Four items, so the column count is stated rather than derived.
	 *
	 * `auto-fit` with a minimum track was picking three columns at some widths,
	 * which left one claim alone on a second row beside a large empty cell. And
	 * it was picking three at 1024 only when a scrollbar was present — a layout
	 * that changes shape depending on whether the page happens to scroll is not
	 * one to tune by adjusting the minimum and re-measuring. 4, 2 or 1 divides
	 * four evenly; three never will.
	 */
	grid-template-columns: repeat(4, 1fr);
	gap: 1px;
	margin: 46px 0 0;
	padding: 0;
	list-style: none;
	background: var(--line);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	overflow: hidden;
}
.signals li {
	margin: 0;
	padding: 18px 20px;
	background: var(--bg);
	text-align: left;
}
.signals b {
	display: block;
	font-family: var(--font-small);
	font-size: 0.72rem;
	font-weight: 680;
	letter-spacing: 0.11em;
	text-transform: uppercase;
	color: var(--mint);
}
.signals span {
	display: block;
	margin-top: 4px;
	font-size: 0.9rem;
	color: var(--muted);
	line-height: 1.5;
}

/* Two-by-two through the middle widths, one column on a phone. */
@media (max-width: 1100px) {
	.signals {
		grid-template-columns: repeat(2, 1fr);
	}
}
@media (max-width: 560px) {
	.signals {
		grid-template-columns: 1fr;
	}
}

/* ------------------------------------------------------------------- type -- */

h1 {
	font-family: var(--font-display);
	font-size: clamp(1.95rem, 1.3rem + 2.5vw, 2.7rem);
	font-weight: 660;
	letter-spacing: -0.026em;
	line-height: 1.14;
	margin: 0 0 18px;
	background: linear-gradient(176deg, #fff 30%, var(--mint-pale));
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}

h2 {
	font-family: var(--font-display);
	font-size: 1.46rem;
	font-weight: 640;
	letter-spacing: -0.02em;
	color: var(--text);
	margin: 52px 0 14px;
}

h3 {
	font-size: 1.06rem;
	font-weight: 650;
	color: var(--text);
	margin: 28px 0 8px;
}

p,
li {
	color: var(--body);
}

.lede {
	font-size: 1.16rem;
	line-height: 1.62;
	color: var(--text);
}

a {
	color: var(--mint);
	text-decoration-color: rgba(66, 242, 154, 0.38);
	text-underline-offset: 3px;
}
a:hover {
	text-decoration-color: var(--mint);
}

code {
	font-family: var(--font-mono);
	font-size: 0.87em;
	background: rgba(147, 168, 158, 0.13);
	padding: 1px 6px;
	border-radius: 5px;
	color: var(--mint-pale);
}

pre {
	background: #05080b;
	border: 1px solid var(--line);
	border-left: 3px solid rgba(66, 242, 154, 0.5);
	border-radius: var(--radius-sm);
	padding: 16px 18px;
	overflow-x: auto;
}
pre code {
	background: none;
	padding: 0;
	color: var(--mint-pale);
	font-size: 0.86rem;
}

ul,
ol {
	padding-left: 22px;
}
li {
	margin: 8px 0;
}
.plain {
	list-style: none;
	padding-left: 0;
}
.plain > li {
	margin: 12px 0;
	padding-left: 18px;
	border-left: 2px solid var(--line-strong);
}
.next > li {
	border-left-color: rgba(66, 242, 154, 0.55);
}
ol.signs > li {
	margin: 15px 0;
}

/* --------------------------------------------------------------- surfaces -- */

.callout {
	margin: 28px 0;
	padding: 20px 22px;
	border: 1px solid var(--line-strong);
	border-left: 3px solid var(--mint);
	border-radius: var(--radius);
	background: linear-gradient(160deg, rgba(255, 255, 255, 0.04), transparent 60%), var(--panel);
	box-shadow: 0 16px 40px -28px rgba(0, 0, 0, 1);
}
.callout-warn {
	border-left-color: var(--warn);
}
.callout h2 {
	margin: 0 0 8px;
	font-size: 1.1rem;
}
.callout p:last-child {
	margin-bottom: 0;
}

.grid {
	display: grid;
	gap: 16px;
	grid-template-columns: repeat(auto-fit, minmax(252px, 1fr));
	margin: 26px 0;
}
.grid section {
	position: relative;
	padding: 22px 22px 20px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--panel);
	box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
	transition:
		border-color 260ms var(--ease),
		transform 260ms var(--ease),
		box-shadow 260ms var(--ease);
}
.grid section:hover {
	border-color: rgba(66, 242, 154, 0.28);
	transform: translateY(-2px);
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.07),
		0 18px 38px -24px rgba(0, 0, 0, 1);
}
.grid h3 {
	margin: 0 0 7px;
	color: var(--mint-pale);
}
.grid p:last-child {
	margin-bottom: 0;
}

.defs {
	margin: 22px 0;
}
.defs dt {
	font-weight: 650;
	color: var(--mint-pale);
	margin-top: 20px;
}

/*
 * A term that links somewhere is a navigation target, not prose.
 *
 * In the documentation index these are the way through to each section, and as
 * a bare inline link the tappable area was the 21px height of the text. Given
 * its own block with padding it becomes something a thumb can actually land on,
 * without changing how the list reads.
 */
.defs dt a {
	display: inline-block;
	padding: 5px 0;
}
.defs dd {
	margin: 5px 0 0;
	padding-left: 16px;
	border-left: 2px solid var(--line);
}

.faq-item {
	border-top: 1px solid var(--line);
	padding-top: 4px;
}
.faq-item h2 {
	font-size: 1.2rem;
	margin-top: 34px;
}

.button {
	display: inline-block;
	background: linear-gradient(135deg, var(--mint), var(--teal));
	color: #04140c;
	font-weight: 670;
	padding: 12px 22px;
	border-radius: var(--radius-sm);
	text-decoration: none;
	transition:
		filter 160ms var(--ease),
		transform 160ms var(--ease),
		box-shadow 160ms var(--ease);
}
.button:hover {
	filter: brightness(1.07);
	transform: translateY(-1px);
	box-shadow: 0 14px 30px -16px rgba(66, 242, 154, 0.75);
}
.button-quiet {
	background: transparent;
	border: 1px solid var(--line-strong);
	color: var(--text);
}
.button-quiet:hover {
	border-color: var(--mint);
	color: var(--mint);
	box-shadow: none;
}

/* --------------------------------------------------------------- projects -- */

/*
 * The other things MASTERPANEL builds.
 *
 * A fixed-height logo plate with the mark at its natural width, rather than
 * forcing everything square. Two of these are square icons and one is a wide
 * wordmark; boxing them identically would either crush the wordmark to
 * illegibility or float the icons in empty space.
 */
.projects {
	display: grid;
	gap: 16px;
	grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
	margin: 30px 0;
	padding: 0;
	list-style: none;
}
.project {
	display: flex;
	flex-direction: column;
	margin: 0;
	padding: 24px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--panel);
	transition:
		border-color 260ms var(--ease),
		transform 260ms var(--ease);
}
.project:hover {
	border-color: rgba(66, 242, 154, 0.28);
	transform: translateY(-2px);
}
.project-plate {
	display: flex;
	align-items: center;
	height: 44px;
	margin-bottom: 16px;
}
.project-plate img {
	max-height: 40px;
	max-width: 170px;
	width: auto;
	height: auto;
}
.project h3 {
	margin: 0 0 4px;
	color: var(--text);
}
.project .domain {
	font-family: var(--font-mono);
	font-size: 0.8rem;
	color: var(--faint);
}
.project p {
	margin: 12px 0 0;
	font-size: 0.94rem;
}
.project .go {
	margin-top: auto;
	padding-top: 16px;
	font-family: var(--font-small);
	font-size: 0.86rem;
}

/* ------------------------------------------------------------ furniture --- */

.crumbs {
	font-family: var(--font-small);
	font-size: 0.82rem;
	margin: 0 0 20px;
}
.crumbs ol {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin: 0;
	padding: 0;
}
.crumbs li + li::before {
	content: '›';
	color: var(--faint);
	margin-right: 8px;
}
.crumbs a {
	color: var(--muted);
	text-decoration: none;
}
.crumbs a:hover {
	color: var(--mint);
	text-decoration: underline;
}
.crumbs [aria-current='page'] {
	color: var(--faint);
}

.reviewed {
	margin-top: 52px;
	padding-top: 16px;
	border-top: 1px solid var(--line);
	font-family: var(--font-small);
	font-size: 0.82rem;
	color: var(--faint);
}

.fineprint {
	margin-top: 40px;
	font-size: 0.8rem;
	color: var(--faint);
}

.empty {
	padding: 44px 28px;
	border: 1px dashed var(--line-strong);
	border-radius: var(--radius);
	text-align: center;
	color: var(--muted);
}

main {
	padding: 42px 0 20px;
}
main.has-hero {
	padding-top: 0;
}

.site-foot {
	border-top: 1px solid var(--line);
	padding: 40px 0 56px;
	margin-top: 48px;
	font-family: var(--font-small);
	font-size: 0.88rem;
	color: var(--muted);
	background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.35));
}
.foot-lead {
	color: var(--text);
	margin-top: 0;
}
.site-foot nav {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 20px;
	margin: 16px 0 22px;
}
.site-foot nav a {
	color: var(--muted);
	text-decoration: none;
}
.site-foot nav a:hover {
	color: var(--mint);
	text-decoration: underline;
}
.site-foot .fineprint {
	margin-top: 0;
	max-width: 66ch;
}

/* ------------------------------------------------------- foot attribution -- */

/*
 * Credit where it belongs, on every page.
 *
 * Two blocks that have to survive being read quickly: where the original SDA
 * actually lives, and who publishes this. The first is the more important of
 * the two — somebody who came here searching for SDA should be able to leave
 * with the genuine repository rather than the clone they would otherwise click.
 */
.foot-brand {
	display: flex;
	flex-wrap: wrap;
	gap: 18px 28px;
	align-items: center;
	justify-content: space-between;
	margin: 26px 0 22px;
	padding: 18px 20px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--panel);
}
.foot-origin {
	/* `min-width: 0` so the flex item may shrink past the long URL inside it;
	   without it the item floors at its min-content width and overflows. */
	flex: 1 1 380px;
	min-width: 0;
	margin: 0;
	max-width: 68ch;
	font-family: var(--font-small);
	font-size: 0.9rem;
	line-height: 1.6;
	color: var(--body);
}
.foot-origin strong {
	color: var(--text);
}

/* The company mark. A link, because a brand you cannot follow is decoration. */
.powered {
	display: inline-flex;
	align-items: center;
	gap: 11px;
	padding: 9px 15px 9px 11px;
	border: 1px solid var(--line-strong);
	border-radius: 999px;
	text-decoration: none;
	color: var(--text);
	/* Not `nowrap`: at 375px the pill could not shrink and pushed the footer
	   past the viewport. The two words wrap onto their own lines instead. */
	white-space: normal;
	max-width: 100%;
	transition:
		border-color 160ms var(--ease),
		background-color 160ms var(--ease),
		transform 160ms var(--ease);
}
.powered:hover {
	border-color: var(--mint);
	background: var(--panel-hi);
	transform: translateY(-1px);
}
.powered img {
	display: block;
	width: 28px;
	height: 28px;
	border-radius: 6px;
}
.powered span {
	display: grid;
	line-height: 1.25;
}
.powered .powered-by {
	font-family: var(--font-small);
	font-size: 0.72rem;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	color: var(--faint);
}
.powered strong {
	font-size: 0.95rem;
	font-weight: 680;
	letter-spacing: 0.02em;
}
.powered:hover strong {
	color: var(--mint);
}

/*
 * The same pointer, used mid-page where SDA is being discussed.
 *
 * Deliberately not styled as a warning. Somebody choosing the original over
 * this is making a reasonable choice, and a box that looks like an alarm would
 * be arguing with them rather than helping.
 */
.origin-note {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 14px;
	margin: 26px 0;
	padding: 16px 18px;
	border: 1px solid var(--line-strong);
	border-left: 3px solid var(--mint-pale);
	border-radius: var(--radius);
	background: var(--bg-lift);
}
.origin-note p {
	flex: 1 1 300px;
	min-width: 0;
	margin: 0;
	font-size: 0.95rem;
	color: var(--body);
}
.origin-note .button {
	flex: 0 0 auto;
	font-size: 0.92rem;
	padding: 10px 18px;
}

/* ------------------------------------------------------------------- ask -- */

/*
 * The review request.
 *
 * Sits at the end of a page, after whatever it is asking about, and is styled
 * to read as an aside rather than a call to action — no accent bar, no filled
 * panel, nothing that competes with the content above it. A request that shouts
 * is a request that gets ignored on a site like this one, where the reader has
 * usually arrived because something went wrong.
 */
.ask {
	display: grid;
	gap: 18px;
	margin: 48px 0 8px;
	padding: 24px 26px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--bg-lift);
}
.ask-body h2 {
	margin: 0 0 10px;
	font-size: 1.15rem;
}
.ask-body p {
	margin: 0 0 10px;
	max-width: 66ch;
}
.ask-body p:last-child {
	margin-bottom: 0;
}
.ask-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	align-items: center;
}

/* ------------------------------------------------------------------ give -- */

/* Three ways to pay somebody else, side by side and equally weighted. */
.give {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
	gap: 12px;
	margin: 24px 0;
}
.give-card {
	display: grid;
	gap: 4px;
	padding: 16px 18px;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius);
	background: var(--panel);
	text-decoration: none;
	transition:
		border-color 160ms var(--ease),
		background-color 160ms var(--ease),
		transform 160ms var(--ease);
}
.give-card:hover {
	border-color: var(--mint);
	background: var(--panel-hi);
	transform: translateY(-2px);
}
.give-what {
	font-weight: 660;
	color: var(--text);
}
.give-card:hover .give-what {
	color: var(--mint);
}
.give-detail {
	font-family: var(--font-small);
	font-size: 0.85rem;
	color: var(--muted);
}

/*
 * A payment address, displayed to be read rather than merely to be copied.
 *
 * The full string is always visible and never truncated with an ellipsis: the
 * page asks people to compare the first and last four characters after pasting,
 * and it cannot ask that while hiding the middle. Monospace and a generous line
 * height because these get compared character by character.
 */
.wallets {
	display: grid;
	gap: 14px;
	margin: 24px 0;
	padding: 0;
	list-style: none;
}
.wallet {
	padding: 18px 20px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--panel);
}
.wallet-head {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 10px;
}
.wallet-asset {
	font-family: var(--font-display);
	font-size: 1.05rem;
	font-weight: 660;
	color: var(--text);
}
.wallet-chain {
	font-family: var(--font-small);
	font-size: 0.85rem;
	color: var(--mint-pale);
}
.wallet .hint {
	margin: 6px 0 12px;
}
.wallet-address {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px;
}
.wallet-address code {
	flex: 1 1 320px;
	min-width: 0;
	padding: 11px 13px;
	background: #05080b;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius-sm);
	color: var(--mint-pale);
	font-size: 0.84rem;
	line-height: 1.7;
	/* `anywhere`, not `break-word`: this must never widen its container, and a
	   payment address has no word boundaries to break on politely. */
	overflow-wrap: anywhere;
	user-select: all;
}
.wallet-address button {
	flex: 0 0 auto;
	font-family: inherit;
	font-size: 0.9rem;
	font-weight: 640;
	padding: 11px 18px;
	border-radius: var(--radius-sm);
	border: 1px solid var(--line-strong);
	background: transparent;
	color: var(--text);
	cursor: pointer;
	transition:
		border-color 160ms var(--ease),
		color 160ms var(--ease);
}
.wallet-address button:hover {
	border-color: var(--mint);
	color: var(--mint);
}

/* ------------------------------------------------------------------ forms -- */

/*
 * The support form.
 *
 * There were no form rules at all until now, so every control fell back to the
 * browser's defaults: labels sitting inline beside their inputs, a `select`
 * painted in system grey, a textarea the size of a postage stamp. On a page
 * whose entire argument is "we are the careful ones, the other download is the
 * fake", a form that looks unfinished is not a cosmetic problem — it is the
 * page contradicting itself at the exact moment it asks for trust.
 *
 * Fields are a grid rather than floated labels: label above control, one column,
 * consistent gap. That survives translation and long labels, which two-column
 * label layouts do not.
 */

.form {
	display: grid;
	gap: 22px;
	margin: 28px 0;
	padding: 26px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background:
		radial-gradient(120% 90% at 0% 0%, rgba(66, 242, 154, 0.05), transparent 55%), var(--panel);
}

.field {
	display: grid;
	gap: 8px;
}

/*
 * `display: grid` on a class outranks the user agent's `[hidden]{display:none}`,
 * so a hidden field would otherwise stay on screen. The attachment field depends
 * on this: without script it must not appear at all, because there is nothing
 * behind it to accept the file.
 */
[hidden] {
	display: none !important;
}

.field > label,
.form > label {
	font-family: var(--font-small);
	font-size: 0.92rem;
	font-weight: 620;
	color: var(--text);
	letter-spacing: 0.01em;
}

/* The one-line explanation under a control, not a second label. */
.field .hint,
.form .hint {
	margin: 0;
	font-family: var(--font-small);
	font-size: 0.86rem;
	line-height: 1.5;
	color: var(--muted);
}

.input,
.form input[type='text'],
.form input[type='email'],
.form input[type='password'],
.form textarea,
.form select {
	width: 100%;
	padding: 12px 14px;
	font-family: inherit;
	font-size: 1rem;
	line-height: 1.5;
	color: var(--text);
	background: var(--bg-lift);
	border: 1px solid var(--line-strong);
	border-radius: var(--radius-sm);
	transition:
		border-color 140ms var(--ease),
		box-shadow 140ms var(--ease),
		background-color 140ms var(--ease);
}

.form textarea {
	min-height: 190px;
	resize: vertical;
}

.form input::placeholder,
.form textarea::placeholder {
	color: var(--faint);
}

.form input:hover,
.form textarea:hover,
.form select:hover {
	border-color: var(--line-strong);
	background: var(--panel-hi);
}

/*
 * A visible focus ring, on purpose.
 *
 * The default outline is the first thing a redesign removes and the first thing
 * a keyboard user misses. This replaces it with something louder, not quieter.
 */
.form input:focus-visible,
.form textarea:focus-visible,
.form select:focus-visible,
.form button:focus-visible,
.dropzone:focus-visible {
	outline: none;
	border-color: var(--mint);
	box-shadow:
		0 0 0 3px rgba(66, 242, 154, 0.18),
		0 0 0 1px var(--mint);
}

/* `appearance: none` removes the system arrow, so one is drawn back on. */
.form select {
	appearance: none;
	padding-right: 42px;
	background-image:
		linear-gradient(45deg, transparent 50%, var(--muted) 50%),
		linear-gradient(135deg, var(--muted) 50%, transparent 50%);
	background-position:
		calc(100% - 20px) calc(50% + 2px),
		calc(100% - 14px) calc(50% + 2px);
	background-size:
		6px 6px,
		6px 6px;
	background-repeat: no-repeat;
}
.form select:hover {
	background-image:
		linear-gradient(45deg, transparent 50%, var(--mint) 50%),
		linear-gradient(135deg, var(--mint) 50%, transparent 50%);
}

/* A field the server rejected. Paired with a message, never colour alone. */
.field.is-bad .input,
.field.is-bad input,
.field.is-bad textarea,
.field.is-bad select {
	border-color: var(--danger);
}
.field-error {
	margin: 0;
	font-family: var(--font-small);
	font-size: 0.86rem;
	color: var(--danger);
}

.controls {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	align-items: center;
	margin: 0;
}

.form button,
.controls button,
button.button {
	font-family: inherit;
	font-size: 0.98rem;
	font-weight: 670;
	padding: 12px 22px;
	border: 1px solid transparent;
	border-radius: var(--radius-sm);
	color: #04140c;
	background: linear-gradient(135deg, var(--mint), var(--teal));
	cursor: pointer;
	transition:
		filter 160ms var(--ease),
		transform 160ms var(--ease),
		box-shadow 160ms var(--ease);
}
.form button:hover,
.controls button:hover,
button.button:hover {
	filter: brightness(1.07);
	transform: translateY(-1px);
	box-shadow: 0 14px 30px -16px rgba(66, 242, 154, 0.75);
}
.form button:active,
.controls button:active,
button.button:active {
	transform: translateY(0);
}

.form button.secondary,
.controls button.secondary {
	background: transparent;
	border-color: var(--line-strong);
	color: var(--text);
}
.form button.secondary:hover,
.controls button.secondary:hover {
	border-color: var(--mint);
	color: var(--mint);
	box-shadow: none;
}
.form button:disabled,
.controls button:disabled {
	opacity: 0.5;
	cursor: not-allowed;
	filter: none;
	transform: none;
	box-shadow: none;
}

/* ------------------------------------------------------------ attachments -- */

/*
 * The file picker.
 *
 * The native control is a grey button with a filename beside it and no sense of
 * what is allowed until the file is refused. This is a drop target that states
 * the limits up front, because the alternative is a person recording a
 * screen capture and only then being told video is capped.
 *
 * The real `<input type="file">` stays in the DOM and stays keyboard-reachable;
 * it is clipped rather than `display: none`, which would take it out of the tab
 * order and off screen readers.
 */
.dropzone {
	position: relative;
	display: grid;
	place-items: center;
	gap: 6px;
	padding: 26px 20px;
	text-align: center;
	border: 1.5px dashed var(--line-strong);
	border-radius: var(--radius);
	background: var(--bg-lift);
	cursor: pointer;
	transition:
		border-color 140ms var(--ease),
		background-color 140ms var(--ease);
}
.dropzone:hover,
.dropzone.is-over {
	border-color: var(--mint);
	background: var(--panel-hi);
}
.dropzone strong {
	color: var(--text);
	font-weight: 640;
}
.dropzone .hint {
	font-size: 0.84rem;
}
.dropzone input[type='file'] {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.attachments {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
	gap: 12px;
	margin: 0;
	padding: 0;
	list-style: none;
}
.attachment {
	position: relative;
	display: grid;
	gap: 8px;
	padding: 10px;
	border: 1px solid var(--line);
	border-radius: var(--radius-sm);
	background: var(--panel);
	overflow: hidden;
}
.attachment img,
.attachment video {
	display: block;
	width: 100%;
	height: 116px;
	/*
	 * `contain`, not `cover`. A tidy grid of centre-crops is the wrong trade for
	 * this content: these are screenshots of a problem, and the crop reliably
	 * throws away the edges — which is where the error dialog, the address bar
	 * and the thing being pointed at usually are. Letterboxed and complete beats
	 * neat and partial.
	 */
	object-fit: contain;
	border-radius: 8px;
	background: var(--bg);
}
.attachment .meta {
	display: flex;
	justify-content: space-between;
	gap: 8px;
	font-family: var(--font-small);
	font-size: 0.78rem;
	color: var(--muted);
}
.attachment .bytes {
	font-family: var(--font-numeric);
	font-variant-numeric: tabular-nums;
}
.attachment button {
	padding: 6px 10px;
	font-size: 0.8rem;
	background: transparent;
	border: 1px solid var(--line-strong);
	color: var(--body);
	border-radius: 8px;
	cursor: pointer;
}
.attachment button:hover {
	border-color: var(--danger);
	color: var(--danger);
}
.attachment.is-uploading {
	opacity: 0.6;
}

/* -------------------------------------------------------------- the thread -- */

/*
 * A report, read by the person who filed it.
 *
 * The old page was a status word and a wall of text. Someone who has just been
 * robbed and has written to a stranger for help wants two things from this page:
 * evidence a human has it, and somewhere to add the thing they forgot. So the
 * state is a labelled row rather than one adjective, and every message says who
 * said it.
 */
.status {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 5px 12px 5px 10px;
	font-family: var(--font-small);
	font-size: 0.82rem;
	font-weight: 640;
	letter-spacing: 0.02em;
	border-radius: 999px;
	border: 1px solid var(--line-strong);
	color: var(--body);
	background: var(--panel-hi);
}
.status::before {
	content: '';
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: currentColor;
}
.status-open {
	color: var(--mint-pale);
	border-color: rgba(66, 242, 154, 0.35);
}
.status-assigned {
	color: var(--mint-bright);
	border-color: rgba(108, 255, 190, 0.45);
	background: rgba(66, 242, 154, 0.08);
}
.status-waiting {
	color: var(--warn);
	border-color: rgba(245, 194, 107, 0.4);
	background: rgba(245, 194, 107, 0.08);
}
.status-resolved {
	color: var(--muted);
}
.status-declined {
	color: var(--danger);
	border-color: rgba(255, 138, 138, 0.35);
}

/* A live-looking dot only where something is actually in progress. */
.status-assigned::before {
	animation: pulse 2.4s var(--ease) infinite;
}
@keyframes pulse {
	0%,
	100% {
		opacity: 1;
		box-shadow: 0 0 0 0 rgba(108, 255, 190, 0.5);
	}
	50% {
		opacity: 0.75;
		box-shadow: 0 0 0 5px rgba(108, 255, 190, 0);
	}
}

/* Title on the left, sign-out on the right, rather than orphaned beneath it. */
.admin-head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
}
.admin-head h1 {
	margin: 0;
}

.ticket-head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px;
	margin-bottom: 6px;
}
.ticket-ref {
	font-family: var(--font-numeric);
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.06em;
	font-size: 1.05rem;
	color: var(--text);
}

.thread {
	display: grid;
	gap: 16px;
	margin: 26px 0;
	padding: 0;
	list-style: none;
}
.message {
	display: grid;
	gap: 10px;
	padding: 18px 20px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--panel);
}
/* From us: marked, because "has a human seen this" is the whole question. */
.message-us {
	border-left: 3px solid var(--mint);
	background: linear-gradient(160deg, rgba(66, 242, 154, 0.06), transparent 55%), var(--panel);
}
.message-reporter {
	border-left: 3px solid var(--line-strong);
}
.message-head {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 10px;
	font-family: var(--font-small);
	font-size: 0.84rem;
}
.message-who {
	font-weight: 660;
	color: var(--text);
}
.message-us .message-who {
	color: var(--mint);
}
.message-when {
	color: var(--faint);
	font-variant-numeric: tabular-nums;
}
.message p {
	margin: 0;
	color: var(--body);
}
.message p + p {
	margin-top: 10px;
}

.thread-empty {
	padding: 22px;
	border: 1px dashed var(--line-strong);
	border-radius: var(--radius);
	color: var(--muted);
	text-align: center;
}

/* ------------------------------------------------------------- responsive -- */

@media (max-width: 720px) {
	body {
		font-size: 16px;
	}
	.wrap {
		padding: 0 18px;
	}
	.hero {
		padding: 52px 0 40px;
	}
	.band {
		padding: 44px 0;
	}
	h2 {
		margin-top: 40px;
	}

	/* The card padding is a desktop luxury; on a phone it is lost width. */
	.form {
		padding: 18px;
		gap: 18px;
	}
	.form textarea {
		min-height: 150px;
	}
	/* Full-width targets: a 12px-padded button next to another is a mis-tap. */
	.controls {
		flex-direction: column;
		align-items: stretch;
	}
	.controls button {
		width: 100%;
	}
	.attachments {
		grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
	}
	.message {
		padding: 15px 16px;
	}

	/*
	 * The header stops being sticky on a phone.
	 *
	 * Nine navigation links wrap to three rows at 375px, and stuck to the top
	 * that bar held 190px of an 812px screen — a quarter of the display, on every
	 * page, permanently, so a quarter of every article was unreachable without
	 * more scrolling. Sticky navigation earns its space when it is one compact
	 * row; at three rows it is a menu that refuses to go away.
	 *
	 * It stays at the top of the document, where a scroll upward still finds it,
	 * and the skip link still jumps past it for keyboard and screen-reader use.
	 */
	.masthead {
		position: static;
	}

	/*
	 * Two deliberate rows, rather than one row that ran out of space.
	 *
	 * The desktop header is `justify-content: space-between` with wrapping, which
	 * on a phone put the mark alone on the first line and left the links to fall
	 * underneath it, ragged and hard against the left edge. Nothing was broken and
	 * it looked broken — the giveaway of a wrapped flex row is that the two halves
	 * are aligned to different things.
	 *
	 * So on a narrow screen it stops being a row at all. The mark is centred on a
	 * line of its own, a rule separates it, and the links are centred beneath —
	 * symmetrical, which reads as a decision instead of an accident.
	 */
	.masthead .wrap {
		display: block;
		padding: 0 18px;
	}
	.brand {
		justify-content: center;
		padding: 13px 0 12px;
	}
	.masthead nav {
		justify-content: center;
		gap: 2px 4px;
		padding: 9px 0 11px;
		border-top: 1px solid var(--line);
	}

	/*
	 * Commands wrap instead of scrolling sideways.
	 *
	 * The verification page exists to get somebody to actually run a checksum
	 * before they open an installer. In a 339px box those commands were a
	 * horizontal scrollbar showing about half of each one — and a command you
	 * have to drag through to read is a command people skip, on the one page
	 * where skipping it is how the account gets taken.
	 *
	 * Soft wrapping inserts no characters, so copying still yields the original
	 * single line.
	 */
	pre {
		white-space: pre-wrap;
		overflow-wrap: anywhere;
		padding: 13px 14px;
	}
	pre code {
		font-size: 0.8rem;
	}
	/*
	 * Stacked, and the brand pill goes full width rather than stranded right.
	 *
	 * `flex-wrap: nowrap` is the load-bearing line. A column flex container that
	 * is allowed to wrap sizes each line to its contents rather than to the
	 * container, so `align-items: stretch` stretched the text to the width of
	 * the longest unbreakable thing in it — the SDA repository URL — and every
	 * page on the site came out 7px wider than a 375px phone.
	 */
	.foot-brand {
		flex-direction: column;
		flex-wrap: nowrap;
		align-items: stretch;
		gap: 16px;
	}
	/*
	 * `flex-basis` changes meaning with the direction, and this container changes
	 * direction here.
	 *
	 * `flex: 1 1 380px` on the text is a width hint for the desktop row. Turned
	 * into a column, the basis applies to the main axis instead — so 380px became
	 * a *height*, and four lines of text sat in a 380px box with an empty
	 * quarter-screen underneath it, on every page. Nothing overflowed and no tap
	 * target shrank, which is why the layout sweep passed it and only looking at
	 * the page found it.
	 */
	.foot-origin {
		flex: 0 0 auto;
	}
	.powered {
		justify-content: center;
	}
	.origin-note .button {
		width: 100%;
		text-align: center;
	}
}

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

/*
 * The reading measure, applied to text rather than to a wrapper.
 *
 * Constraining a container would drag the card grids and the project list in
 * with it, which is the thing this layout exists to avoid. Limiting the text
 * elements directly lets prose stay at a comfortable 68 characters while wide
 * structure uses the whole 1080px band — that contrast is most of what stops a
 * page reading like a document.
 */
article > p,
article > h2,
article > h3,
article > ul,
article > ol,
article > dl,
article > .callout,
.faq-item > p,
.faq-item > ul,
.faq-item > ol {
	max-width: 68ch;
}

/* Grids and the hero deliberately opt out. */
article > .grid,
article > .projects,
article > .signals {
	max-width: none;
}
