/*
Stardust website theme: a space HUD after StarCraft II: Legacy of the Void.

Frames are 96x96 SVGs in frames/, cut as 9-slices (border-image slice 24) so
their chamfered corners keep their shape at any size. Michroma sets display
type and HUD labels; Exo 2 sets everything else. Both are OFL licensed
(fonts/*-OFL.txt). The /theme page shows every component.

Every page links this sheet first, then the sheets its views render (see
shared.Page_Kind). Each file puts every rule in one of the layers declared
here, so the layer, not the link or source order, settles a conflict between
layers; inside a layer, later sheets win ties.
*/

@layer tokens, base, layout, components, pages, utilities;

@layer tokens {
	/* Each face is subset to Basic Latin, Latin-1, a few more Latin letters and
	accents, General Punctuation, the euro, trade mark, minus, division slash and
	the arrows U+2190 to U+2199, with the layout features the site uses:
	kerning, marks, ligatures, contextual alternates and tabular figures. A
	character outside them falls back to the next family in the stack. */
	@font-face {
		font-family: "Michroma";
		src: url("/assets/fonts/michroma-dfd58ab6a43236fe6853b4788ffc174a60f91c1a0133ebe2bcad9e93bfceded0.woff2") format("woff2");
		font-weight: 400;
		font-style: normal;
		font-display: swap;
	}

	/* Code, terminals and data; its programming ligatures (=>, !=, ::) are on. */
	@font-face {
		font-family: "Fira Code";
		src: url("/assets/fonts/firacode-49d3fabcc8b33abd5a53869a369e1ecb102d9ea4cd84cc1b7dbe195d7f273f55.woff2") format("woff2");
		font-weight: 300 700;
		font-style: normal;
		font-display: swap;
	}

	/* Datastar's brand face, for the wordmark on the team card. Self-hosted like
	every other face here: data-star.dev pulls it from Google Fonts, which would be
	a third-party request on a page that makes none. */
	@font-face {
		font-family: "Audiowide";
		src: url("/assets/fonts/audiowide-c0f54194525c83638652a56093b19a015a113f203818a6ac5dff4b6a397e025d.woff2") format("woff2");
		font-weight: 400;
		font-style: normal;
		font-display: swap;
	}

	@font-face {
		font-family: "Exo 2";
		src: url("/assets/fonts/exo2-52b1c5adc0eccf61b71503acf8dd3b3acfb0696c506f0ace81269390080e2bab.woff2") format("woff2");
		font-weight: 100 900;
		font-style: normal;
		font-display: swap;
	}

	@font-face {
		font-family: "Exo 2";
		src: url("/assets/fonts/exo2-italic-3e2bd351a76122deb10bbeb7c527030612d799fb78cc7ab84df5093fb492b9da.woff2") format("woff2");
		font-weight: 100 900;
		font-style: italic;
		font-display: swap;
	}

	:root {
		color-scheme: dark;

		/* Every translucent tint in this sheet is rgb(var(--ch-*) / a%), so a theme
		redefines a channel once and each tint that uses it follows. Most colour
		tokens below are their channel at full opacity, so they follow it too. */
		--ch-void: 2 5 11;
		--ch-ink: 2 9 19;
		--ch-deep: 6 17 38;
		--ch-surface: 6 20 42;
		--ch-edge: 47 140 255;
		--ch-glow: 92 200 255;
		--ch-info: 98 210 255;
		--ch-prompt: 125 255 154;
		--ch-success: 111 224 168;
		--ch-action: 214 162 91;
		--ch-action-high: 255 170 60;
		--ch-warning: 255 193 77;
		--ch-danger: 255 107 107;
		--ch-paper: 255 255 255;
		--ch-shadow: 0 0 0;

		/* Code colours the hl- classes use that are not interface colours. */
		--color-code-string: #9fe6b8;
		--color-code-constant: #ff9f7a;
		--color-code-escape: #d7a6ff;
		--color-code-variable: #ff9d3c;
		--color-code-tag: #ff9fd0;
		/* VS Code's dark bracket colors, so DUST matches from editor to page. */
		--color-code-rainbow-1: #ffd700;
		--color-code-rainbow-2: #da70d6;
		--color-code-rainbow-3: #87cefa;

		/* Frame art. A theme swaps the file; the 9-slice geometry never changes. */
		--frame-src-modal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3Cdefs%3E %3ClinearGradient id='rim' x1='0' y1='0' x2='0' y2='1'%3E %3Cstop offset='0' stop-color='%238fdcff'/%3E %3Cstop offset='.5' stop-color='%232f8cff'/%3E %3Cstop offset='1' stop-color='%231b56a8'/%3E %3C/linearGradient%3E %3Cfilter id='glow' x='-10%25' y='-10%25' width='120%25' height='120%25'%3E %3CfeGaussianBlur stdDeviation='2'/%3E %3C/filter%3E %3C/defs%3E %3Cpath fill='%23040b18' fill-opacity='.95' d='M16 6h64l10 10v64L80 90H16L6 80V16z'/%3E %3Cpath fill='none' stroke='%232f8cff' stroke-opacity='.75' stroke-width='5' filter='url(%23glow)' d='M16 6h64l10 10v64L80 90H16L6 80V16z'/%3E %3Cpath fill='none' stroke='%2301040a' stroke-width='3' d='M17 10h62l7 7v62l-7 7H17l-7-7V17z'/%3E %3Cpath fill='none' stroke='url(%23rim)' stroke-width='3' d='M16 6h64l10 10v64L80 90H16L6 80V16z'/%3E %3Cpath fill='none' stroke='%239fe0ff' stroke-opacity='.3' d='M18 13h60l5 5v60l-5 5H18l-5-5V18z'/%3E %3Cpath fill='%23bfe9ff' d='M6 16 16 6h6l-3 3h-2l-8 8v2l-3 3z'/%3E %3Cpath fill='%23bfe9ff' fill-opacity='.7' d='M90 80 80 90h-6l3-3h2l8-8v-2l3-3z'/%3E %3C/svg%3E") /* frames/modal.svg */;
		--frame-src-panel: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3Cpath fill='%2306142a' fill-opacity='.86' d='M14 2h68l12 12v68L82 94H14L2 82V14z'/%3E %3Cpath fill='none' stroke='%231f5fae' stroke-width='2' d='M14 3h68l11 11v68L82 93H14L3 82V14z'/%3E %3Cpath fill='none' stroke='%235cc8ff' stroke-width='2' d='M3 22v-8L14 3h8M74 3h8l11 11v8M93 74v8L82 93h-8M22 93h-8L3 82v-8'/%3E %3C/svg%3E") /* frames/panel.svg */;
		--frame-src-code-brackets: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- HUD targeting brackets at the corners, no edges between. The dim fill and its pattern come from pre.panel's background in core.css. --%3E %3Cpath fill='none' stroke='%235cc8ff' stroke-width='3' d='M2 20V2h18M76 2h18v18M94 76v18H76M20 94H2V76'/%3E %3Cpath fill='none' stroke='%232f8cff' stroke-opacity='.5' d='M9 14V9h5M82 9h5v5M87 82v5h-5M14 87H9v-5'/%3E %3C/svg%3E") /* frames/code-brackets.svg */;
		--frame-src-row: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3Cpath fill='%230a1a30' fill-opacity='.92' d='M10 4h76l6 6v76l-6 6H10l-6-6V10z'/%3E %3Cpath fill='none' stroke='%231f5fae' stroke-width='2' d='M10 5h76l5 5v76l-5 5H10l-5-5V10z'/%3E %3Cpath fill='%236f8399' d='M10 1h76l7 7v6h-3V9l-5-5H11L6 9v5H3V8z'/%3E %3Cpath fill='%23d7e3ee' fill-opacity='.55' d='M11 1h74v1H11z'/%3E %3Cpath fill='%232f8cff' fill-opacity='.35' d='M5 86v-8h1v8l4 4h8v1h-8z'/%3E %3C/svg%3E") /* frames/row.svg */;
		--frame-src-field: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3Cpath fill='%23030a14' d='M8 3h80l5 5v80l-5 5H8l-5-5V8z'/%3E %3Cpath fill='none' stroke='%23000' stroke-opacity='.6' stroke-width='4' d='M8 7h80l1 1v80l-1 1H8l-1-1V8z'/%3E %3Cpath fill='none' stroke='%232a73d6' stroke-width='3' d='M8 4h80l4 4v80l-4 4H8l-4-4V8z'/%3E %3Cpath fill='none' stroke='%238fdcff' stroke-opacity='.35' d='M9 2h78'/%3E %3C/svg%3E") /* frames/field.svg */;
		--frame-src-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- One diagonal cut, a lifted face and a hairline rim, after the mood boards. A vertical gradient survives the 9-slice, because every cell in a row stretches the same way. --%3E %3Cdefs%3E%3ClinearGradient id='default-face' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23061a33'/%3E%3Cstop offset='.45' stop-color='%230f3160'/%3E%3Cstop offset='.62' stop-color='%230f3160'/%3E%3Cstop offset='1' stop-color='%23061a33'/%3E%3C/linearGradient%3E%3C/defs%3E %3Cpath fill='url(%23default-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%2301060d' stroke-width='14' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='url(%23default-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%239fd8ff' stroke-width='3.0' d='M23 7H89v66l-16 16H7V23z'/%3E %3C/svg%3E") /* frames/button.svg */;
		--frame-src-button-action: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- One diagonal cut, a lifted face and a hairline rim, after the mood boards. A vertical gradient survives the 9-slice, because every cell in a row stretches the same way. --%3E %3Cdefs%3E%3ClinearGradient id='action-face' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%232a1708'/%3E%3Cstop offset='.45' stop-color='%236b3f17'/%3E%3Cstop offset='.62' stop-color='%236b3f17'/%3E%3Cstop offset='1' stop-color='%232a1708'/%3E%3C/linearGradient%3E%3C/defs%3E %3Cpath fill='url(%23action-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%23120a03' stroke-width='14' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='url(%23action-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%23ffd08a' stroke-width='3.0' d='M23 7H89v66l-16 16H7V23z'/%3E %3C/svg%3E") /* frames/button-action.svg */;
		--frame-src-button-success: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- One diagonal cut, a lifted face and a hairline rim, after the mood boards. A vertical gradient survives the 9-slice, because every cell in a row stretches the same way. --%3E %3Cdefs%3E%3ClinearGradient id='success-face' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%230d2b20'/%3E%3Cstop offset='.45' stop-color='%2317513a'/%3E%3Cstop offset='.62' stop-color='%2317513a'/%3E%3Cstop offset='1' stop-color='%230d2b20'/%3E%3C/linearGradient%3E%3C/defs%3E %3Cpath fill='url(%23success-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%2304120c' stroke-width='14' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='url(%23success-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%23a8ecd0' stroke-width='3.0' d='M23 7H89v66l-16 16H7V23z'/%3E %3C/svg%3E") /* frames/button-success.svg */;
		--frame-src-button-danger: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- One diagonal cut, a lifted face and a hairline rim, after the mood boards. A vertical gradient survives the 9-slice, because every cell in a row stretches the same way. --%3E %3Cdefs%3E%3ClinearGradient id='danger-face' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23350d11'/%3E%3Cstop offset='.45' stop-color='%236b1a20'/%3E%3Cstop offset='.62' stop-color='%236b1a20'/%3E%3Cstop offset='1' stop-color='%23350d11'/%3E%3C/linearGradient%3E%3C/defs%3E %3Cpath fill='url(%23danger-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%23140507' stroke-width='14' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='url(%23danger-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%23ffb0ab' stroke-width='3.0' d='M23 7H89v66l-16 16H7V23z'/%3E %3C/svg%3E") /* frames/button-danger.svg */;
		--frame-src-tabs: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- Tab strip: one frame around connected tabs, chamfered top-left and bottom-right like the LotV commander tabs. --%3E %3Cpath fill='%23040f20' fill-opacity='.92' d='M16 3h77v77L80 93H3V16z'/%3E %3Cpath fill='none' stroke='%232a73d6' stroke-width='2' d='M16 4h76v76L80 92H4V16z'/%3E %3Cpath fill='none' stroke='%238fdcff' stroke-opacity='.55' stroke-width='2' d='M4 22v-6L16 4h6M92 74v6L80 92h-6'/%3E %3C/svg%3E") /* frames/tabs.svg */;

		--color-void: rgb(var(--ch-void));
		--color-deep: rgb(var(--ch-deep));
		--color-line: #1f5fae;
		--color-line-low: rgb(var(--ch-edge) / 22%);
		--color-edge: rgb(var(--ch-edge));
		--color-glow: rgb(var(--ch-glow));
		--color-text: #c9ddf2;
		--color-text-strong: #f2f8ff;
		--color-text-dim: #7d97b8;
		--color-sand: #fff4dc;
		--color-info: rgb(var(--ch-info));
		/* The prototype site's action gold, which is also the Stardust logo's. */
		--color-action: rgb(var(--ch-action));
		--color-action-high: #ffc76a;
		--color-success: rgb(var(--ch-success));
		--color-danger: rgb(var(--ch-danger));
		--color-warning: rgb(var(--ch-warning));
		--color-prompt: rgb(var(--ch-prompt));

		/* One colour per license tier, on the account page and the pricing icons.
		Community is the action gold and Solo the info blue, so each follows its
		source in the light theme; Team has its own violet and channel. */
		--color-tier-community: var(--color-action);
		--color-tier-solo: var(--color-info);
		--color-tier-team: rgb(var(--ch-tier-team));
		--ch-tier-community: var(--ch-action);
		--ch-tier-solo: var(--ch-info);
		--ch-tier-team: 215 166 255;

		/* A control's face comes from its frame, so these name what has to agree
		with it: the label on it, the selected tab's lit segment and its rim, and
		what a hover and a press do to it. */
		--color-control-text: var(--color-text-strong);
		--color-action-label: var(--color-sand);
		/* The selected tab's face: even its lighter end carries
		--color-control-text at WCAG AA. */
		--color-tab-face: linear-gradient(#2a6cc0, #0d3a78);
		--color-tab-rim: #9fe2ff;
		/* A dark face lights up under the pointer and lights up more under a
		press. The whole filter is the token, not a number, because a light face
		answers with colour instead (see the light theme). */
		--control-hover: brightness(1.3);
		--control-press: brightness(1.6);
		/* The unfilled part of a progress track, and the scanlines over an
		avatar: both sit under bright marks, so neither follows the surfaces. */
		--color-track: rgb(var(--ch-ink) / 90%);
		--color-scanline: rgb(var(--ch-shadow) / 30%);

		--font-display: "Michroma", "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
		--font-text: "Exo 2", system-ui, sans-serif;
		--font-data: "Fira Code", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

		--space-1: 0.25rem;
		--space-2: 0.5rem;
		--space-3: 0.75rem;
		--space-4: 1rem;
		--space-5: 1.5rem;
		--space-6: 2rem;
		--space-7: 3rem;
		--space-8: 4rem;

		/* Rendered width of a frame's 24 unit slice. */
		--frame-modal: 22px;
		--frame-panel: 18px;
		--frame-row: 14px;
		--frame-control: 10px;
		/* Buttons use the prototype's per-type frames, whose corner ornaments
		need a wider slice to read. */
		--frame-button: 0.75rem;

		--content-wide: 78rem;
		/* Match the topbar's one/two-row thresholds so anchors and sticky rails
		stay below the controls. The extra language control needs the wider row. */
		--topbar-height: 6.75rem;
		--motion-fast: 140ms;

		@media (min-width: 85rem) {
			--topbar-height: 3.5rem;
		}
	}

	:root:has(.topbar-two-toggles) {
		@media (max-width: 64rem) {
			--topbar-height: 10.5rem;
		}
		@media (min-width: 85rem) and (max-width: 111.99rem) {
			--topbar-height: 6.75rem;
		}
	}

	/*
	The light theme. Every pair meets WCAG 2.1 AA for its job: 4.5:1 for text,
	3:1 for a control's boundary and for large display type. theme-boot.js and
	<theme-switch> always set data-theme on <html>, so no media query is needed;
	without JavaScript the page keeps the dark theme.
	*/
	:root[data-theme="light"] {
		color-scheme: light;

		--ch-void: 247 244 238;
		/* Surfaces that are near-black scrims in the dark theme. */
		--ch-ink: 253 252 249;
		--ch-deep: 236 231 221;
		--ch-surface: 255 255 255;
		/* The one white inset highlight, which has to darken on paper. */
		--ch-paper: 23 19 14;
		--ch-edge: 47 106 168;
		--ch-glow: 29 79 134;
		--ch-info: 20 83 143;
		--ch-prompt: 37 107 50;
		/* Just dark enough to clear WCAG AA on the light code panel. */
		--ch-success: 28 100 59;
		--ch-action: 138 90 18;
		--ch-action-high: 122 74 0;
		--ch-warning: 122 82 0;
		--ch-danger: 163 34 24;
		--ch-shadow: 0 0 0;

		--color-line: #8a8377;
		--color-line-low: rgb(94 87 76 / 30%);
		--color-text: #35302a;
		--color-text-strong: #17130e;
		--color-text-dim: #5e574c;
		--color-sand: #2a241b;
		--color-action-high: #7a4a00;
		--ch-tier-team: 106 52 173;

		--color-code-string: #1a6538;

		--color-code-constant: #9a3d12;
		--color-code-escape: #6a34ad;
		--color-code-variable: #8a4a00;
		--color-code-tag: #9c2f6b;
		/* VS Code's light bracket colors, which hold AA on paper. */
		--color-code-rainbow-1: #0431fa;
		--color-code-rainbow-2: #319331;
		--color-code-rainbow-3: #7b3814;

		/* The controls are light too: a pale face of the type's own hue (the
		frames in frames/paper/), an ink label on it, and a hover that darkens
		the face instead of brightening it. */
		--color-control-text: #141b25;
		--color-action-label: #33220a;
		--color-tab-face: linear-gradient(#cfe1f6, #a6c6e8);
		--color-tab-rim: #1c5f9e;
		/* A light face has no room to brighten and must not darken under the
		hand: it deepens its colour instead, and the press adds a little light
		on top, so the control never reads as switched off while it is held. */
		--control-hover: saturate(1.5);
		--control-press: saturate(1.9) brightness(1.04);
		--color-track: rgb(var(--ch-shadow) / 14%);

		--frame-src-modal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- Generated from ../modal.svg for the light theme: see the frames/paper table in apps/web/DESIGN.md. Keep the geometry identical to the dark frame; only the colours differ. --%3E %3Cdefs%3E %3ClinearGradient id='rim' x1='0' y1='0' x2='0' y2='1'%3E %3Cstop offset='0' stop-color='%235e8fc4'/%3E %3Cstop offset='.5' stop-color='%232f6aa8'/%3E %3Cstop offset='1' stop-color='%2315406f'/%3E %3C/linearGradient%3E %3Cfilter id='glow' x='-10%25' y='-10%25' width='120%25' height='120%25'%3E %3CfeGaussianBlur stdDeviation='2'/%3E %3C/filter%3E %3C/defs%3E %3Cpath fill='%23fffefb' fill-opacity='.95' d='M16 6h64l10 10v64L80 90H16L6 80V16z'/%3E %3Cpath fill='none' stroke='%232f6aa8' stroke-opacity='.75' stroke-width='5' filter='url(%23glow)' d='M16 6h64l10 10v64L80 90H16L6 80V16z'/%3E %3Cpath fill='none' stroke='%23cfc8bb' stroke-width='3' d='M17 10h62l7 7v62l-7 7H17l-7-7V17z'/%3E %3Cpath fill='none' stroke='url(%23rim)' stroke-width='3' d='M16 6h64l10 10v64L80 90H16L6 80V16z'/%3E %3Cpath fill='none' stroke='%232f6aa8' stroke-opacity='.3' d='M18 13h60l5 5v60l-5 5H18l-5-5V18z'/%3E %3Cpath fill='%232f6aa8' d='M6 16 16 6h6l-3 3h-2l-8 8v2l-3 3z'/%3E %3Cpath fill='%232f6aa8' fill-opacity='.7' d='M90 80 80 90h-6l3-3h2l8-8v-2l3-3z'/%3E %3C/svg%3E") /* frames/paper/modal.svg */;
		--frame-src-panel: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- Generated from ../panel.svg for the light theme: see the frames/paper table in apps/web/DESIGN.md. Keep the geometry identical to the dark frame; only the colours differ. --%3E %3Cpath fill='%23fffefb' fill-opacity='.86' d='M14 2h68l12 12v68L82 94H14L2 82V14z'/%3E %3Cpath fill='none' stroke='%238a8377' stroke-width='2' d='M14 3h68l11 11v68L82 93H14L3 82V14z'/%3E %3Cpath fill='none' stroke='%232f6aa8' stroke-width='2' d='M3 22v-8L14 3h8M74 3h8l11 11v8M93 74v8L82 93h-8M22 93h-8L3 82v-8'/%3E %3C/svg%3E") /* frames/paper/panel.svg */;
		--frame-src-code-brackets: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- Generated from ../code-brackets.svg for the light theme: see the frames/paper table in apps/web/DESIGN.md. Keep the geometry identical to the dark frame; only the colours differ. --%3E %3C!-- HUD targeting brackets at the corners, no edges between. The dim fill and its pattern come from pre.panel's background in core.css. --%3E %3Cpath fill='none' stroke='%232f6aa8' stroke-width='3' d='M2 20V2h18M76 2h18v18M94 76v18H76M20 94H2V76'/%3E %3Cpath fill='none' stroke='%235e574c' stroke-opacity='.5' d='M9 14V9h5M82 9h5v5M87 82v5h-5M14 87H9v-5'/%3E %3C/svg%3E") /* frames/paper/code-brackets.svg */;
		--frame-src-row: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- Generated from ../row.svg for the light theme: see the frames/paper table in apps/web/DESIGN.md. Keep the geometry identical to the dark frame; only the colours differ. --%3E %3Cpath fill='%23fffefb' fill-opacity='.92' d='M10 4h76l6 6v76l-6 6H10l-6-6V10z'/%3E %3Cpath fill='none' stroke='%238a8377' stroke-width='2' d='M10 5h76l5 5v76l-5 5H10l-5-5V10z'/%3E %3Cpath fill='%23b9b2a4' d='M10 1h76l7 7v6h-3V9l-5-5H11L6 9v5H3V8z'/%3E %3Cpath fill='%23ffffff' fill-opacity='.55' d='M11 1h74v1H11z'/%3E %3Cpath fill='%232f6aa8' fill-opacity='.35' d='M5 86v-8h1v8l4 4h8v1h-8z'/%3E %3C/svg%3E") /* frames/paper/row.svg */;
		--frame-src-field: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- Generated from ../field.svg for the light theme: see the frames/paper table in apps/web/DESIGN.md. Keep the geometry identical to the dark frame; only the colours differ. --%3E %3Cpath fill='%23ffffff' d='M8 3h80l5 5v80l-5 5H8l-5-5V8z'/%3E %3Cpath fill='none' stroke='%235e574c' stroke-opacity='.6' stroke-width='4' d='M8 7h80l1 1v80l-1 1H8l-1-1V8z'/%3E %3Cpath fill='none' stroke='%235e574c' stroke-width='3' d='M8 4h80l4 4v80l-4 4H8l-4-4V8z'/%3E %3Cpath fill='none' stroke='%232f6aa8' stroke-opacity='.35' d='M9 2h78'/%3E %3C/svg%3E") /* frames/paper/field.svg */;
		--frame-src-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- Generated from ../button.svg for the light theme: see the frames/paper table in apps/web/DESIGN.md. Keep the geometry identical to the dark frame; only the colours differ. --%3E %3C!-- One diagonal cut, a lifted face and a hairline rim, after the mood boards. A vertical gradient survives the 9-slice, because every cell in a row stretches the same way. --%3E %3Cdefs%3E%3ClinearGradient id='default-face' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23dfeaf8'/%3E%3Cstop offset='.45' stop-color='%23b7cfec'/%3E%3Cstop offset='.62' stop-color='%23b7cfec'/%3E%3Cstop offset='1' stop-color='%23dfeaf8'/%3E%3C/linearGradient%3E%3C/defs%3E %3Cpath fill='url(%23default-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%23f7f4ee' stroke-width='14' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='url(%23default-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%231c5f9e' stroke-width='3.0' d='M23 7H89v66l-16 16H7V23z'/%3E %3C/svg%3E") /* frames/paper/button.svg */;
		--frame-src-button-action: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- Generated from ../button-action.svg for the light theme: see the frames/paper table in apps/web/DESIGN.md. Keep the geometry identical to the dark frame; only the colours differ. --%3E %3C!-- One diagonal cut, a lifted face and a hairline rim, after the mood boards. A vertical gradient survives the 9-slice, because every cell in a row stretches the same way. --%3E %3Cdefs%3E%3ClinearGradient id='action-face' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23f8e7c8'/%3E%3Cstop offset='.45' stop-color='%23e3c288'/%3E%3Cstop offset='.62' stop-color='%23e3c288'/%3E%3Cstop offset='1' stop-color='%23f8e7c8'/%3E%3C/linearGradient%3E%3C/defs%3E %3Cpath fill='url(%23action-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%23f7f4ee' stroke-width='14' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='url(%23action-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%238a5a12' stroke-width='3.0' d='M23 7H89v66l-16 16H7V23z'/%3E %3C/svg%3E") /* frames/paper/button-action.svg */;
		--frame-src-button-success: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- Generated from ../button-success.svg for the light theme: see the frames/paper table in apps/web/DESIGN.md. Keep the geometry identical to the dark frame; only the colours differ. --%3E %3C!-- One diagonal cut, a lifted face and a hairline rim, after the mood boards. A vertical gradient survives the 9-slice, because every cell in a row stretches the same way. --%3E %3Cdefs%3E%3ClinearGradient id='success-face' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23daf1e4'/%3E%3Cstop offset='.45' stop-color='%23a6dcc1'/%3E%3Cstop offset='.62' stop-color='%23a6dcc1'/%3E%3Cstop offset='1' stop-color='%23daf1e4'/%3E%3C/linearGradient%3E%3C/defs%3E %3Cpath fill='url(%23success-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%23f7f4ee' stroke-width='14' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='url(%23success-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%230c7245' stroke-width='3.0' d='M23 7H89v66l-16 16H7V23z'/%3E %3C/svg%3E") /* frames/paper/button-success.svg */;
		--frame-src-button-danger: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- Generated from ../button-danger.svg for the light theme: see the frames/paper table in apps/web/DESIGN.md. Keep the geometry identical to the dark frame; only the colours differ. --%3E %3C!-- One diagonal cut, a lifted face and a hairline rim, after the mood boards. A vertical gradient survives the 9-slice, because every cell in a row stretches the same way. --%3E %3Cdefs%3E%3ClinearGradient id='danger-face' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23f9dcda'/%3E%3Cstop offset='.45' stop-color='%23eeb3ae'/%3E%3Cstop offset='.62' stop-color='%23eeb3ae'/%3E%3Cstop offset='1' stop-color='%23f9dcda'/%3E%3C/linearGradient%3E%3C/defs%3E %3Cpath fill='url(%23danger-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%23f7f4ee' stroke-width='14' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='url(%23danger-face)' d='M23 7H89v66l-16 16H7V23z'/%3E %3Cpath fill='none' stroke='%23a3221a' stroke-width='3.0' d='M23 7H89v66l-16 16H7V23z'/%3E %3C/svg%3E") /* frames/paper/button-danger.svg */;
		--frame-src-tabs: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E %3C!-- Generated from ../tabs.svg for the light theme: see the frames/paper table in apps/web/DESIGN.md. Keep the geometry identical to the dark frame; only the colours differ. --%3E %3C!-- Tab strip: one frame around connected tabs, chamfered top-left and bottom-right like the LotV commander tabs. --%3E %3Cpath fill='%23fffefb' fill-opacity='.92' d='M16 3h77v77L80 93H3V16z'/%3E %3Cpath fill='none' stroke='%238a8377' stroke-width='2' d='M16 4h76v76L80 92H4V16z'/%3E %3Cpath fill='none' stroke='%232f6aa8' stroke-opacity='.55' stroke-width='2' d='M4 22v-6L16 4h6M92 74v6L80 92h-6'/%3E %3C/svg%3E") /* frames/paper/tabs.svg */;

	}
}

@layer base {
	*,
	*::before,
	*::after {
		box-sizing: border-box;
		margin: 0;
		padding: 0;
	}

	html {
		background-color: var(--color-void);
		color: var(--color-text);
		font-family: var(--font-text);
		font-size: 17px;
		line-height: 1.55;
		scrollbar-color: var(--color-line) var(--color-void);
		/* The page keeps the same width with or without a scrollbar, so moving
		between a short and a long page, or a page growing past the window,
		does not shift the layout sideways (and break a view transition). */
		scrollbar-gutter: stable;
		/* An anchor jumps straight to its target, below the sticky top bar: a
		section link in the rail, a heading link, history and a link from another
		page alike. Scrolling is never animated. */
		scroll-padding-block-start: calc(var(--topbar-height) + var(--space-4));
	}

	body {
		min-block-size: 100dvh;
		background: var(--color-void);
	}

	:is(a, button, input, select, summary, [tabindex]):focus-visible {
		outline: 2px solid var(--color-glow);
		outline-offset: 3px;
	}

	a {
		color: var(--color-info);
		text-decoration-color: rgb(var(--ch-info) / 40%);
		text-underline-offset: 0.2em;

		&:hover {
			color: var(--color-text-strong);
		}
	}

	/* Display type: the HUD face, uppercase, tracked by each rule's own --track.
	A selector list, so each selector keeps its own specificity and a rule that
	takes the treatment off still wins. */
	h1,
	h2,
	h3,
	h4,
	blockquote > p:last-child:has(> strong:only-child) strong,
	.kicker,
	.label,
	.topbar-brand,
	.topbar-nav a,
	.menu-summary,
	.button,
	.tab,
	.copy-popover,
	.briefing li,
	.tree-badge,
	.app-node,
	.site-footer a,
	.article-nav a,
	.article-nav span,
	.article-nav-category,
	.article-pagination a,
	.article-pagination span[aria-disabled],
	.toggle-label,
	th {
		font-family: var(--font-display);
		letter-spacing: var(--track);
		text-transform: uppercase;
	}

	h1,
	h2,
	h3,
	h4 {
		--track: 0.04em;
		color: var(--color-sand);
		font-weight: 400;
		line-height: 1.2;
		overflow-wrap: anywhere;
	}

	h1 {
		font-size: clamp(1.8rem, 1.2rem + 2.4vw, 3rem);
		text-shadow: 0 0 18px rgb(var(--ch-glow) / 35%);
	}

	/* On paper a heading needs no glow, and the blue one reads as a smear. */
	:root[data-theme="light"] h1 {
		text-shadow: none;
	}

	h2 {
		font-size: 1.2rem;
	}

	h3 {
		font-size: 0.9rem;
	}

	code,
	pre {
		font-family: var(--font-data);
		font-size: 0.9em;
	}

	ul {
		padding-inline-start: 1.1rem;
	}

	li::marker {
		color: var(--color-info);
	}

	/* A last paragraph that is only bold text is the quote's credit. */
	blockquote {
		position: relative;
		display: grid;
		gap: var(--space-3);
		border-inline-start: 3px solid var(--color-info);
		padding: var(--space-4) var(--space-5) var(--space-4) var(--space-7);
		background: linear-gradient(90deg, rgb(var(--ch-info) / 8%), transparent);
		color: var(--color-text-strong);
		font-size: 1.1rem;
		font-style: italic;

		&::before {
			content: "\201C";
			position: absolute;
			inset-block-start: var(--space-2);
			inset-inline-start: var(--space-3);
			color: var(--color-info);
			font-size: 3rem;
			font-style: normal;
			line-height: 1;
			opacity: 0.6;
		}

		> p:last-child:has(> strong:only-child) {
			text-align: end;

			strong {
				--track: 0.1em;
				color: var(--color-info);
				font-size: 0.7rem;
				font-style: normal;
				font-weight: 400;

				&::before {
					content: "\2014\00A0";
				}
			}
		}
	}

	/* Type roles */

	.kicker,
	.label {
		--track: 0.12em;
		font-size: 0.7rem;
		color: var(--color-info);
	}

	.label {
		color: var(--color-text-dim);
	}

	.dim {
		color: var(--color-text-dim);
	}

	.prompt {
		color: var(--color-prompt);
		font-family: var(--font-data);
		text-shadow: 0 0 6px rgb(var(--ch-prompt) / 45%);

	}

	/* The block cursor that blinks after a command, like a waiting terminal. Two
	places wear it: a .prompt line, and the last line of a live terminal. */
	.prompt::after,
	.terminal.live .terminal-line:last-child::after {
		content: "";
		display: inline-block;
		inline-size: 0.6em;
		block-size: 1.1em;
		margin-inline-start: 0.2em;
		vertical-align: text-bottom;
		background: var(--color-prompt);
		box-shadow: 0 0 6px rgb(var(--ch-prompt) / 45%);
		animation: prompt-cursor 1.1s steps(1) infinite;

		/* The global reduced-motion rule only shortens animations, which would
		make an infinite blink flicker; the cursor stays solid instead. */
		@media (prefers-reduced-motion: reduce) {
			animation: none;
		}
	}

	@keyframes prompt-cursor {
		50% {
			opacity: 0;
		}
	}

	.stat {
		color: var(--color-text-strong);
		font-family: var(--font-display);
		font-size: 1.8rem;
		line-height: 1;
		text-align: center;
	}

	@media (prefers-reduced-motion: reduce) {
		*,
		*::before,
		*::after {
			transition-duration: 0.01ms !important;
			animation-duration: 0.01ms !important;
		}

	}
}

@layer layout {
	/* Layout */

	/* A page sits on its art (--page-art, from the backgrounds package's
	page-art.css) under --page-shade. Without
	that sheet the page is a plain void. */
	.page {
		--page-shade: rgb(var(--ch-void) / 88%);

		display: flex;
		flex-direction: column;
		min-block-size: 100dvh;
		background-color: var(--color-void);
		background-image:
			linear-gradient(var(--page-shade), var(--page-shade)),
			var(--page-art, none);
		background-position: center;
		background-size: cover;
		background-attachment: fixed;

		&.art-desert,
		&.art-enterprise {
			--page-shade: rgb(var(--ch-void) / 64%);
		}
	}

	/* The light theme keeps the art under a paper shade, with more of it than the
	dark theme needs: the art is dark and the text on it is not. */
	:root[data-theme="light"] .page {
		--page-shade: rgb(var(--ch-void) / 93%);

		&.art-desert,
		&.art-enterprise {
			--page-shade: rgb(var(--ch-void) / 86%);
		}
	}

	.content {
		display: flex;
		flex-direction: column;
		gap: var(--space-7);
		inline-size: min(100% - 2rem, var(--content-wide));
		margin-inline: auto;
		padding-block: var(--space-7) var(--space-8);
	}

	/* The last control of a row, held against its far end: a row reads left to
	right until this, which belongs to the row but not to the group before it. */
	.push-end {
		margin-inline-start: auto;
	}

	.stack {
		display: flex;
		flex-direction: column;
		gap: var(--space-4);
	}

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

	.grid {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
		gap: var(--space-5);
	}

	.section-title {
		display: flex;
		align-items: center;
		gap: var(--space-3);

		&::after {
			flex: 1;
			border-block-start: 1px solid var(--color-line-low);
			content: "";
		}
	}
}

@layer components {
	/* Top bar */

	/* The top bar stays in view; --topbar-height keeps sticky and anchored content below it. */
	.topbar {
		position: sticky;
		inset-block-start: 0;
		z-index: 10;
		/* A narrow bar has two rows: the brand and the tools, then the links
		across the full width. The four-format bar needs 85rem for one row. */
		display: grid;
		/* The brand takes the width it needs and the tools take the rest. An auto
		tools column sizes to the row unwrapped, which leaves nothing for the brand
		and spills the controls over the wordmark instead of wrapping them. */
		grid-template-columns: auto minmax(0, 1fr);
		/* The first row grows past 3.5rem when it holds the format tabs, whose
		touch targets are taller on a narrow bar; --topbar-height covers both. */
		grid-template-rows: minmax(3.5rem, auto) 2.75rem;
		grid-template-areas:
			"brand tools"
			"nav nav";
		min-block-size: var(--topbar-height);
		border-block-end: 1px solid var(--color-line-low);
		/* Translucent glass: content scrolling under the bar is blurred, not hidden. */
		background: linear-gradient(rgb(var(--ch-deep) / 72%), rgb(var(--ch-void) / 58%));
		backdrop-filter: blur(8px) saturate(1.2);

		/* Two toggles need 112rem for one row; below that the bar stays a grid,
		so the toggles keep the brand's row. */
		&.topbar-two-toggles {
			@media (min-width: 85rem) and (max-width: 111.99rem) {
				display: grid;
			}
		}

		@media (min-width: 85rem) {
			display: flex;
			align-items: stretch;
			/* Cells never shrink; a bar with every part at once (/theme) wraps to
			a second row instead. */
			flex-wrap: wrap;
		}
	}

	.topbar-brand {
		grid-area: brand;
		display: flex;
		align-items: center;
		gap: var(--space-2);
		padding-inline: var(--space-3);
		color: var(--color-text-strong);
		--track: 0.1em;
		text-decoration: none;

		@media (min-width: 75rem) {
			padding-inline: var(--space-5);
		}
	}

	/* On the narrowest screens the brand shows only the logo; the name stays for
	assistive technology. */
	.topbar-name {
		@media (max-width: 30rem) {
			position: absolute;
			inline-size: 1px;
			block-size: 1px;
			overflow: hidden;
			clip-path: inset(50%);
			white-space: nowrap;
		}
	}

	/* The Stardust logo (assets/stardust-logo.svg, from the prototype tutorials)
	drawn as a mask in the action gold, before the brand name. */
	.topbar-logo {
		flex: none;
		inline-size: 1.75rem;
		block-size: 1.75rem;
		background-color: var(--color-action);
		mask: url("/assets/stardust-logo-26d20ffecf9b4974380794fb1bf9ccc63b27030b23d1fd26479991681d5ce89a.svg") center / contain no-repeat;
	}

	/* Page-wide controls at the end of the top bar: the theme switch, the format
	selector, and the page's one action. */
	.topbar-tools {
		grid-area: tools;
		display: flex;
		align-items: center;
		gap: var(--space-3);
		margin-inline-start: auto;
		padding-inline: var(--space-3);

		/* Both are hosts around a control, so they lay it out in the bar rather
		than boxing it. */
		theme-switch,
		.topbar-languages,
		.topbar-formats {
			display: flex;
			align-items: center;
			/* A toggle wraps whole. Squeezed instead, its strip would scroll its
			own options out of reach. */
			flex: none;
		}

		/* An empty host has no width: the island renders the button on upgrade,
		and the bar must not jump when it does. */
		theme-switch:not(:has(button)) {
			inline-size: 2.75rem;
		}

		/* On a narrow bar the action shrinks and wraps its label first, then the
		tools wrap onto a second line inside the bar's first row. */
		@media (max-width: 74.99rem) {
			flex-wrap: wrap;
			justify-content: flex-end;
			row-gap: var(--space-2);

			/* On the narrowest bar the icon-only toggles, the logo and the theme
			switch are within a few pixels of one line. The gap closes rather than
			let them wrap, which would make the bar taller the narrower it gets. */
			@media (max-width: 30rem) {
				column-gap: var(--space-2);
				padding-inline: var(--space-2);
			}

			.button {
				flex-shrink: 1;
			}
		}

		@media (min-width: 75rem) {
			padding-inline: var(--space-5);
		}

		/* A narrow bar gives the action button only the room its label needs, so
		the brand and the tools share the row instead of the button filling it. */
		.button {
			min-inline-size: 0;
			font-size: 0.7rem;

			@media (min-width: 75rem) {
				min-inline-size: 9rem;
				font-size: 0.78rem;
			}
		}

		> .tabs {
			align-self: center;
		}
	}

	/* Cross-document view transitions animate ordinary page loads. The bar and
	the article rail are named, so they hold still while the article fades. */
	@view-transition {
		navigation: auto;
	}

	.topbar {
		view-transition-name: site-bar;
	}


	/* Every group, named or not, moves on the same short clock. */
	::view-transition-group(*),
	::view-transition-old(*),
	::view-transition-new(*) {
		animation-duration: 180ms;
	}

	/* A reader who asked for less motion gets the cut they had before. */
	@media (prefers-reduced-motion: reduce) {
		@view-transition {
			navigation: none;
		}
	}

	/* No scroll container here: it would clip the menu panel that drops out of the
	bar. A narrow bar wraps instead. */
	.topbar-nav {
		grid-area: nav;
		display: flex;
		min-inline-size: 0;
		border-block-start: 1px solid var(--color-line-low);

		@media (min-width: 75rem) {
			border-block-start: 0;
		}

		a {
			display: flex;
			align-items: center;
			/* Four cells share a phone's bar: a tight gutter keeps every label,
			and the More caret, inside it. */
			padding-inline: var(--space-1);
			border-inline-start: 1px solid var(--color-line-low);
			color: var(--color-text);
			/* No tracking and the smallest label on a phone, so the four
			documentation labels fit a 320px bar unclipped. */
			--track: 0;
			font-size: 0.62rem;
			text-decoration: none;
			white-space: nowrap;

			/* A 320px bar is the narrowest phone still sold, and the
			documentation's four labels do not fit it even without tracking. */
			@media (max-width: 21.99rem) {
				font-size: 0.56rem;
			}

			/* Past the widest failing bar, the label gets its air back. */
			@media (min-width: 34rem) {
				--track: 0.06em;
				font-size: 0.7rem;
			}

			@media (min-width: 75rem) {
				--track: 0.08em;
				padding-inline: var(--space-5);
				font-size: 0.8rem;
			}

			&:hover {
				background: rgb(var(--ch-edge) / 8%);
				color: var(--color-text-strong);
			}

			&[aria-current="page"] {
				background: linear-gradient(transparent 50%, rgb(var(--ch-edge) / 24%));
				box-shadow: inset 0 -2px 0 var(--color-glow);
				color: var(--color-text-strong);
			}
		}

		/* On a narrow bar the top-level links share the row. */
		& > a,
		& > .menu {
			flex: 1;
			min-inline-size: 0;

			@media (min-width: 75rem) {
				flex: none;
			}
		}

		& > a {
			justify-content: center;
		}

		& > :first-child,
		& > .menu:first-child > .menu-summary {
			border-inline-start: 0;

			@media (min-width: 75rem) {
				border-inline-start: 1px solid var(--color-line-low);
			}
		}

		& > .menu > .menu-summary {
			flex: 1;
			justify-content: center;
			gap: var(--space-1);
			padding-inline: var(--space-1);
			font-size: 0.66rem;
			letter-spacing: 0.04em;

			@media (min-width: 22rem) {
				font-size: 0.7rem;
				letter-spacing: 0.06em;
			}

			@media (min-width: 75rem) {
				flex: none;
				gap: var(--space-2);
				padding-inline: var(--space-5);
				font-size: 0.8rem;
				letter-spacing: 0.08em;
			}
		}
	}

	/* 9-slice frames */

	.modal,
	.panel,
	.row,
	.field,
	.button {
		border-style: solid;
		border-color: transparent;
		background: none;
	}

	.modal {
		border-width: var(--frame-modal);
		border-image: var(--frame-src-modal) 24 fill / var(--frame-modal) stretch;
		padding: var(--space-3) var(--space-4);
		filter: drop-shadow(0 0 24px rgb(var(--ch-edge) / 22%));
	}

	.panel {
		border-width: var(--frame-panel);
		border-image: var(--frame-src-panel) 24 fill / var(--frame-panel) stretch;
		padding: var(--space-2) var(--space-3);
	}

	/* A panel holding a sentence rather than code wraps instead of scrolling. */
	pre.panel.panel-note {
		white-space: pre-wrap;
	}

	pre.panel {
		font-variant-ligatures: common-ligatures contextual;
		overflow-x: auto;
		border-image: var(--frame-src-code-brackets) 24 / var(--frame-panel) stretch;
		/* The bracket border already insets the code; keep only a little more. */
		padding: var(--space-1) var(--space-2);
		background:
			url("/assets/patterns/hex-c2c83d179d58aa11ca11bda559153a3287e40b548ec4d5ad47eba64de9bdb381.svg") 0 0 / 24px auto,
			rgb(var(--ch-ink) / 82%);
		color: var(--color-text-strong);
		font-size: 0.85rem;
		line-height: 1.6;
	}

	.row {
		display: grid;
		grid-template-columns: minmax(0, 1fr) auto;
		align-items: center;
		gap: var(--space-4);
		border-width: var(--frame-row);
		border-image: var(--frame-src-row) 24 fill / var(--frame-row) stretch;
		padding: var(--space-2) var(--space-3);
	}

	/*
	The theme's one two-column layout: one column below 60rem, then
	--split-main beside --split-side (each a cap or a share; the first defaults
	to minmax(0, 1fr) so long code cannot widen the page). It follows .row, so
	an element that is both takes its columns from here.
	*/
	.split {
		display: grid;
		grid-template-columns: minmax(0, 1fr);
		gap: var(--space-6);

		@media (min-width: 60rem) {
			grid-template-columns: var(--split-main, minmax(0, 1fr)) var(--split-side, minmax(0, 1fr));
		}
	}

	.field {
		min-block-size: 2.6rem;
		border-width: var(--frame-control);
		border-image: var(--frame-src-field) 24 fill / var(--frame-control) stretch;
		padding: 0 var(--space-2);
		color: var(--color-text-strong);
		font: inherit;

		&::placeholder {
			color: var(--color-text-dim);
		}
	}

	/* A document the visitor writes: a field that holds code, so it takes the
	data font and grows with what is pasted into it, between a few lines and a
	screenful. */
	textarea.field {
		display: block;
		inline-size: 100%;
		min-block-size: 8lh;
		max-block-size: 60dvh;
		padding: var(--space-2);
		font-family: var(--font-data);
		font-size: 0.9rem;
		line-height: 1.5;
		tab-size: 2;
		resize: vertical;
	}

	select.field {
		appearance: none;
		padding-inline-end: 2rem;
		background-image:
			linear-gradient(45deg, transparent 50%, var(--color-glow) 50%),
			linear-gradient(135deg, var(--color-glow) 50%, transparent 50%);
		background-position:
			calc(100% - 0.95rem) 50%,
			calc(100% - 0.55rem) 50%;
		background-size: 0.4rem 0.4rem;
		background-repeat: no-repeat;

		/* The option list is the browser's own popup: without these it keeps a
		light background under the field's light text. */
		option {
			background-color: var(--color-deep);
			color: var(--color-text);
		}

		option:checked {
			background-color: var(--color-line);
			color: var(--color-text-strong);
		}
	}

	/* Each button type has its own frame. A button brightens and glows on hover
	and more while pressed, in its type's channel (--button-glow). */
	.button {
		--track: 0.08em;
		/* A variant sets the hover glow's channel, reach and strength; a press
		glows the same on every variant, in its channel. */
		--button-glow: var(--ch-glow);
		--button-glow-size: 8px;
		--button-glow-alpha: 45%;
		display: inline-flex;
		flex-shrink: 0;
		align-items: center;
		justify-content: center;
		gap: var(--space-2);
		min-block-size: 2.75rem;
		/* A long label wraps inside the frame on a phone instead of pushing the
		button past its column; a button with room to spare is unchanged. */
		min-inline-size: min(9rem, 100%);
		max-inline-size: 100%;
		border-width: var(--frame-button);
		border-image: var(--frame-src-button) 24 fill / var(--frame-button) stretch;
		padding: 0 var(--space-3);
		color: var(--color-control-text);
		font-size: 0.78rem;
		text-align: center;
		text-decoration: none;
		text-wrap: balance;
		/* A label on a control is not text to select, so a drag or a double click
		presses the button instead of highlighting its word. */
		user-select: none;
		cursor: pointer;
		transition:
			filter var(--motion-fast),
			transform var(--motion-fast);

		&:hover:not(:disabled) {
			filter: var(--control-hover)
				drop-shadow(0 0 var(--button-glow-size) rgb(var(--button-glow) / var(--button-glow-alpha)));
		}

		&:active:not(:disabled) {
			filter: var(--control-press) drop-shadow(0 0 14px rgb(var(--button-glow) / 65%));
			transform: translateY(1px);
		}

		&:disabled {
			cursor: not-allowed;
			filter: grayscale(0.85) brightness(0.65);
		}

		/* The theme button carries an icon and no words, so it is square: the
		frame's chamfers need the same room on both sides. */
		&.theme-button {
			min-inline-size: 2.75rem;
			padding-inline: 0;

			.icon {
				inline-size: 1.35rem;
				block-size: 1.35rem;
			}
		}

		&.action {
			--button-glow: var(--ch-action-high);
			--button-glow-size: 10px;
			--button-glow-alpha: 50%;
			border-image-source: var(--frame-src-button-action);
			color: var(--color-action-label);
		}

		/* A toned button glows in its tone; its frame art carries the colour. */
		&:is(.success, .danger) {
			--button-glow: var(--ch-tone);
			--button-glow-size: 10px;
			--button-glow-alpha: 50%;
		}

		&.success {
			border-image-source: var(--frame-src-button-success);
		}

		&.danger {
			border-image-source: var(--frame-src-button-danger);
		}

		/* A button that started a request keeps .loading until the next frame from
		the server draws the result: a dim label behind a sweeping scan line, and no
		second press. */
		&.loading {
			position: relative;
			overflow: hidden;
			cursor: progress;
			filter: grayscale(0.4) brightness(0.8);
			pointer-events: none;

			&::after {
				position: absolute;
				inset-block: 0;
				inset-inline-start: 0;
				animation: button-loading 900ms linear infinite;
				background: linear-gradient(90deg, transparent, var(--color-info), transparent);
				content: "";
				inline-size: 40%;
				opacity: 0.55;
			}
		}
	}

	@keyframes button-loading {
		from {
			transform: translateX(-100%);
		}

		to {
			transform: translateX(350%);
		}
	}

	/* Connected tabs: one 9-slice strip holds every tab, a slanted divider
	separates neighbours, and the selected tab lights a slanted segment. */
	.tabs {
		display: inline-flex;
		align-self: flex-start;
		/* The lit segment of a tab is wider than the tab, because a skewed
		rectangle needs the room. The gap holds that width, so a selected tab
		stops short of the tab beside it. */
		gap: 0.5rem;
		max-inline-size: 100%;
		border: var(--frame-control) solid transparent;
		border-image: var(--frame-src-tabs) 24 fill / var(--frame-control) stretch;
		/* More tabs than the strip can show scroll inside it: clipped tabs would
		be unreachable on a phone. The bar has no room for a scrollbar, and the
		strip still clips the lit segment at its ends. */
		overflow-x: auto;
		overflow-y: hidden;
		scrollbar-width: none;

		@media (min-width: 60rem) {
			overflow: hidden;
		}
	}

	/* A toggle is an icon strip with its label inside the frame, after the options.
	The options carry no text, so the label is what names the choice: it reads the
	chosen one rather than the control. */
	.toggle {
		display: inline-flex;
		align-items: center;

		> .tabs {
			align-self: center;
			margin-block-end: 0;
		}

		/* An icon-only tab is square, and keeps one height at every width. */
		.tab {
			justify-content: center;
			min-inline-size: 2.5rem;
			min-block-size: 2rem;
			padding-inline: var(--space-2);
		}
	}

	/* The label rides inside the strip's frame, after the last option. It is not a
	tab, so it takes the strip's padding but none of a tab's segment or divider. */
	.toggle-label {
		--track: 0.12em;
		display: grid;
		align-self: center;
		padding-inline: var(--space-2);
		font-size: 0.7rem;
		color: var(--color-text-dim);
		/* Centred in the width the widest option reserves, so a short name sits
		under the middle of the label rather than adrift at its start. */
		text-align: center;
		white-space: nowrap;

		/* Below 53rem the label goes, so the toggles stay on one line; the
		role="status" beside a toggle still announces the choice. */
		@media (max-width: 52.99rem) {
			display: none;
		}

		/* The value and a hidden copy of the longest option share one cell, so
		switching cannot change the label's width. */
		> * {
			grid-area: 1 / 1;
		}

		> .toggle-sizer {
			visibility: hidden;
		}
	}


	.tab {
		position: relative;

		/* A tab is a <button> in the SDK language group and an <a> in the format
		selector, which is a link because the format is part of the URL. */
		text-decoration: none;
		isolation: isolate;
		display: inline-flex;
		align-items: center;
		/* A tab is a touch target on a phone and a compact HUD control on a
		pointer screen. */
		min-block-size: 2.75rem;
		border: 0;
		padding: 0 var(--space-4);
		background: none;
		color: var(--color-text);
		--track: 0.1em;
		font-size: 0.7rem;
		white-space: nowrap;
		user-select: none;
		cursor: pointer;

		@media (min-width: 60rem) {
			min-block-size: 2rem;
		}

		/* The slanted segment behind the label. */
		&::before {
			position: absolute;
			z-index: -1;
			inset: -2px -0.4rem;
			border: 1px solid transparent;
			transform: skewX(-24deg);
			transition:
				background var(--motion-fast),
				border-color var(--motion-fast);
			content: "";
		}

		/* The slanted divider from the previous tab, in the middle of the gap. */
		& + .tab::after {
			position: absolute;
			inset-block: 0.35rem;
			inset-inline-start: -0.25rem;
			border-inline-start: 1px solid var(--color-line);
			transform: skewX(-24deg);
			content: "";
		}

		&:hover::before {
			background: rgb(var(--ch-edge) / 14%);
		}

		/* A toggle strip (role="group", aria-pressed buttons) lights the same way,
		and so does a strip of links that choose a document format, whose own page
		is marked aria-current. */
		&:is([aria-selected="true"], [aria-pressed="true"], [aria-current="true"]) {
			color: var(--color-control-text);

			&::before {
				border-color: var(--color-tab-rim);
				background: var(--color-tab-face);
				box-shadow: 0 0 10px rgb(var(--ch-glow) / 45%);
			}

			/* The selected segment covers its own divider and the next one. */
			&::after,
			& + .tab::after {
				border-color: transparent;
			}
		}
	}

	/*
	Icons are SVGs in icons/, drawn as masks so they take the text colour:
	<span class="icon icon-rocket" aria-hidden="true"></span>. icons.css, which
	web:generate writes from that folder, gives each one its class; a colour set
	(Devicon and Material Icon Theme marks) keeps its own colours instead of the
	mask. The licences are icons/*-LICENSE.txt.
	*/
	.icon {
		display: inline-block;
		flex: none;
		inline-size: 1.25em;
		block-size: 1.25em;
		background-color: currentcolor;
		mask: var(--icon) center / contain no-repeat;
	}

	:is([class*="icon-devicon-"], [class*="icon-material-icon-theme-"]) {
		background-color: transparent;
		background-position: center;
		background-repeat: no-repeat;
		background-size: contain;
		mask: none;
	}

	/* A code panel: its label sits on the top edge and its copy control in the
	middle of the right edge. */
	.code-block {
		position: relative;
		display: flex;
		flex-direction: column;
		min-inline-size: 0;

		> figcaption > .panel-caption-heading {
			inset-block-start: 0;
			inset-inline-end: var(--space-4);
			transform: translateY(-50%);
			padding: 2px 2px 2px var(--space-2);
		}
	}

	/* The caption chip a code panel hangs on its top edge and a result on its
	bottom edge: what both share. */
	:is(.code-block, .result) > figcaption {
		position: static;

		> .panel-caption-heading {
			position: absolute;
			z-index: 1;
			display: inline-flex;
			align-items: center;
			gap: var(--space-1);
			border: 1px solid var(--color-line-low);
			background: rgb(var(--ch-ink) / 92%);
			backdrop-filter: blur(4px);
		}
	}

	.code-block > figcaption > .panel-size {
		position: absolute;
		z-index: 1;
		inset-block-end: 0;
		inset-inline-end: var(--space-4);
		transform: translateY(50%);
		padding: 2px var(--space-2);
		line-height: 1;
		background: rgb(var(--ch-ink) / 92%);
		font-family: var(--font-data);
		font-size: 0.75rem;
		color: var(--color-text-dim);
		pointer-events: none;
	}

	:is(.code-block, .result) > figcaption > copy-button {
		position: absolute;
		inset-block-start: 50%;
		inset-inline-end: 0;
		z-index: 1;
		transform: translate(50%, -50%);

		.copy-popover {
			inset-block-start: auto;
			inset-block-end: calc(100% + var(--space-1));
		}
	}

	.copy-button.success,
	.result > figcaption > copy-button .copy-button {
		color: var(--color-success);

		&:hover {
			filter: var(--control-hover) drop-shadow(0 0 6px rgb(var(--ch-success) / 50%));
		}

		&:active {
			filter: var(--control-press) drop-shadow(0 0 10px rgb(var(--ch-success) / 65%));
		}
	}

	.panel-caption-heading {
		display: contents;

		/* The icon is an inline-block, so on its own it sits on the text baseline
		and hangs below the cap height beside a short uppercase label like JSON.
		The label centres it instead. */
		> .label {
			display: inline-flex;
			align-items: center;
			gap: var(--space-1);
		}
	}

	/* <copy-button code="..."> (assets/rocket/copy-button.js) in a panel caption. */
	copy-button {
		display: inline-flex;
	}

	.copy-button-wrapper {
		position: relative;
		display: inline-flex;
	}

	.copy-button {
		display: inline-grid;
		place-items: center;
		border: 1px solid var(--color-line-low);
		padding: var(--space-1);
		background: rgb(var(--ch-deep) / 80%);
		color: var(--color-info);
		user-select: none;
		cursor: pointer;
		transition: filter var(--motion-fast);

		&:hover {
			filter: var(--control-hover) drop-shadow(0 0 6px rgb(var(--ch-glow) / 50%));
		}

		&:active {
			filter: var(--control-press) drop-shadow(0 0 10px rgb(var(--ch-glow) / 65%));
		}
	}

	.copy-popover {
		position: absolute;
		inset-block-start: calc(100% + var(--space-1));
		inset-inline-end: 0;
		border: 1px solid var(--color-line);
		padding: 0.1em var(--space-2);
		background: var(--color-deep);
		color: var(--color-success);
		--track: 0.12em;
		font-size: 0.6rem;
		white-space: nowrap;
		pointer-events: none;
	}

	.highlight {
		font: inherit;
		tab-size: 2;
	}

	.hl-comment {
		color: var(--color-text-dim);
		font-style: italic;
	}

	.hl-keyword,
	.hl-operator {
		color: var(--color-action-high);
	}

	.hl-string {
		color: var(--color-code-string);
	}

	.hl-string-special-key,
	.hl-property,
	.hl-attribute {
		color: var(--color-info);
	}

	/* A Go type names what the code defines, so it reads like a head without the
	weight of a call. */
	.hl-type {
		color: var(--color-glow);
	}

	.hl-number,
	.hl-constant {
		color: var(--color-warning);
	}

	.hl-constant-builtin {
		color: var(--color-code-constant);
	}

	/* The head of a compact list (`[format ...]`) is the call, so it reads ahead of
	its arguments. A known head (an operator, an aggregate, a window function) is
	brighter again, because Stardust defines its meaning. */
	.hl-function {
		color: var(--color-glow);
		font-weight: 700;
	}

	.hl-function-builtin {
		color: var(--color-action-high);
		font-weight: 700;
	}

	.hl-escape,
	.hl-embedded {
		color: var(--color-code-escape);
	}

	/* Query variables (?e) in orange, far from the green of plain strings; then
	DUST entity tags (#link) and brackets. */
	.hl-variable {
		color: var(--color-code-variable);
	}

	.hl-tag {
		color: var(--color-code-tag);
	}

	.hl-punctuation {
		color: var(--color-text-dim);
	}

	/* Rainbow brackets (DUST): each depth its own color, an opener and its closer
	alike, so a bracket is matched by eye. Three hues, VS Code's own, repeated over the six
	depth classes the highlighter cycles through. */
	.rainbow-bracket.depth-0,
	.rainbow-bracket.depth-3,
	.hl-rainbow-1,
	.hl-rainbow-4 {
		color: var(--color-code-rainbow-1);
	}

	.rainbow-bracket.depth-1,
	.rainbow-bracket.depth-4,
	.hl-rainbow-2,
	.hl-rainbow-5 {
		color: var(--color-code-rainbow-2);
	}

	.rainbow-bracket.depth-2,
	.rainbow-bracket.depth-5,
	.hl-rainbow-3,
	.hl-rainbow-6 {
		color: var(--color-code-rainbow-3);
	}

	/* A run of closers (the ]]} that ends a nested query) reads as one stop, not
	three marks: set tight and a step back from the words, the colors kept so
	each one still finds its opener. */
	.hl-closers {
		letter-spacing: -0.08em;
		opacity: 0.75;
	}

	.lede {
		max-inline-size: 48ch;
		color: var(--color-text);
		font-size: 1.2rem;
		line-height: 1.6;
	}

	/* A result readout below an example: the label, the parameter chips and the
	value, read left to right. */
	.result {
		position: relative;
		display: flex;
		flex-direction: column;
		gap: var(--space-2);
		min-inline-size: 0;
		margin: 0;
		border-inline-start: 2px solid var(--color-success);
		padding: var(--space-3) var(--space-4);
		background: linear-gradient(90deg, rgb(var(--ch-success) / 10%), transparent);

		> figcaption > .panel-caption-heading {
			inset-block-end: 0;
			inset-inline-end: 0;
			transform: translateY(50%);
			padding: 2px 0;
			color: var(--color-success);
			line-height: 1;
		}
	}

	.result-params {
		display: flex;
		flex-wrap: wrap;
		gap: var(--space-2);
		margin: 0;

		> div {
			display: flex;
			align-items: flex-start;
			gap: var(--space-1);
			max-inline-size: 100%;
			min-block-size: 1.6rem;
			border: 1px solid var(--color-line-low);
			padding: var(--space-1) var(--space-2);
			line-height: 1.3;
			background: rgb(var(--ch-ink) / 60%);
			font-family: var(--font-data);
			font-size: 0.8rem;
		}

		dt {
			color: var(--color-info);

			&::after {
				content: ":";
			}
		}

		dd {
			min-inline-size: 0;
			margin: 0;
			color: var(--color-text-strong);
		}
	}

	.protocol-contract .result-params code.highlight {
		border: 0;
		padding: 0;
		background: transparent;
		white-space: pre-wrap;
		overflow-wrap: anywhere;
	}

	/* The value follows the parameters, after an equals sign, so the line reads
	from left to right. */
	.result-value {
		display: block;
		min-inline-size: 0;
		margin: 0;
		overflow-x: auto;
		white-space: pre;
		line-height: 1.5;
		color: var(--color-success);
		font-family: var(--font-data);
		text-shadow: 0 0 8px rgb(var(--ch-success) / 35%);
	}

	/* The home page's More menu: a native disclosure holding the links to the rest
	of the site, so the bar keeps only this page's sections. */
	.menu {
		position: relative;
		display: flex;
	}

	/* The trigger is a bar cell like the links beside it, with a caret. */
	.menu-summary {
		display: flex;
		align-items: center;
		gap: var(--space-2);
		border: 0;
		border-inline-start: 1px solid var(--color-line-low);
		padding-inline: var(--space-5);
		background: none;
		--track: 0.08em;
		color: var(--color-text);
		font-size: 0.8rem;
		white-space: nowrap;
		user-select: none;
		cursor: pointer;

		&::after {
			content: "";
			inline-size: 0.4rem;
			block-size: 0.4rem;
			border-inline-end: 1px solid currentcolor;
			border-block-end: 1px solid currentcolor;
			transform: translateY(-0.1em) rotate(45deg);
		}

		&:hover,
		&[aria-expanded="true"] {
			background: rgb(var(--ch-edge) / 8%);
			color: var(--color-text-strong);
		}
	}

	/* The panel drops from the bar under its trigger, hung from the trigger's end
	edge and capped at the screen's width, so it never runs off the screen. */
	.menu-list {
		position: absolute;
		z-index: 30;
		inset-block-start: 100%;
		inset-inline-start: auto;
		inset-inline-end: 0;
		display: grid;
		min-inline-size: 12rem;
		max-inline-size: calc(100vw - var(--space-4));
		border: 1px solid var(--color-line);
		border-block-start: 0;
		background-color: rgb(var(--ch-ink) / 96%);
		backdrop-filter: blur(8px) saturate(1.2);
		box-shadow: 0 12px 30px -14px rgb(var(--ch-shadow) / 85%);

		a {
			border-inline-start: 0;
			padding: var(--space-3) var(--space-4);
			font-size: 0.72rem;

			& + a {
				border-block-start: 1px solid var(--color-line-low);
			}

			&:hover {
				background: rgb(var(--ch-edge) / 14%);
			}
		}
	}

	/* The reader's account in the bar: .topbar-end keeps the account and
	theme switch together. A label that opens with a provider mark centres the mark on the
	text. */
	.label > .icon {
		vertical-align: -0.3em;
		margin-inline-end: var(--space-2);
	}

	.topbar-end {
		display: flex;
		flex: none;
		align-items: center;
		gap: var(--space-3);

		@media (max-width: 30rem) {
			gap: var(--space-2);
		}
	}

	.topbar-tools .topbar-sign-in {
		min-inline-size: 0;
		white-space: nowrap;

		@media (min-width: 75rem) {
			min-inline-size: 0;
		}
	}

	.topbar-account {
		display: flex;
		align-items: center;
		gap: var(--space-3);
	}

	.topbar-avatar {
		display: block;
		flex: none;
		/* The theme button's box (theme-switch), with its frame's 3px inset, so the
		two read as the same size: the button's border image draws its visible
		frame inside a pale margin, and the avatar has none. */
		box-sizing: border-box;
		inline-size: 2.75rem;
		padding: 3px;
		line-height: 0;

		&:hover .avatar,
		&:focus-visible .avatar {
			filter: brightness(1.3);
		}
	}

	/* An account whose provider gave no picture: the user mark on the avatar's
	frame, in the action gold, over the same scanlines. */
	.avatar-fallback {
		display: grid;
		place-items: center;

		&::before {
			content: "";
			position: absolute;
			inset: 2px;
			clip-path: var(--chamfer);
			background: rgb(var(--ch-ink) / 92%);
		}

		.icon {
			position: relative;
			z-index: 1;
			inline-size: 62%;
			block-size: 62%;
			color: var(--color-action);
		}
	}

	/* Avatars take the theme's chamfer (top left and bottom right), a glowing
	edge, a cool tone and faint scanlines. */
	.avatar {
		--chamfer: polygon(20% 0, 100% 0, 100% 80%, 80% 100%, 0 100%, 0 20%);

		position: relative;
		display: block;
		aspect-ratio: 1;
		padding: 2px;
		background: linear-gradient(135deg, var(--color-glow), var(--color-edge) 45%, var(--color-action));
		clip-path: var(--chamfer);

		img {
			display: block;
			inline-size: 100%;
			block-size: 100%;
			object-fit: cover;
			clip-path: var(--chamfer);
			filter: saturate(0.6) contrast(1.1);
		}

		&::after {
			content: "";
			position: absolute;
			inset: 2px;
			background:
				repeating-linear-gradient(transparent 0 2px, var(--color-scanline) 2px 3px),
				linear-gradient(160deg, transparent 40%, rgb(var(--ch-edge) / 35%));
			clip-path: var(--chamfer);
			pointer-events: none;
		}
	}

	/* Tables are HUD readouts; .num cells align figures. A table in
	.table-scroll (as Markdown writes them) scrolls inside its frame. */
	.table-scroll {
		max-inline-size: 100%;
		overflow-x: auto;
		overscroll-behavior-inline: contain;
		border: 1px solid var(--color-line-low);
		background: rgb(var(--ch-ink) / 70%);
	}

	table {
		inline-size: 100%;
		border-collapse: collapse;
		font-size: 0.9rem;
	}

	.table-scroll > table {
		min-inline-size: 42rem;
	}

	.reference .table-scroll > table {
		min-inline-size: 0;
		table-layout: auto;
	}

	th,
	td {
		padding: var(--space-2) var(--space-3);
		overflow-wrap: normal;
		word-break: normal;
		text-align: start;
		vertical-align: top;
	}

	th {
		border-block-end: 1px solid var(--color-glow);
		background: linear-gradient(rgb(var(--ch-edge) / 16%), rgb(var(--ch-edge) / 4%));
		--track: 0.1em;
		color: var(--color-info);
		font-size: 0.68rem;
		font-weight: 400;
		white-space: nowrap;
	}

	td {
		border-block-end: 1px solid var(--color-line-low);
	}

	/* Identifiers are one unit. The table scrolls rather than splitting a member
	name into arbitrary fragments on a narrow screen. */
	.table-scroll code {
		overflow-wrap: normal;
		white-space: nowrap;
	}

	tr:nth-child(even) td {
		background: rgb(var(--ch-edge) / 3%);
	}

	tr:hover td {
		background: rgb(var(--ch-edge) / 10%);
		color: var(--color-text-strong);
	}

	tr:last-child td {
		border-block-end: 0;
	}

	:is(th, td).num {
		text-align: end;
		font-variant-numeric: tabular-nums;
	}

	/* A status tone: .info, .success, .warning and .danger set --tone and its
	channel --ch-tone, and a toned component reads only those. An alert
	without a tone is info. */
	:where(.alert) {
		--tone: var(--color-info);
		--ch-tone: var(--ch-info);
	}

	.info {
		--tone: var(--color-info);
		--ch-tone: var(--ch-info);
	}

	.success {
		--tone: var(--color-success);
		--ch-tone: var(--ch-success);
	}

	.warning {
		--tone: var(--color-warning);
		--ch-tone: var(--ch-warning);
	}

	.danger {
		--tone: var(--color-danger);
		--ch-tone: var(--ch-danger);
	}

	/* A form button that reads as a link, for an action beside a heading. */
	.link-button {
		min-block-size: 2.75rem;
		border: 0;
		padding: 0;
		background: none;
		color: var(--color-info);
		font: inherit;
		font-size: 0.85rem;
		text-decoration: underline;
		cursor: pointer;

		&:hover {
			color: var(--color-text-strong);
		}
	}

	/* A section's heading with what belongs beside it: a count, a link or a
	form button, at the far end, wrapping under it on a narrow screen. */
	.section-head {
		display: flex;
		flex-wrap: wrap;
		align-items: baseline;
		justify-content: space-between;
		gap: var(--space-3);

		> a {
			color: var(--color-info);
			font-size: 0.85rem;
		}
	}

	.alert {
		display: flex;
		align-items: flex-start;
		gap: var(--space-3);
		border-inline-start: 3px solid var(--tone);
		padding: var(--space-3) var(--space-4);
		background: linear-gradient(90deg, rgb(var(--ch-tone) / 12%), transparent);

		> .icon {
			inline-size: 1.75rem;
			block-size: 1.75rem;
			color: var(--tone);
		}

		> div {
			display: flex;
			flex-direction: column;
			gap: var(--space-1);
			min-inline-size: 0;
		}

		/* A named tone colours all four edges, though only the inline-start one
		has width. */
		&:is(.info, .success, .warning, .danger) {
			border-color: var(--tone);
		}
	}

	/* Devicon's Rust mark (revision 7330accdbc47e2dc0c19789a48533c4a3c50fe58, MIT)
	is black, so the dark theme inverts it. */
	:root:not([data-theme="light"]) .icon-devicon-rust {
		filter: invert(1);
	}

	/* The six-language strip keeps the prototype's local tab scrolling on phones. */
	.topbar-languages { max-inline-size: 100%; min-inline-size: 0; }
	.topbar-languages .toggle { max-inline-size: 100%; }
	.topbar-tools { min-inline-size: 0; max-inline-size: 100%; box-sizing: border-box; }

	.offers-note {
		color: var(--color-text-dim);
		font-size: 0.78rem;

		a {
			color: var(--color-info);
		}
	}
}

@layer utilities {
	/* Text only for assistive technology, such as a role="status" announcement. */
	.visually-hidden {
		position: absolute;
		inline-size: 1px;
		block-size: 1px;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
	}

	@media (forced-colors: active) {
		.modal,
		.panel,
		.row,
		.field,
		.button,
		.tabs {
			border-image: none;
			border-color: CanvasText;
		}

		.tab:is([aria-selected="true"], [aria-pressed="true"], [aria-current="true"]) {
			outline: 2px solid Highlight;
		}
	}
}
