/* VRCDN status page.
   Implements the design guide: dark, square, dense. Tokens only, no literal
   colours in components, no border-radius other than 0 or 50%. */

/* 1. Tokens ------------------------------------------------------------- */

:root {
	/* Brand */
	--accent:       #3be2e2;
	--accent-dim:   #2bb4b4;
	--accent-ink:   #04211f;
	--accent-wash:  rgba(59, 226, 226, 0.10);
	--accent-line:  rgba(59, 226, 226, 0.35);

	/* Surfaces, darkest to lightest */
	--bg:            #0a0c0d;
	--surface:       #111315;
	--surface-2:     #16191b;
	--surface-3:     #1d2124;
	--border:        #24292d;
	--border-strong: #333a40;

	/* Type */
	--text:       #f4f7f8;
	--text-muted: #97a2a8;
	--text-faint: #68737a;

	/* Status */
	--ok:        #3be2e2;  --ok-bg:     rgba(59, 226, 226, 0.12);
	--warn:      #ffd23f;  --warn-bg:   rgba(255, 210, 63, 0.12);
	--danger:    #ff4d63;  --danger-bg: rgba(255, 77, 99, 0.12);
	--info:      #9d7cff;  --info-bg:   rgba(157, 124, 255, 0.12);
	--live:      #ff1a34;

	/* Layout */
	--sidebar-w:   236px;
	--topbar-h:    62px;
	--content-max: 2400px;
	--prose-max:   90ch;

	--font: "Noto Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
		"Helvetica Neue", Arial, sans-serif;
	--mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas,
		"Liberation Mono", monospace;

	color-scheme: dark;
}

/* 2. Base layer --------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
	margin: 0;
	background: var(--bg);
	color: var(--text);
	font-family: var(--font);
	font-size: 14px;
	line-height: 1.5;
	-webkit-font-smoothing: antialiased;
}

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

h1, h2, h3, h4 { margin: 0; font-weight: 800; letter-spacing: -0.01em; }

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

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

/* The UA sheet's [hidden] rule loses to any author rule that sets display, and
   several things here are flex. Not a style preference: "not currently
   relevant" must win. */
[hidden] { display: none !important; }

.eyebrow {
	font-size: 11px;
	font-weight: 800;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--text-faint);
}

/* 3. Layout ------------------------------------------------------------- */

/* A status page has one subject and no navigation, so the shell is the single
   main column of the system's two-column shell. */
.shell {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	min-height: 100vh;
}

.main { min-width: 0; display: flex; flex-direction: column; min-height: 100vh; }

.topbar {
	position: sticky; top: 0; z-index: 20;
	height: var(--topbar-h);
	display: flex;
	background: rgba(10, 12, 13, 0.9);
	backdrop-filter: blur(10px);
	border-bottom: 1px solid var(--border);
}

.topbar-inner {
	display: flex; align-items: center; gap: 16px;
	width: 100%; max-width: var(--content-max);
	margin-inline: auto; padding: 0 24px;
}
.topbar-inner .spacer { flex: 1; }
.topbar-meta { color: var(--text-muted); white-space: nowrap; }

.brand {
	display: inline-flex; align-items: baseline; gap: 8px;
	font-size: 17px; font-weight: 800; letter-spacing: -0.01em;
	color: var(--text);
}
.brand:hover { text-decoration: none; color: var(--accent); }
.brand-sub {
	font-size: 10px; font-weight: 800; letter-spacing: 2px;
	text-transform: uppercase; color: var(--accent);
}

.content {
	padding: 22px 24px 60px;
	max-width: var(--content-max);
	width: 100%;
	margin-inline: auto;
	flex: 1;
}

.foot {
	border-top: 1px solid var(--border);
	background: var(--surface);
	color: var(--text-faint);
	font-size: 12px;
}
.foot-inner {
	display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
	width: 100%; max-width: var(--content-max);
	margin-inline: auto; padding: 14px 24px;
}
.foot-inner .spacer { flex: 1; }

@media (max-width: 900px) {
	.content, .topbar-inner, .foot-inner { padding-left: 14px; padding-right: 14px; }
}

/* How current the page is stays visible as long as it fits: on a status page
   that is not decoration. Below this the pill carries it alone. */
@media (max-width: 560px) {
	.topbar-meta { display: none; }
}

@media print {
	.topbar, .btn { display: none !important; }
}

/* Grids */

.grid   { display: grid; gap: 14px; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

.stack { display: flex; flex-direction: column; gap: 14px; }
.row   { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Page heading */

.page-head {
	display: flex; align-items: flex-start; gap: 15px;
	margin-bottom: 20px; flex-wrap: wrap;
}
.page-head h1 { font-size: 26px; font-weight: 800; letter-spacing: -0.02em; }

.page-head .sub { color: var(--text-muted); font-size: 13px; margin-top: 3px; }
.page-head .spacer { flex: 1; }

/* 4. Components --------------------------------------------------------- */

/* Cards */

.card { background: var(--surface); border: 1px solid var(--border); }

.card-head {
	display: flex; align-items: center; gap: 12px;
	padding: 13px 16px;
	border-bottom: 1px solid var(--border);
}
.card-head h2 {
	font-size: 13px; font-weight: 800;
	letter-spacing: .04em; text-transform: uppercase;
}
.card-head .spacer { flex: 1; }

.card-body { padding: 16px; }

/* A collapsible card. Closed, the header is the whole card, so it carries the
   verdict and the pooled uptime for everything folded behind it. */

.section-head { cursor: pointer; list-style: none; user-select: none; }
/* Both spellings of "do not draw the default triangle": we draw our own, which
   can be positioned and animated. */
.section-head::-webkit-details-marker { display: none; }
.section-head::marker { content: ""; }

.section-head:hover { background: var(--surface-2); }
/* Keyboard focus must be visible: the summary is the control, and somebody
   tabbing to it has no pointer to tell them where they are. */
.section-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* The chevron. A square-cornered caret drawn from two borders, since the design
   has no rounded geometry to borrow. */
.section-chevron {
	flex: none; width: 7px; height: 7px;
	border-right: 2px solid var(--text-faint);
	border-bottom: 2px solid var(--text-faint);
	transform: rotate(-45deg);
	margin-left: 2px; margin-right: 2px;
	transition: transform .12s ease;
}
.section[open] > .section-head .section-chevron { transform: rotate(45deg); }
.section-head:hover .section-chevron { border-color: var(--text); }

/* The header must never push its own badge off the edge: the verdict is the
   one thing on it that cannot be allowed to go missing. So the long parts give
   way first, and if it still will not fit, the figure and the badge drop to a
   line of their own rather than overflowing. They drop together and stay to
   the right, because a badge that lands on the left under the name reads as a
   row of the card rather than as the card's own verdict. */
.section-head { flex-wrap: wrap; }
.card-head h2 { min-width: 0; }
.section-count { font-variant-numeric: tabular-nums; min-width: 0; }
.section-meta {
	display: inline-flex; align-items: center; gap: 12px;
	margin-left: auto;
}
.section-uptime { font-variant-numeric: tabular-nums; }

/* Closed, the header is the only thing left, so it should not look like a card
   that has had its contents cut off. */
.section:not([open]) > .section-head { border-bottom: none; }

@media (prefers-reduced-motion: reduce) {
	.section-chevron { transition: none; }
}

.card-note {
	padding: 10px 16px;
	padding-right: max(16px, calc(100% - var(--prose-max)));
	font-size: 12px;
	color: var(--text-faint);
	border-top: 1px solid var(--border);
	background: var(--surface-2);
}

/* Badges */

.badge {
	display: inline-flex; align-items: center; gap: 5px;
	padding: 2px 8px;
	font-size: 11px; font-weight: 700; line-height: 1.7;
	white-space: nowrap;
	border: 1px solid transparent;
	letter-spacing: .02em;
}
.badge-ok     { background: var(--ok-bg);     color: var(--ok);     border-color: var(--accent-line); }
.badge-warn   { background: var(--warn-bg);   color: var(--warn);   border-color: rgba(255,210,63,.30); }
.badge-danger { background: var(--danger-bg); color: var(--danger); border-color: rgba(255,77,99,.32); }
.badge-info   { background: var(--info-bg);   color: var(--info);   border-color: rgba(157,124,255,.30); }
.badge-muted  { background: var(--surface-3); color: var(--text-muted); border-color: var(--border-strong); }

/* Buttons */

.btn {
	display: inline-flex; align-items: center; justify-content: center; gap: 7px;
	padding: 8px 20px;
	border-radius: 0;
	border: 2px solid var(--border-strong);
	background: transparent;
	color: var(--text);
	font-family: inherit; font-size: 13px; font-weight: 700;
	letter-spacing: .02em;
	cursor: pointer; white-space: nowrap;
	transition: background .12s, border-color .12s, color .12s;
}
.btn:hover { background: var(--surface-3); border-color: var(--text-faint); text-decoration: none; }
.btn-ghost { border-color: var(--border); color: var(--text-muted); }
.btn-ghost:hover { border-color: var(--accent); background: transparent; color: var(--accent); }
.btn-sm { padding: 3px 11px; font-size: 12px; border-width: 1px; }

/* Tables */

.table-wrap { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: 13px; }

thead th {
	position: sticky; top: 0;
	background: var(--surface-2);
	text-align: left;
	font-size: 10px; font-weight: 800; letter-spacing: 1.5px;
	text-transform: uppercase; color: var(--text-faint);
	padding: 10px 14px;
	border-bottom: 1px solid var(--border);
	white-space: nowrap;
}
thead th.right { text-align: right; }

tbody td { padding: 10px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--surface-2); }

td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

.empty { padding: 46px 20px; text-align: center; color: var(--text-faint); }
.empty strong {
	display: block; color: var(--text-muted);
	font-size: 14px; font-weight: 700; margin-bottom: 5px;
}

/* Property lists */

dl.props {
	margin: 0;
	display: grid;
	grid-template-columns: minmax(120px, auto) minmax(0, 1fr);
	gap: 0 18px;
}
dl.props dt {
	padding: 8px 0;
	font-size: 11px; font-weight: 700; letter-spacing: .5px;
	text-transform: uppercase; color: var(--text-faint);
	border-bottom: 1px solid var(--border);
}
dl.props dd {
	margin: 0; padding: 8px 0;
	font-size: 13px;
	border-bottom: 1px solid var(--border);
	overflow-wrap: anywhere;
}
dl.props dt:last-of-type, dl.props dd:last-of-type { border-bottom: none; }

/* Flashes */

.flashes { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.flash {
	display: flex; align-items: flex-start; gap: 10px;
	padding: 12px 15px;
	border: 1px solid; border-left-width: 4px;
	font-size: 13px; font-weight: 600;
}
.flash .spacer { flex: 1; }
.flash-ok     { background: var(--ok-bg);     border-color: var(--accent-line);    border-left-color: var(--accent); color: #b0f6f6; }
.flash-warn   { background: var(--warn-bg);   border-color: rgba(255,210,63,.32);  border-left-color: var(--warn);   color: #ffe38a; }
.flash-danger { background: var(--danger-bg); border-color: rgba(255,77,99,.34);   border-left-color: var(--danger); color: #ffa8b2; }
.flash-info   { background: var(--info-bg);   border-color: rgba(157,124,255,.32); border-left-color: var(--info);   color: #d5c9ff; }

/* The masthead. It sits on the page rather than in a card: it is the first
   thing a viewer meets, and a box around it would make the verdict look like
   one more panel among the panels below. The colour of the headline is the
   whole of the tone. */

.hero {
	display: flex; flex-direction: column; align-items: center;
	text-align: center;
	padding: 34px 24px 30px;
}

.hero-logo {
	width: 124px; height: auto;
	margin-bottom: 22px;
}

.hero-eyebrow { margin-bottom: 8px; }

.hero-title {
	font-size: 30px; font-weight: 800;
	letter-spacing: -0.02em;
	line-height: 1.15;
	color: var(--hero-ink, var(--text));
}

.hero-detail {
	margin: 10px 0 0;
	font-size: 13px;
	color: var(--text-muted);
	max-width: 60ch;
}

/* One custom property per tone, so nothing can end up half-coloured. */
.hero-ok     { --hero-ink: #b0f6f6; }
.hero-info   { --hero-ink: #d5c9ff; }
.hero-warn   { --hero-ink: #ffe38a; }
.hero-danger { --hero-ink: #ffa8b2; }
.hero-muted  { --hero-ink: var(--text-muted); }

@media (max-width: 640px) {
	.hero { padding: 26px 18px 22px; }
	.hero-title { font-size: 23px; }
	.hero-logo { width: 96px; }
}

/* Components.
   The list of things and their recent record: the shape a status page has had
   since the first one. Regions and services render the same way and differ
   only in what hangs below the name. */

.components { display: flex; flex-direction: column; }

.component { padding: 14px 16px; border-bottom: 1px solid var(--border); }
.component:last-child { border-bottom: none; }
.component:hover { background: var(--surface-2); }

.component-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.component-head .spacer { flex: 1; min-width: 20px; }
/* The name and the location are one item, so they can be laid out side by side
   where there is room and stacked where there is not, without the rest of the
   row having to know which. */
.component-id { display: inline-flex; align-items: baseline; gap: 10px; min-width: 0; }
.component-name { font-size: 14px; font-weight: 700; }
.component-sub { font-size: 12px; color: var(--text-faint); }
/* Two windows are in play, so the percentage carries its own. "12d 100%" says
   what the number is over; "365d 100%" says the same thing a year later. */
.component-uptime {
	font-size: 12px; color: var(--text-muted);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}
.uptime-window {
	color: var(--text-faint);
	font-size: 10px; font-weight: 800; letter-spacing: 1px;
	text-transform: uppercase;
	margin-right: 6px;
}

/* Announcements: what the checks cannot say. Same thick left edge as a notice,
   because this is a message about something. */

.anns { display: flex; flex-direction: column; gap: 10px; }

.ann {
	border: 1px solid var(--border-strong);
	border-left: 4px solid var(--text-faint);
	background: var(--surface-2);
	padding: 12px 15px;
}
.ann-info    { border-left-color: var(--info);   border-color: rgba(157,124,255,.32); background: var(--info-bg); }
.ann-warn    { border-left-color: var(--warn);   border-color: rgba(255,210,63,.32);  background: var(--warn-bg); }
.ann-danger  { border-left-color: var(--danger); border-color: rgba(255,77,99,.34);   background: var(--danger-bg); }

/* Ended, and kept only as a record. Toned down so that a month of resolved
   incidents does not read as a page full of things currently on fire; the
   severity badge still says what it was. */
.anns-past .ann {
	background: var(--surface-2);
	border-color: var(--border-strong);
	border-left-color: var(--text-faint);
}

.ann-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ann-head .spacer { flex: 1; }
.ann-title { font-size: 14px; font-weight: 800; letter-spacing: -0.01em; }
.ann-where { font-variant-numeric: tabular-nums; }
.ann-body { font-size: 13px; color: var(--text); margin-top: 6px; }
.ann-window { font-variant-numeric: tabular-nums; }

.ann-updates {
	list-style: none; margin: 10px 0 0; padding: 10px 0 0;
	border-top: 1px solid var(--border-strong);
	display: flex; flex-direction: column; gap: 6px;
}
.ann-updates li { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }

/* The compact form, sitting under the component it is about. */
.ann-inline {
	display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap;
	margin-top: 10px;
	padding: 8px 11px;
	border: 1px solid var(--border-strong);
	border-left: 3px solid var(--text-faint);
	background: var(--surface-2);
}
.ann-inline .spacer { flex: 1; }
.ann-inline.ann-info   { border-left-color: var(--info); }
.ann-inline.ann-warn   { border-left-color: var(--warn); }
.ann-inline.ann-danger { border-left-color: var(--danger); }
.ann-inline-body { display: flex; flex-direction: column; gap: 2px; font-size: 12px; min-width: 0; }
.ann-inline-detail { color: var(--text-muted); }

/* The uptime strip. One bar per check, laid out server-side and stretched to
   the width of the row. */
.history { display: block; width: 100%; height: 26px; margin-top: 10px; }
.hist-bar { fill: var(--surface-3); }
.hist-bar.is-ok     { fill: var(--ok); }
.hist-bar.is-info   { fill: var(--info); }
.hist-bar.is-warn   { fill: var(--warn); }
.hist-bar.is-danger { fill: var(--danger); }

/* The timestamp and text of one operator update, down an announcement's
   timeline. */
.event-time {
	font-size: 12px; color: var(--text-faint);
	white-space: nowrap; font-variant-numeric: tabular-nums;
}
.event-body { font-size: 13px; }

.demo-detail { font-size: 12px; font-weight: 400; margin-top: 3px; }

/* Tabs: square, with a solid accent underline when active. Used for the demo
   scenario picker. */
.filter-chips {
	display: flex; gap: 0; flex-wrap: wrap;
	margin-bottom: 14px;
	border-bottom: 1px solid var(--border);
}
.chip {
	display: inline-flex; align-items: center; gap: 7px;
	padding: 8px 16px;
	border-bottom: 3px solid transparent;
	color: var(--text-muted);
	font-size: 12px; font-weight: 700; letter-spacing: .03em;
	margin-bottom: -1px;
}
.chip:hover { color: var(--text); background: var(--surface-2); text-decoration: none; }
.chip.active { border-bottom-color: var(--accent); color: var(--accent); }

/* Charts. Server-rendered SVG, stretched to the card width, with no numbers on
   the y axis: every series is scaled against a ceiling the page keeps to
   itself, and an axis label would give that away. */

.charts {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
	gap: 14px;
}
/* Each chart is its own panel now that they sit on the page rather than inside
   a card, so the row does not read as one wide box with dividers in it. */
.chart {
	padding: 14px 16px;
	background: var(--surface);
	border: 1px solid var(--border);
	min-width: 0;
}
.charts-note { line-height: 1.5; padding: 0 2px; }
.chart-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.chart-head .spacer { flex: 1; }
.chart-title { font-size: 12px; font-weight: 800; letter-spacing: .02em; }
.chart-level { white-space: nowrap; }

/* Tall enough to be worth looking at, now that it has the room the explanation
   underneath used to take. The viewBox is stretched rather than rescaled, so
   the extra height is extra vertical resolution on the same data, and the
   stroke stays one pixel wide through vector-effect. */
.chart-plot { display: block; width: 100%; height: 104px; }
.chart-area { fill: var(--surface-3); }
.chart-line { fill: none; stroke: var(--text-faint); stroke-width: 1.5px; }

.chart-accent .chart-area { fill: rgba(59,226,226,.14); }
.chart-accent .chart-line { stroke: var(--accent); }
.chart-info   .chart-area { fill: rgba(157,124,255,.14); }
.chart-info   .chart-line { stroke: var(--info); }
.chart-danger .chart-area { fill: rgba(255,77,99,.14); }
.chart-danger .chart-line { stroke: var(--danger); }
/* Nothing wrong, and drawn so it reads that way. */
.chart-quiet  .chart-area { fill: transparent; }
.chart-quiet  .chart-line { stroke: var(--text-faint); }

.chart-empty {
	height: 104px; display: flex; align-items: center; justify-content: center;
	background: var(--surface-2);
}
/* The time axis. The only axis these charts label: the y scale is withheld,
   but when something happened is published outright. */
.chart-axis {
	display: flex; align-items: baseline;
	margin-top: 4px;
	font-variant-numeric: tabular-nums;
}
.chart-axis .spacer { flex: 1; }


/* The decision breakdown: one stacked bar, so the shares read as parts of a
   whole rather than as separate figures. */
.decision-bar { display: block; width: 100%; height: 18px; }
.decision-ok     { fill: var(--ok); }
/* Light enough to read as a segment rather than as empty track. */
.decision-muted  { fill: #4a545b; }
.decision-warn   { fill: var(--warn); }
.decision-danger { fill: var(--danger); }

.decision-keys { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 10px; }
.decision-key { display: inline-flex; align-items: center; gap: 6px; }
.decision-dot-ok     { background: var(--ok); }
.decision-dot-muted  { background: #4a545b; }
.decision-dot-warn   { background: var(--warn); }
.decision-dot-danger { background: var(--danger); }

/* The legend, once under the component cards. The dots are status dots, one of
   the three round things in the system. */
.legend-row { display: flex; justify-content: flex-end; padding: 0 2px; }
.legend { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.legend-item { display: inline-flex; align-items: center; gap: 6px; }
.legend-dot {
	width: 7px; height: 7px; border-radius: 50%;
	flex-shrink: 0;
}
.legend-dot.is-ok     { background: var(--ok); }
.legend-dot.is-info   { background: var(--info); }
.legend-dot.is-warn   { background: var(--warn); }
.legend-dot.is-danger { background: var(--danger); }

/* 5. Narrow screens ------------------------------------------------------ */

/* Below this width the page has run out of room for the strips, so it does not
   draw them.

   A bar and its gap come to under a pixel here. The strip stops being a record
   and becomes a texture: a reader cannot pick a bar out of it, cannot hover one
   on a touch screen to find out when it was, and ten of them stacked make the
   card taller than the screen while saying less than one of them would on a
   desktop. Drawing it anyway would be drawing it for the look of the thing.

   What is left is what a phone can actually carry: the name, the year figure
   and the current verdict on one line each, and the card's own verdict on the
   header. The strips are still there on any screen with the width to render
   them honestly. */
@media (max-width: 620px) {
	.component .history { display: none; }

	/* One line per row. The name gives way and wraps within itself; the figure
	   and the badge stay together on the right, where somebody scanning down
	   the card expects them. A badge left on a line of its own, which is what
	   the old wrapping produced, read as a row of its own. */
	.component-head { flex-wrap: nowrap; }
	.component-head .spacer { min-width: 8px; }
	.component-head .badge { flex: none; }
	/* The name wraps at spaces and no tighter: "Database" broken across two
	   lines is harder to read than the location moving. So the location goes
	   under the name rather than shrinking beside it, which at the narrow end
	   left it printed over the figure in the next column. */
	.component-id { flex-direction: column; align-items: flex-start; gap: 0; }
	.component { padding: 12px 14px; }
}

/* 6. Live status -------------------------------------------------------- */

.live-pill {
	display: inline-flex; align-items: center; gap: 7px;
	padding: 4px 10px;
	border: 1px solid var(--border-strong);
	font-size: 10px; font-weight: 800; letter-spacing: 1.5px;
	text-transform: uppercase; color: var(--text-faint);
	white-space: nowrap; user-select: none;
}
.live-pill::before { content: ""; width: 7px; height: 7px; background: currentColor; flex: none; }

.live-pill[data-state="live"] { color: var(--accent); border-color: var(--accent-line); }
.live-pill[data-state="live"]::before { animation: live-pulse 2s ease-in-out infinite; }
.live-pill[data-state="stale"],
.live-pill[data-state="connecting"] { color: var(--warn); }
.live-pill[data-state="offline"] { color: var(--danger); border-color: var(--danger); }

@keyframes live-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .25 } }

.live-changed { animation: live-flash 1.2s ease-out; }
@keyframes live-flash { 0% { background: var(--accent-wash) } 100% { background: transparent } }

@media (prefers-reduced-motion: reduce) {
	.live-pill[data-state="live"]::before, .live-changed { animation: none; }
}

/* Utilities ------------------------------------------------------------- */

.muted { color: var(--text-muted); }
.faint { color: var(--text-faint); }
.small { font-size: 12px; }
.tiny  { font-size: 11px; }
.strong { font-weight: 700; }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.mono { font-family: var(--mono); font-size: .92em; }
.danger-text { color: var(--danger); }
.warn-text   { color: var(--warn); }
.accent-text { color: var(--accent); }

.mt-0 { margin-top: 0; }
.mt-1 { margin-top: 8px; }
.mt-2 { margin-top: 16px; }
.mb-1 { margin-bottom: 8px; }
.mb-2 { margin-bottom: 16px; }

.divider { height: 1px; background: var(--border); margin: 15px 0; }

/* 7. Icons ---------------------------------------------------------------- */

/* The sprite itself renders nothing: it is a bank of <symbol> definitions the
   badges point at. Kept in flow with no size rather than display:none, which
   stops <use> resolving in some browsers. */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* An icon takes the colour of the badge around it, so one rule covers every
   tone rather than one rule per icon per tone. */
.badge-icon {
	width: 12px; height: 12px;
	flex: none;
	fill: none;
	stroke: currentColor;
	stroke-width: 2.1;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* The latest update on a component row: its stage, when, and what was said,
   on one line that wraps as a whole rather than mid-sentence. */
.ann-inline-update {
	display: flex; align-items: baseline;
	flex-wrap: wrap;
	gap: 6px;
}
.ann-inline-update .badge { align-self: center; }
