/*
 * Hand-rolled, no framework and no build step, matching the Cix
 * dashboard's own approach (its ADR-0010). Colours are defined once as
 * custom properties so light and dark differ only in the token block,
 * never in a rule.
 *
 * Layout is three fixed regions and one that grows: a menu bar for what
 * the registry HOLDS, a search surface in the middle, a log dock that
 * is always present whether open or shut, and a status bar for what the
 * server is DOING. The two kinds of number change on completely
 * different timescales, which is why they live in different places.
 */
/*
 * Inter, self-hosted. The brand system names it as the primary UI and
 * display face (Cix Brand Guidelines v1.0, Typography), and a fallback
 * stack alone would not deliver it -- almost nobody has Inter
 * installed, so the page would quietly render in the system sans the
 * guidelines list only as a fallback.
 *
 * One variable file rather than four static weights: it carries the
 * opsz axis, which is what "Inter Display" is -- the optical size cut
 * for large headlines -- so the same 337KB covers both roles the brand
 * asks for. No CDN, for the reason every other asset here is local: an
 * operations dashboard has to work when other things do not.
 */
@font-face {
	font-family: "InterVariable";
	src: url("/InterVariable.woff2") format("woff2");
	font-weight: 100 900;
	font-style: normal;
	font-display: swap;
}

/*
 * Colours are the Cix palette (Brand Guidelines v1.0, Colour system),
 * defined once as custom properties so light and dark differ only in
 * this block and never in a rule.
 *
 * Dark mode uses the brand hexes unchanged -- every signal colour is
 * built for a dark ground and clears 5:1 on Carbon.
 *
 * Light mode cannot. Measured against Paper, Nickel is 2.28:1, Copper
 * 2.73:1, Phosphor 1.25:1 and Amber 1.70:1 -- all below the 4.5:1 a
 * reader needs. So the light values are each darkened along their own
 * hue until they clear it, keeping the hue and saturation of the brand
 * colour they come from. That is a deliberate, documented and
 * reversible exception of the kind the guidelines' AUTHORITY clause
 * provides for, and it is the same instruction the Product UI section
 * gives directly: never generate light mode by inverting dark.
 */
:root {
	/*
	 * Spacing, on the brand system's 8px base with 4px half-steps
	 * (Brand Guidelines v1.0, Grid, spacing and composition). Named
	 * steps rather than literals, for the same reason the colours are
	 * named: the palette swap was a contained change precisely because
	 * every value lived in one place, and spacing had drifted to 29
	 * distinct values -- 0.85rem, 0.28rem, 0.15rem -- each nudged until
	 * a component looked right, which is the practice the guidelines
	 * name outright.
	 */
	--space-1: 0.25rem;   /*  4px  fine internal gap, optical correction */
	--space-2: 0.5rem;    /*  8px  base inline gap */
	--space-3: 0.75rem;   /* 12px  half-step, where 16 is too loose */
	--space-4: 1rem;      /* 16px  standard component padding */
	--space-5: 1.5rem;    /* 24px  related content separation */
	--space-6: 2rem;      /* 32px  section separation */
	--space-7: 3rem;      /* 48px  large content group */
	--space-8: 4rem;      /* 64px  major section boundary */
	--space-9: 6rem;      /* 96px  editorial whitespace */

	color-scheme: light dark;
	--bg: #f3f0e7;         /* Paper */
	--panel: #ffffff;      /* white, which the brand allows for system-native surfaces */
	--text: #0a0d0f;       /* Carbon */
	--muted: #646f76;      /* Nickel, darkened to 4.5:1 on Paper */
	--border: #dcd7ca;     /* Paper-tinted divider; Machined is a dark-surface colour */
	--ok: #547813;         /* Phosphor, darkened */
	--warn: #8d6715;       /* Amber, darkened */
	--error: #d02d25;      /* Fault, darkened */
	--accent: #ad5424;     /* Copper, darkened */
	--logo-accent: #ad5424;
	--row-alt: rgba(10, 13, 15, 0.035);
	--hover: rgba(10, 13, 15, 0.05);
	--shadow: 0 8px 28px rgba(10, 13, 15, 0.14);
}

@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) {
		--bg: #0a0d0f;         /* Carbon */
		--panel: #151a1e;      /* Ferrite */
		--text: #f3f0e7;       /* Paper */
		--muted: #98a2a8;      /* Nickel */
		--border: #242c31;     /* Machined */
		--ok: #b9e769;         /* Phosphor */
		--warn: #e4b34a;       /* Amber */
		--error: #e15d57;      /* Fault */
		--accent: #d87945;     /* Copper */
		--logo-accent: #d87945;
		--row-alt: rgba(243, 240, 231, 0.04);
		--hover: rgba(243, 240, 231, 0.07);
		--shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
	}
}

:root[data-theme="dark"] {
	--bg: #0a0d0f;
	--panel: #151a1e;
	--text: #f3f0e7;
	--muted: #98a2a8;
	--border: #242c31;
	--ok: #b9e769;
	--warn: #e4b34a;
	--error: #e15d57;
	--accent: #d87945;
	--logo-accent: #d87945;
	--row-alt: rgba(243, 240, 231, 0.04);
	--hover: rgba(243, 240, 231, 0.07);
	--shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
}

* {
	box-sizing: border-box;
}

/*
 * A visible focus state on everything keyboard-operable. The brand's
 * accessibility section requires one for every keyboard-operable
 * action, and WCAG 2.2 AA -- which it sets as the baseline -- makes it
 * a conformance item rather than a preference.
 *
 * :focus-visible rather than :focus, so a pointer click does not leave
 * a ring behind; and an outline with an offset rather than a colour
 * change, because a colour change alone conveys state by colour alone,
 * which the same section forbids.
 */
:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 2px;
	border-radius: 3px;
}

/*
 * Reduced motion, also required by the accessibility section. Nothing
 * here is load-bearing animation -- a hover tint, the log dock's
 * chevron, the activity lights -- so honouring the preference costs
 * nothing and removing it costs someone a headache.
 */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		transition-duration: 0.01ms !important;
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		scroll-behavior: auto !important;
	}
}

body {
	margin: 0;
	background: var(--bg);
	color: var(--text);
	font: 0.85rem/1.5 "InterVariable", Inter, "Noto Sans", system-ui, -apple-system,
		"Segoe UI", Roboto, sans-serif;
	display: flex;
	flex-direction: column;
	height: 100vh;
	overflow: hidden;
}

.spacer {
	flex: 1;
}

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

/* ---------- menu bar ---------- */

header {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	padding: 0 var(--space-3);
	height: 2.6rem;
	background: var(--panel);
	border-bottom: 1px solid var(--border);
	flex: none;
}

header .stat {
	color: var(--muted);
	font-size: 0.78rem;
	white-space: nowrap;
}

header .stat b {
	color: var(--text);
	font-weight: 600;
	font-variant-numeric: tabular-nums;
}

button {
	font: inherit;
	color: var(--text);
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: 6px;
	padding: var(--space-1) var(--space-3);
	cursor: pointer;
}

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

button:disabled {
	opacity: 0.4;
	cursor: default;
}

button.icon {
	border: none;
	background: none;
	padding: var(--space-1);
	line-height: 0;
	border-radius: 6px;
	color: var(--muted);
}

button.icon:hover {
	background: var(--hover);
	color: var(--text);
}

button.icon svg {
	width: 1.15rem;
	height: 1.15rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.7;
	stroke-linecap: round;
	stroke-linejoin: round;
}

button.subtle {
	border: none;
	background: none;
	color: var(--muted);
	padding: var(--space-1) var(--space-2);
}

button.subtle:hover {
	color: var(--accent);
}

/* ---------- search surface ---------- */

main {
	flex: 1;
	overflow-y: auto;
	display: flex;
	flex-direction: column;
	padding: 0 var(--space-4);
}

/*
 * An explicit display always beats the hidden attribute, so a view with
 * display:flex needs telling. Without this both views render stacked.
 */
main[hidden] {
	display: none;
}

/*
 * Centred as a group the way a search home page is, using auto margins
 * rather than justify-content: a centred flex line that overflows its
 * container becomes unreachable past the start edge, and auto margins
 * do not have that failure.
 */
.search-wrap {
	width: 100%;
	max-width: 860px;
	margin: auto;
	/*
	 * Biased upward by the extra bottom padding. A block centred on its
	 * geometry reads as slightly low when its weight is at the top, and
	 * this one is a large mark over progressively lighter text; the
	 * bias puts the search box near the optical centre instead.
	 */
	padding: var(--space-5) 0 clamp(var(--space-6), 9vh, 7rem);
}

/*
 * Once results are up they want the height, and a group that re-centres
 * on every keystroke slides the logo around under the cursor. So the
 * block settles to the top for as long as there is a list.
 */
#main-view.has-results .search-wrap {
	margin-top: 4vh;
	margin-bottom: 0;
}

/*
 * The wordmark sits above the search box rather than in the menu bar:
 * on a search-first page the name belongs with the thing you came to
 * use, not tucked into a corner.
 */
/*
 * Set as type, not shipped as traced outlines.
 *
 * The brand system is explicit that final logo artwork is still open
 * and that official geometry must not be invented from a text mockup
 * (Logo system, "PENDING FINAL ARTWORK"). Tracing a typeset name into
 * paths and shipping it as the wordmark would be doing exactly that.
 * As live text it is honestly what it is -- the product name set in
 * the brand face -- and it stays selectable and searchable.
 *
 * Lower case, because the primary wordmark is lower-case cix. Tracking
 * is left alone: the guidelines call out avoiding ultra-tight tracking
 * on display sizes, and Inter's own optical-size axis already tightens
 * appropriately as the size grows.
 */
.wordmark {
	margin: 0 0 var(--space-5);
	text-align: center;
	font-size: clamp(2.4rem, 7vw, 3.6rem);
	font-weight: 700;
	font-variation-settings: "opsz" 32;
	line-height: 1.02;
	letter-spacing: -0.015em;
	color: var(--text);
	user-select: none;
}

/*
 * cix is the wordmark; -cache is the descriptor beside it, which is
 * the lockup shape the brand describes. Copper carries the accent.
 */
.wordmark span {
	color: var(--accent);
}

/* Smaller viewports: the wordmark gives up width before the box does. */
@media (max-height: 620px) {
	.logo {
		width: min(330px, 58%);
	}

	.wordmark {
		margin-bottom: var(--space-4);
	}
}

/*
 * The mark in the menu bar. Sized off the bar rather than the font so
 * it stays put if the type scale moves, and it is the only thing in
 * the bar that is not a number -- which is the point of it being here.
 */
.brand {
	display: flex;
	align-items: center;
	flex: none;
	margin-right: var(--space-1);
	color: var(--text);
	text-decoration: none;
}

/*
 * A square tile reads smaller than a wide glyph at the same height, so
 * it gets a little more than the bare mark did.
 */
.brand .mark {
	/*
	 * 0.825rem to match the cix web console's own .header-mark exactly,
	 * so the two products wear the mark at one size rather than at two
	 * that are nearly the same. A height, not spacing -- the 8px grid
	 * (ADR-0011) governs gaps and padding, and an icon dimension is
	 * governed by the brand's iconography rules instead.
	 */
	height: 0.825rem;
	width: auto;
	display: block;
}

/*
 * The tile inverts with the page rather than being fixed dark: the
 * tile takes the text colour and the glyph the background, so it holds
 * the same contrast either way round instead of sitting as a dark
 * block on a light page. web/favicon.svg carries the same pairing as a
 * media query, because a favicon is drawn by the browser chrome and
 * cannot see these properties.
 */
/*
 * One colour, taking the page's text colour, with no enclosing shape.
 * The brand's logo criteria require a mark that works in one colour and
 * does not depend on a rounded-square app icon behind it.
 */
.mark-fg {
	fill: currentColor;
}

.brand:hover {
	color: var(--logo-accent);
}

.search-box {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: 999px;
	padding: var(--space-2) var(--space-4);
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}

.search-box:focus-within {
	border-color: var(--accent);
	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.search-icon {
	width: 1.1rem;
	height: 1.1rem;
	flex: none;
	fill: none;
	stroke: var(--muted);
	stroke-width: 1.9;
	stroke-linecap: round;
}

#q {
	flex: 1;
	border: none;
	background: none;
	color: var(--text);
	font: inherit;
	font-size: 1rem;
	padding: var(--space-1) 0;
}

#q:focus {
	outline: none;
}

#q::-webkit-search-cancel-button {
	display: none;
}

.chips {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2);
	margin: var(--space-3) 0 0;
}

.chip {
	font-size: 0.75rem;
	padding: var(--space-1) var(--space-2);
	border-radius: 999px;
	color: var(--muted);
}

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

/*
 * The selected architecture is filled rather than outlined. An active
 * filter silently removing rows is the kind of thing someone forgets
 * they switched on, so it has to be the loudest thing in the strip.
 */
.chip.on {
	background: var(--accent);
	border-color: var(--accent);
	color: #fff;
}

.chip.on:hover {
	color: #fff;
	opacity: 0.9;
}

/* Results drop beneath the box, as a floating sheet rather than a
 * section of the page -- the search is the subject, not the table. */
.results {
	margin-top: var(--space-3);
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: 10px;
	box-shadow: var(--shadow);
	overflow: hidden;
}

.table-wrap {
	overflow-x: auto;
	max-height: 52vh;
	overflow-y: auto;
}

table {
	width: 100%;
	border-collapse: collapse;
	font-size: 0.8rem;
}

th {
	position: sticky;
	top: 0;
	background: var(--panel);
	text-align: left;
	font-weight: 600;
	color: var(--muted);
	padding: var(--space-2) var(--space-4);
	border-bottom: 1px solid var(--border);
	white-space: nowrap;
	font-size: 0.7rem;
	text-transform: uppercase;
	letter-spacing: 0.03em;
}

td {
	padding: var(--space-2) var(--space-4);
	border-bottom: 1px solid var(--border);
	white-space: nowrap;
}

tbody tr:nth-child(even) {
	background: var(--row-alt);
}

tbody tr:hover {
	background: var(--hover);
}

/* A right-aligned column needs its heading right-aligned too, or the
 * label floats away from the values it names. */
th.num,
td.num {
	text-align: right;
	font-variant-numeric: tabular-nums;
}

.mono {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 0.75rem;
}

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

a:hover {
	text-decoration: underline;
}

.digest {
	color: var(--muted);
	cursor: pointer;
}

.digest:hover {
	color: var(--accent);
}

.results-foot {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	padding: var(--space-2) var(--space-3);
	border-top: 1px solid var(--border);
	font-size: 0.75rem;
}

.results-foot button {
	font-size: 0.75rem;
	padding: var(--space-1) var(--space-2);
}

.empty {
	padding: var(--space-5) var(--space-4);
	color: var(--muted);
	text-align: center;
}

.hint {
	color: var(--muted);
	font-size: 0.75rem;
	margin: var(--space-5) auto 0;
	max-width: 46rem;
	text-align: center;
	line-height: 1.6;
}

/* ---------- help ---------- */

/*
 * A routed overlay rather than a modal: an API reference needs room to
 * scroll and a table of contents, and both are hostile in a dialog.
 */
#help {
	flex: 1;
	display: flex;
	overflow: hidden;
	background: var(--bg);
}

#help[hidden] {
	display: none;
}

.help-nav {
	flex: none;
	width: 13rem;
	padding: var(--space-5) var(--space-4);
	border-right: 1px solid var(--border);
	background: var(--panel);
	overflow-y: auto;
	display: flex;
	flex-direction: column;
	gap: var(--space-1);
}

.help-title {
	font-size: 0.7rem;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--muted);
	margin-bottom: var(--space-2);
}

.help-nav a {
	color: var(--text);
	padding: var(--space-1) var(--space-2);
	border-radius: 6px;
	font-size: 0.8rem;
	text-decoration: none;
}

.help-nav a:hover {
	background: var(--hover);
	color: var(--accent);
	text-decoration: none;
}

#help-close {
	margin-top: auto;
	font-size: 0.75rem;
}

.help-body {
	flex: 1;
	overflow-y: auto;
	padding: var(--space-5) var(--space-6) var(--space-9);
	scroll-behavior: smooth;
}

.help-body > * {
	max-width: 46rem;
}

.help-body h2 {
	font-size: 1.05rem;
	font-weight: 600;
	letter-spacing: -0.01em;
	margin: var(--space-6) 0 var(--space-3);
	padding-top: var(--space-2);
}

.help-body h2:first-child {
	margin-top: 0;
}

.help-body p {
	margin: var(--space-2) 0;
	line-height: 1.65;
}

.help-body code {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 0.78rem;
	background: var(--row-alt);
	border-radius: 4px;
	padding: 0 var(--space-1);
}

.help-body pre {
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: 8px;
	padding: var(--space-3) var(--space-4);
	overflow-x: auto;
	margin: var(--space-3) 0;
}

.help-body pre code {
	background: none;
	padding: 0;
	font-size: 0.76rem;
	line-height: 1.6;
}

.help-table {
	margin: var(--space-3) 0;
	border: 1px solid var(--border);
	border-radius: 8px;
	overflow: hidden;
	background: var(--panel);
}

.help-table td {
	padding: var(--space-2) var(--space-3);
	border-bottom: 1px solid var(--border);
	white-space: normal;
	vertical-align: top;
}

.help-table tr:last-child td {
	border-bottom: none;
}

.help-table td:first-child {
	width: 4.5rem;
	white-space: nowrap;
}

.tag {
	display: inline-block;
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 0.68rem;
	font-weight: 600;
	padding: 0 var(--space-2);
	border-radius: 4px;
	border: 1px solid currentColor;
	color: var(--muted);
}

.tag.ok {
	color: var(--ok);
}

.tag.warn {
	color: var(--warn);
}

.help-body .note {
	border-left: 2px solid var(--accent);
	padding-left: var(--space-3);
	color: var(--muted);
	font-size: 0.8rem;
}

/* ---------- maintenance popover ---------- */

.popover {
	position: absolute;
	top: 2.8rem;
	right: 0.85rem;
	z-index: 30;
	width: 20rem;
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: 10px;
	box-shadow: var(--shadow);
	padding: var(--space-3);
}

.popover label {
	display: block;
	font-size: 0.72rem;
	color: var(--muted);
	margin-bottom: var(--space-1);
}

.popover input {
	width: 100%;
	font: inherit;
	color: var(--text);
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: 6px;
	padding: var(--space-1) var(--space-2);
}

.popover input:focus {
	outline: none;
	border-color: var(--accent);
}

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

.popover-actions button {
	font-size: 0.75rem;
}

.popover p {
	margin: var(--space-2) 0 0;
	font-size: 0.72rem;
}

/* ---------- log dock ---------- */

#logdock {
	flex: none;
	background: var(--panel);
	border-top: 1px solid var(--border);
	display: flex;
	flex-direction: column;
}

/*
 * Grab strip on the dock's top edge. Sits over the border so the
 * target is easier to hit than the 1px line it appears to be.
 *
 * The three numbers are load-bearing and must keep summing to zero:
 * the strip is 8px tall and pulled 4px up and 4px down, so it costs no
 * layout height while still presenting a real target. It was 7/-4/-3,
 * which summed the same way but put a 3px value on nothing.
 */
.log-resize {
	height: 8px;
	margin-top: -4px;
	margin-bottom: -4px;
	flex: none;
	cursor: ns-resize;
	background: transparent;
	transition: background 100ms linear;
}

.log-resize:hover,
.log-resize.dragging {
	background: var(--accent);
}

#logdock.collapsed .log-resize {
	display: none;
}

/* Whatever the drag set, don't let the dock outgrow the window. */
body.resizing {
	cursor: ns-resize;
	user-select: none;
}

#logdock.collapsed #log {
	display: none;
}

.logdock-bar {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	padding: var(--space-1) var(--space-3);
	font-size: 0.75rem;
	flex: none;
}

.log-toggle {
	display: flex;
	align-items: center;
	gap: var(--space-1);
	border: none;
	background: none;
	color: var(--text);
	padding: var(--space-1) 0;
	font-size: 0.75rem;
	font-weight: 600;
}

.log-toggle:hover {
	color: var(--accent);
}

.chev {
	width: 0.9rem;
	height: 0.9rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
	transform: rotate(180deg);
	transition: transform 120ms ease;
}

#logdock.collapsed .chev {
	transform: rotate(0deg);
}

#log {
	height: 14rem;
	max-height: 70vh;
	overflow-y: auto;
	padding: var(--space-1) var(--space-3) var(--space-2);
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 0.72rem;
	line-height: 1.55;
}

#log div {
	white-space: pre-wrap;
	word-break: break-all;
}

#log .warn {
	color: var(--warn);
}

#log .err {
	color: var(--error);
}

/* ---------- status bar ---------- */

footer {
	flex: none;
	display: flex;
	align-items: center;
	gap: var(--space-2);
	height: 1.7rem;
	padding: 0 var(--space-3);
	background: var(--panel);
	border-top: 1px solid var(--border);
	color: var(--muted);
	font-size: 0.72rem;
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
	overflow: hidden;
}

.sep {
	width: 1px;
	height: 0.85rem;
	background: var(--border);
	flex: none;
}

.led-group {
	display: flex;
	align-items: center;
	gap: var(--space-1);
}

/* Idle is the same lamp at rest -- dimmed, not a different colour, so a
 * blink brightens rather than switching identity. */
.led {
	width: 0.55rem;
	height: 0.55rem;
	border-radius: 50%;
	background: var(--led-color, var(--muted));
	box-shadow: 0 0 5px 1px transparent;
	transition: box-shadow 90ms linear, filter 90ms linear;
	filter: brightness(0.75);
	flex: none;
	display: inline-block;
}

.led.lit {
	filter: brightness(1.4);
	box-shadow: 0 0 6px 2px var(--led-color, var(--muted));
}

/* Reachability is steady rather than blinking: it reports a state, not
 * an event. */
.led.up {
	--led-color: var(--ok);
	filter: brightness(1.2);
	box-shadow: 0 0 6px 1px var(--ok);
}

.led.down {
	--led-color: var(--error);
	filter: brightness(1.2);
	box-shadow: 0 0 6px 1px var(--error);
}

/*
 * The release is a small integer next to a much longer version, so it
 * gets only the width it needs -- otherwise the column reads as empty.
 */
.rel {
	width: 3.5rem;
	color: var(--muted);
}

/* ---------- sortable columns ---------- */

th.sortable {
	cursor: pointer;
	user-select: none;
}

th.sortable:hover {
	color: var(--accent);
}

/*
 * The caret marks the active column only. Reserving room for one on
 * every sortable header would pull the numeric columns out of line
 * with the figures underneath them, which is a worse trade than the
 * small shift when the sort changes.
 */
th.sortable[aria-sort="ascending"]::after {
	content: " \25B4";
}

th.sortable[aria-sort="descending"]::after {
	content: " \25BE";
}

/*
 * Hidden until two artifacts actually differ by it -- see the comment
 * in renderResults(). A column repeating one value is worse than no
 * column: it costs width and teaches nothing.
 */
.arch-col {
	display: none;
}

.show-arch .arch-col {
	display: table-cell;
}

/*
 * The one warning that lives in the menu bar. It is about what the
 * registry HOLDS rather than what it is doing, which is why it sits
 * here and not in the status bar.
 */
/*
 * Compounded with .stat, not on its own: "header .stat" already sets a
 * muted colour and out-specifies a lone class, so the warning would
 * render in exactly the grey it is meant to stand out from.
 */
.stat.warn-stat {
	color: var(--warn);
	font-weight: 600;
}

/* Same rule as .arch-col: a column of blanks teaches nothing. */
.signed-col {
	display: none;
}

.show-signed .signed-col {
	display: table-cell;
}

/*
 * A build from a modified tree is not the release its version names.
 * Coloured rather than annotated, because the status bar is read at a
 * glance and an extra word there is read as part of the version.
 */
/*
 * The build fields, laid out like the columns they mirror rather than
 * run together into one string.
 */
.build-group {
	display: flex;
	align-items: center;
	gap: var(--space-2);
}

/*
 * Release and architecture sit behind the version, the same ordering
 * the table gives them: the version is what is being read, and these
 * two qualify it.
 */
.build-minor {
	opacity: 0.7;
}

.dirty-build {
	color: var(--warn);
}

/* And again: while one encoding exists it repeats ".tar.gz" down the page. */
.format-col {
	display: none;
}

.show-format .format-col {
	display: table-cell;
}

/*
 * One line per encoding, in every column that carries a per-encoding
 * fact. The line box is what keeps format, signature, size and digest
 * reading across: they are stacked in the same order and so line up
 * without any of them knowing about the others.
 */
.stack-line + .stack-line {
	margin-top: 2px;
}
