/*
 * 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;
}

/* --------------------------------------------------------------- diagrams -- */

/*
 * Explanatory figures.
 *
 * These pages had no images at all, which for "why is my code refused" is a
 * genuine gap: the reader is trying to match what is on their screen against
 * prose. Screenshots of Steam and Windows are not ours to publish and would go
 * stale with every interface change, so the diagrams here explain the mechanism
 * instead — the part that does not change and that nobody else illustrates.
 *
 * Inline SVG rather than image files: no extra request, no layout shift, it
 * inherits the page's own colours in both themes, and the text inside it is
 * real text that a screen reader and a search engine can both read.
 */
figure.diagram {
	margin: 28px 0;
	padding: 22px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--panel);
	/*
	 * Scrolls rather than shrinks.
	 *
	 * A 640-unit viewBox rendered into a 360px phone is scaled to about 0.40,
	 * which turns 14px labels into 5px ones — present, technically responsive,
	 * and completely unreadable. Below the floor the figure scrolls sideways
	 * inside its own box instead, which keeps the type legible and keeps the
	 * page itself from scrolling horizontally.
	 */
	overflow-x: auto;
}
figure.diagram svg {
	display: block;
	width: 100%;
	min-width: 540px;
	height: auto;
	overflow: visible;
}
figure.diagram figcaption {
	margin-top: 16px;
	padding-top: 14px;
	border-top: 1px solid var(--line);
	color: var(--muted);
	font-size: 0.92rem;
	line-height: 1.55;
}
/* The parts of a diagram, named so the SVG carries no literal colours. */
.dg-line {
	stroke: var(--line-strong);
}
.dg-key {
	stroke: var(--mint);
}
.dg-bad {
	stroke: var(--danger);
}
.dg-warn {
	stroke: var(--warn);
}
.dg-fill-panel {
	fill: var(--panel-hi);
}
.dg-fill-key {
	fill: var(--mint);
}
.dg-fill-bad {
	fill: var(--danger);
}
.dg-fill-warn {
	fill: var(--warn);
}
.dg-text {
	fill: var(--body);
	font-size: 14px;
	font-family: inherit;
}
.dg-label {
	fill: var(--muted);
	font-size: 12.5px;
	font-family: inherit;
	letter-spacing: 0.04em;
	text-transform: uppercase;
}
.dg-strong {
	fill: var(--text);
	font-size: 15px;
	font-weight: 600;
	font-family: inherit;
}

/* --------------------------------------------------------------- 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;
}

/* ------------------------------------------------------------- guide kit -- */

/*
 * Furniture for the troubleshooting guides.
 *
 * ## The problem this solves
 *
 * The guides were correct and unreadable. Measured on
 * /steam-guard-code-not-working: 1033 words across 27 top-level blocks, with a
 * longest unbroken run of *thirteen* consecutive headings and paragraphs. A
 * reader arriving mid-problem — their code was just refused, they are annoyed,
 * they may be about to lose an inventory — met an undifferentiated grey column
 * and had to read it linearly to find out whether it applied to them.
 *
 * Every number that reader actually wants was buried inside a sentence: the
 * thirty-second window, the two-day transfer hold, the fifteen-day removal
 * hold, the five-day password-reset restriction. Those are the page. They
 * belonged in numerals large enough to see while scrolling past.
 *
 * ## The approach
 *
 * Nothing here is decoration bolted onto prose. Each component answers a
 * question a worried reader asks, in the order they ask it:
 *
 *   .answer      — is this my problem, and what is the fix?   (first thing)
 *   .jump        — which part of this page is mine?
 *   .stat-strip  — what is this going to cost me?
 *   .steps       — what do I do, in order?
 *   .check       — which of these applies to me?
 *   .pull        — the one line worth remembering if they skim
 *   .link-cards  — that was not it, where next?
 *
 * The palette is unchanged. This adds rhythm and entry points, not new colour,
 * because a security page that starts looking like a landing page stops being
 * believed.
 */

/*
 * Scroll position, as a hairline.
 *
 * Long troubleshooting pages give no sense of remaining depth, and "how much
 * more of this is there" is exactly what makes somebody leave. Driven by
 * scroll() so it costs no JavaScript — which matters here, because the content
 * security policy carries no unsafe-inline and this project would rather add a
 * component than an exception.
 *
 * Inside @supports so browsers without scroll-driven animation get no bar at
 * all, rather than one that animates itself once on a time-based default.
 */
.progress {
	display: none;
}
@supports (animation-timeline: scroll()) {
	.progress {
		display: block;
		position: fixed;
		inset: 0 0 auto;
		height: 2px;
		z-index: 30;
		transform-origin: 0 50%;
		background: linear-gradient(90deg, var(--teal), var(--mint), var(--mint-bright));
		scale: 0 1;
		animation: progress-grow linear both;
		animation-timeline: scroll(root block);
	}
	@keyframes progress-grow {
		to {
			scale: 1 1;
		}
	}
}

/* The byline row: when it was checked, how long it takes, what it rests on. */
.guide-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px 14px;
	margin: 0 0 26px;
	font-family: var(--font-small);
	font-size: 0.82rem;
	color: var(--faint);
}
.guide-meta > * {
	display: inline-flex;
	align-items: center;
	gap: 7px;
}
.guide-meta .dot {
	width: 3px;
	height: 3px;
	border-radius: 50%;
	background: var(--line-strong);
}
/* A claim about sourcing, so it is written as one rather than styled as a badge. */
.guide-meta .sourced {
	color: var(--mint-pale);
}

/*
 * The answer, before the explanation.
 *
 * Somebody who reads one box and leaves should still leave fixed. This is also
 * the block a search engine lifts for a featured snippet, which is why the
 * first sentence inside it is written to stand alone with no antecedents.
 */
.answer {
	position: relative;
	margin: 30px 0 34px;
	max-width: 72ch;
	padding: 24px 26px 22px 30px;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius);
	background: linear-gradient(150deg, rgba(66, 242, 154, 0.07), transparent 58%), var(--panel-hi);
	box-shadow: 0 22px 50px -34px rgba(0, 0, 0, 1);
	overflow: hidden;
}
/* A drawn bar rather than a border, so it can carry a gradient. */
.answer::before {
	content: '';
	position: absolute;
	inset: 0 auto 0 0;
	width: 3px;
	background: linear-gradient(180deg, var(--mint-bright), var(--teal));
}
.answer > .eyebrow {
	display: block;
	margin-bottom: 9px;
	font-family: var(--font-small);
	font-size: 0.71rem;
	font-weight: 700;
	letter-spacing: 0.13em;
	text-transform: uppercase;
	color: var(--mint);
}
.answer p {
	margin: 0 0 12px;
	font-size: 1.09rem;
	line-height: 1.6;
	color: var(--text);
}
.answer p:last-child {
	margin-bottom: 0;
}
.answer strong {
	color: var(--mint-pale);
}

/*
 * Jump links.
 *
 * A contents list on a thousand-word page is not navigation furniture, it is a
 * promise that the page is organised — and it lets somebody whose problem is
 * the fourth heading skip the three that are not theirs instead of bouncing.
 */
.jump {
	margin: 0 0 34px;
	padding: 16px 18px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: linear-gradient(180deg, rgba(255, 255, 255, 0.02), transparent);
}
.jump > p {
	margin: 0 0 11px;
	font-family: var(--font-small);
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--muted);
}
.jump ul {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin: 0;
	padding: 0;
	list-style: none;
}
.jump li {
	margin: 0;
}
.jump a {
	display: block;
	padding: 7px 13px;
	border: 1px solid var(--line-strong);
	border-radius: 999px;
	font-size: 0.88rem;
	color: var(--body);
	text-decoration: none;
	transition:
		border-color 200ms var(--ease),
		color 200ms var(--ease),
		background-color 200ms var(--ease);
}
.jump a:hover,
.jump a:focus-visible {
	border-color: rgba(66, 242, 154, 0.5);
	background: rgba(66, 242, 154, 0.08);
	color: var(--mint-pale);
}

/*
 * The numbers, at a size you can read while scrolling.
 *
 * These pages are about costs measured in days, and a reader choosing between
 * two routes wants the difference between 2 and 15 without parsing a clause.
 * Set in the numeric face at display size, because that contrast — one large
 * figure against a small uppercase label — is most of what makes a page look
 * considered rather than typed.
 */
.stat-strip {
	display: grid;
	gap: 14px;
	grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
	margin: 30px 0;
	padding: 0;
	list-style: none;
}
.stat-strip > li {
	margin: 0;
	padding: 20px 20px 18px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--panel);
	box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}
.stat-strip b {
	display: block;
	font-family: var(--font-numeric);
	font-size: clamp(1.9rem, 1.2rem + 2vw, 2.5rem);
	font-weight: 600;
	line-height: 1.02;
	letter-spacing: -0.02em;
	color: var(--mint);
}
.stat-strip b small {
	font-size: 0.42em;
	font-weight: 600;
	letter-spacing: 0.02em;
	color: var(--mint-pale);
}
.stat-strip span {
	display: block;
	margin-top: 8px;
	font-family: var(--font-small);
	font-size: 0.83rem;
	line-height: 1.45;
	color: var(--muted);
}
/* The expensive one, so two very different costs are not weighted the same. */
.stat-strip > li.cost {
	border-color: rgba(245, 194, 107, 0.28);
}
.stat-strip > li.cost b {
	color: var(--warn);
}

/*
 * Instructions as cards rather than list items.
 *
 * An ordered list of four sentences reads as a paragraph with bullets. Giving
 * each step its own surface and its own numeral makes it a place you can be —
 * which matters when somebody is following along with one hand on the keyboard
 * and keeps losing their line.
 */
.steps {
	counter-reset: step;
	margin: 28px 0;
	padding: 0;
	max-width: 72ch;
	list-style: none;
}
.steps > li {
	position: relative;
	margin: 0 0 12px;
	padding: 18px 20px 17px 62px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--panel);
	transition:
		border-color 240ms var(--ease),
		transform 240ms var(--ease);
}
.steps > li:hover {
	border-color: rgba(66, 242, 154, 0.26);
	transform: translateX(2px);
}
.steps > li::before {
	counter-increment: step;
	content: counter(step);
	position: absolute;
	left: 18px;
	top: 16px;
	width: 30px;
	height: 30px;
	display: grid;
	place-items: center;
	border-radius: 50%;
	border: 1px solid rgba(66, 242, 154, 0.4);
	background: rgba(66, 242, 154, 0.1);
	font-family: var(--font-numeric);
	font-size: 0.95rem;
	color: var(--mint);
}
.steps > li > strong {
	display: block;
	margin-bottom: 4px;
	color: var(--text);
	font-weight: 650;
}
.steps > li > p {
	margin: 0;
}

/*
 * Yes-and-no lists.
 *
 * Several of these pages turn on a distinction the prose kept having to
 * restate — what a stolen secret does and does not get an attacker, which
 * routes are Valve's and which are somebody else's. Two marks carry it faster.
 * The mark is paired with wording rather than replacing it, so the meaning
 * never lives in colour alone.
 */
.check {
	margin: 24px 0;
	padding: 0;
	max-width: 72ch;
	list-style: none;
}
.check > li {
	position: relative;
	margin: 0 0 11px;
	padding-left: 34px;
	line-height: 1.6;
}
.check > li::before {
	position: absolute;
	left: 0;
	top: 2px;
	width: 21px;
	height: 21px;
	display: grid;
	place-items: center;
	border-radius: 50%;
	font-size: 0.76rem;
	font-weight: 700;
}
.check > li.yes::before {
	content: '\2713';
	border: 1px solid rgba(66, 242, 154, 0.45);
	background: rgba(66, 242, 154, 0.1);
	color: var(--mint);
}
.check > li.no::before {
	content: '\2715';
	border: 1px solid rgba(255, 138, 138, 0.4);
	background: rgba(255, 138, 138, 0.09);
	color: var(--danger);
}

/*
 * The sentence worth keeping.
 *
 * One per page at most. It is the line somebody repeats to the friend about to
 * make the same mistake, so it is set to be readable from a scroll rather than
 * from a sentence.
 */
.pull {
	margin: 34px 0;
	padding: 4px 0 4px 26px;
	max-width: 66ch;
	border-left: 2px solid rgba(66, 242, 154, 0.5);
	font-family: var(--font-display);
	font-size: clamp(1.17rem, 1rem + 0.8vw, 1.42rem);
	font-weight: 620;
	line-height: 1.38;
	letter-spacing: -0.014em;
	color: var(--text);
}
.pull em {
	font-style: normal;
	color: var(--mint-pale);
}

/*
 * Where to go when this page was not it.
 *
 * The old ending was a bulleted list of bare links, which reads as a footer and
 * gets skipped. As cards with a line of description each, the next step becomes
 * a choice rather than a list — and a reader who leaves for the right page has
 * still been helped.
 */
.link-cards {
	display: grid;
	gap: 14px;
	grid-template-columns: repeat(auto-fit, minmax(258px, 1fr));
	margin: 26px 0 0;
	padding: 0;
	list-style: none;
}
.link-cards > li {
	margin: 0;
}
.link-cards a {
	display: block;
	height: 100%;
	padding: 19px 20px 17px;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--panel);
	text-decoration: none;
	transition:
		border-color 250ms var(--ease),
		transform 250ms var(--ease),
		box-shadow 250ms var(--ease);
}
.link-cards a:hover,
.link-cards a:focus-visible {
	border-color: rgba(66, 242, 154, 0.3);
	transform: translateY(-2px);
	box-shadow: 0 20px 40px -26px rgba(0, 0, 0, 1);
}
.link-cards b {
	display: block;
	margin-bottom: 5px;
	color: var(--mint-pale);
	font-weight: 650;
}
/* A margin rather than a space inside `content`, which collapses in flow. */
.link-cards b::after {
	content: '\2192';
	display: inline-block;
	margin-left: 7px;
	transition: transform 250ms var(--ease);
}
.link-cards a:hover b::after {
	transform: translateX(3px);
}
.link-cards span {
	display: block;
	font-size: 0.9rem;
	line-height: 1.5;
	color: var(--muted);
}

/*
 * Comparison tables.
 *
 * Wide content scrolls inside its own container rather than widening the
 * document, which on a phone is the difference between a readable table and a
 * page that slides sideways under the thumb.
 */
.tbl {
	margin: 28px 0;
	overflow-x: auto;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--panel);
}
.tbl table {
	width: 100%;
	min-width: 480px;
	border-collapse: collapse;
	font-size: 0.94rem;
}
.tbl th,
.tbl td {
	padding: 13px 16px;
	text-align: left;
	vertical-align: top;
	border-bottom: 1px solid var(--line);
}
.tbl thead th {
	font-family: var(--font-small);
	font-size: 0.76rem;
	font-weight: 700;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	color: var(--mint-pale);
	background: rgba(255, 255, 255, 0.025);
	border-bottom-color: var(--line-strong);
}
.tbl tbody tr:last-child th,
.tbl tbody tr:last-child td {
	border-bottom: 0;
}
.tbl tbody th {
	font-weight: 640;
	color: var(--text);
}
.tbl .num {
	font-family: var(--font-numeric);
	font-size: 1.05rem;
	color: var(--mint);
	white-space: nowrap;
}
.tbl .num.warn {
	color: var(--warn);
}

/*
 * Numbered sections.
 *
 * A small figure above each heading gives the page a spine you can feel while
 * scrolling, so you always know roughly where you are in it. Direct children
 * only, so headings inside callouts and figures are left alone.
 */
article.guide {
	counter-reset: sec;
}
article.guide > h2 {
	margin-top: 58px;
}
/*
 * Opt-in, because three of these guides number their own headings — four
 * alternative routes out of a lost authenticator, five steps to open a file.
 * An automatic counter on top of those produced "03" above "1. No phone and no
 * number", numbering the same list twice with two different numbers.
 */
article.numbered > h2::before {
	counter-increment: sec;
	content: counter(sec, decimal-leading-zero);
	display: block;
	margin-bottom: 6px;
	font-family: var(--font-numeric);
	font-size: 0.78rem;
	font-weight: 600;
	letter-spacing: 0.16em;
	color: rgba(66, 242, 154, 0.62);
}

/*
 * A heading you can link to.
 *
 * Every heading gets an id at build time; this makes it addressable by hand for
 * somebody answering a question with "the bit about clock drift". Hidden until
 * the heading is hovered or the link itself is focused, so it never competes
 * with the heading text.
 */
.anchor {
	margin-left: 10px;
	font-size: 0.72em;
	color: var(--faint);
	text-decoration: none;
	opacity: 0;
	transition: opacity 180ms var(--ease);
}
h2:hover > .anchor,
h3:hover > .anchor,
.anchor:focus-visible {
	opacity: 1;
}
.anchor:hover {
	color: var(--mint);
}
/* Headings are scroll targets, so they must not land under the masthead. */
article h2[id],
article h3[id] {
	scroll-margin-top: 88px;
}

/* New wide components opt out of the reading measure; text ones keep it. */
article > .stat-strip,
article > .link-cards,
article > .tbl,
article > .jump {
	max-width: none;
}

@media (max-width: 720px) {
	.answer {
		padding: 20px 18px 18px 22px;
	}
	.steps > li {
		padding: 16px 16px 15px 56px;
	}
	.pull {
		padding-left: 18px;
	}
	article.guide > h2 {
		margin-top: 44px;
	}
}

/* ------------------------------------------------------- stragglers -- */

/*
 * Three classes that were written into pages and never given a rule.
 *
 * Found by diffing the class names in the built HTML against the selectors in
 * this file. Nothing failed and nothing looked obviously wrong, which is why
 * they survived: an unstyled element still renders, it just renders as though
 * the stylesheet forgot about it. The retention table on /privacy had no
 * borders and no padding, the pairs lists on /verify had browser-default
 * indentation, and text marked `.muted` on /donate was drawn at full strength —
 * the one that actually changes meaning, since it is the emphasis telling a
 * reader which half of a sentence is the aside.
 */

/* Secondary text. The whole job of the class is to recede. */
.muted {
	color: var(--muted);
}

/* Term-and-value pairs, matching the definition lists elsewhere. */
.pairs {
	margin: 22px 0;
}
.pairs dt {
	margin-top: 18px;
	font-weight: 650;
	color: var(--mint-pale);
}
.pairs dd {
	margin: 5px 0 0;
	padding-left: 16px;
	border-left: 2px solid var(--line);
}

/* The data-retention table, given the same treatment as every other table. */
.retention {
	width: 100%;
	margin: 26px 0;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	border-collapse: separate;
	border-spacing: 0;
	background: var(--panel);
	overflow: hidden;
	font-size: 0.94rem;
}
.retention th,
.retention td {
	padding: 13px 16px;
	text-align: left;
	vertical-align: top;
	border-bottom: 1px solid var(--line);
}
.retention thead th {
	font-family: var(--font-small);
	font-size: 0.76rem;
	font-weight: 700;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	color: var(--mint-pale);
	background: rgba(255, 255, 255, 0.025);
	border-bottom-color: var(--line-strong);
}
.retention tbody tr:last-child th,
.retention tbody tr:last-child td {
	border-bottom: 0;
}
.retention tbody th {
	font-weight: 640;
	color: var(--text);
}
