/* ==========================================================================
   VRCDN Admin Dashboard

   Theme lifted from vrcdn.live's main.css:

     accent      #3be2e2
     dark base   #111111
     type        Noto Sans, 600 for UI, 800 for headings and eyebrow labels
     geometry    square — border-radius: 0 on buttons and cards
     buttons     tall, solid accent fill, or an accent outline that fills on
                 hover (the site's .btn-tos pattern)
     eyebrows    14px / 800 / letter-spacing 2px / opacity .6
     signature   a small accent circle trailing a page heading

   This is a dark-only inversion of that system. Circles are reserved for the
   things the site reserves them for — avatars, bullets, the heading dot — so
   the square geometry stays the dominant note.

   One deliberate deviation: the site sets white text on the accent fill, which
   is about 1.5:1 contrast. That is borderline on a marketing hero and unusable
   on a dense tool someone reads all day, so filled buttons here use a very dark
   teal ink instead. Shape, weight and colour are unchanged.
   ========================================================================== */

:root {
	/* Brand */
	--accent: #3be2e2;
	--accent-dim: #2bb4b4;
	--accent-ink: #04211f; /* text on an accent fill */
	--accent-wash: rgba(59, 226, 226, 0.1);
	--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: #ffa333;
	--warn-bg: rgba(255, 163, 51, 0.12);
	--danger: #ff4d63;
	--danger-bg: rgba(255, 77, 99, 0.12);
	--info: #6ea8ff;
	--info-bg: rgba(110, 168, 255, 0.12);
	--live: #ff1a34;

	--sidebar-w: 236px;
	--topbar-h: 62px;

	/* How wide the page is allowed to get.
	   A 1440p screen has ~2320px beside the sidebar and a 21:9 has well over
	   3000, so the old 1560 cap left half an ultrawide empty and a quarter of a
	   1440p unused — while the streams table, which has nine columns and two
	   UUIDs in it, was scrolling sideways at the same time. This fills 1440p
	   outright and centres on anything wider, so the leftover space is even
	   rather than piled against one edge.

	   Prose does not follow the container up: --prose-max keeps help text and
	   card notes at a readable measure however wide the page gets. */
	--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;
}

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

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

/* The UA sheet's `[hidden] { display: none }` loses to any author rule that
   sets display — .badge is inline-flex, so a hidden badge stayed visible and
   both halves of a two-state pair rendered at once. !important is the correct
   tool here: `hidden` is not a style choice, it is "this element is not
   currently relevant", and nothing should be able to override it. */
[hidden] {
	display: none !important;
}

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

/* Eyebrow label: the site's .section-title h6 treatment, used anywhere a small
   all-caps label sits above content. */
.eyebrow {
	font-size: 11px;
	font-weight: 800;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--text-faint);
}

/* --------------------------------------------------------------------------
   Shell
   -------------------------------------------------------------------------- */

.shell {
	display: grid;
	grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
	min-height: 100vh;
}

.sidebar {
	position: sticky;
	top: 0;
	height: 100vh;
	display: flex;
	flex-direction: column;
	background: var(--surface);
	border-right: 1px solid var(--border);
	overflow-y: auto;
}

.brand {
	display: flex;
	align-items: center;
	gap: 11px;
	height: var(--topbar-h);
	padding: 0 18px;
	border-bottom: 1px solid var(--border);
	flex-shrink: 0;
}

.brand:hover {
	text-decoration: none;
}

.brand-mark {
	width: 26px;
	height: 26px;
	flex-shrink: 0;
}

/* Wordmark: VR in the accent, CDN in the foreground colour, matching the
   logo. */
.brand-text {
	font-weight: 800;
	font-size: 16px;
	letter-spacing: 0.01em;
	color: var(--text);
	line-height: 1.1;
	display: block;
}

.brand-text .vr {
	color: var(--accent);
}

.brand-sub {
	display: block;
	font-size: 9px;
	text-transform: uppercase;
	letter-spacing: 2px;
	color: var(--text-faint);
	font-weight: 800;
}

.nav {
	padding: 14px 0;
	flex: 1;
}

.nav-group {
	margin-bottom: 16px;
}

.nav-label {
	font-size: 10px;
	font-weight: 800;
	letter-spacing: 2px;
	text-transform: uppercase;
	color: var(--text-faint);
	padding: 0 18px 6px;
}

.nav a {
	display: flex;
	align-items: center;
	gap: 11px;
	padding: 8px 18px;
	color: var(--text-muted);
	font-weight: 600;
	border-left: 3px solid transparent;
	transition: background 0.12s, color 0.12s, border-color 0.12s;
}

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

/* Active item: a solid accent edge and a wash, square — the site's block
   language rather than a rounded pill. */
.nav a.active {
	background: var(--accent-wash);
	border-left-color: var(--accent);
	color: var(--accent);
}

.nav-icon {
	width: 16px;
	height: 16px;
	flex-shrink: 0;
	opacity: 0.8;
}

.sidebar-foot {
	padding: 13px 18px;
	border-top: 1px solid var(--border);
	font-size: 11px;
	color: var(--text-faint);
	flex-shrink: 0;
}

/* Role chip in the sidebar footer, so an operator always knows what they are
   signed in as. */
.role-chip {
	display: inline-block;
	padding: 1px 7px;
	background: var(--surface-3);
	border: 1px solid var(--border-strong);
	color: var(--text-muted);
	font-size: 9px;
	font-weight: 800;
	letter-spacing: 1.5px;
	text-transform: uppercase;
}

.role-chip.is-admin {
	background: var(--accent-wash);
	border-color: var(--accent-line);
	color: var(--accent);
}

/* Main column ------------------------------------------------------------ */

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

.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);
}

/* Same max-width and padding as .content, so the title and the operator chip
   line up with the page rather than with the window edges. */
.topbar-inner {
	display: flex;
	align-items: center;
	gap: 16px;
	width: 100%;
	max-width: var(--content-max);
	margin-inline: auto;
	padding: 0 24px;
}

.topbar h1 {
	font-size: 17px;
	font-weight: 800;
}

.topbar-spacer {
	flex: 1;
}

.operator {
	display: flex;
	align-items: center;
	gap: 9px;
	font-size: 13px;
	font-weight: 600;
	color: var(--text-muted);
}

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

/* --------------------------------------------------------------------------
   Avatars — one of the few round things, as on the site
   -------------------------------------------------------------------------- */

.avatar {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 28px;
	height: 28px;
	border-radius: 50%;
	font-size: 11px;
	font-weight: 800;
	color: #08191a;
	flex-shrink: 0;
}

.avatar-lg {
	width: 54px;
	height: 54px;
	font-size: 19px;
}

/* --------------------------------------------------------------------------
   Cards and panels — square
   -------------------------------------------------------------------------- */

.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: 0.04em;
	text-transform: uppercase;
}

.card-head .spacer {
	flex: 1;
}

.card-body {
	padding: 16px;
}

.card-note {
	/* Right padding, not max-width: the strip's background and top border
	   should still span the whole card, but on a wide screen the sentence
	   inside it should not run the full 2000-odd pixels. */
	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);
}

.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(260px, 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;
}

/* --------------------------------------------------------------------------
   Stat tiles — a solid accent rule on top, no rounding
   -------------------------------------------------------------------------- */

.stat {
	background: var(--surface);
	border: 1px solid var(--border);
	border-top: 3px solid var(--accent);
	padding: 15px 16px;
}

.stat.is-quiet {
	border-top-color: var(--border-strong);
}

.stat.is-danger {
	border-top-color: var(--danger);
}

.stat.is-warn {
	border-top-color: var(--warn);
}

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

.stat-value {
	font-size: 28px;
	font-weight: 800;
	letter-spacing: -0.02em;
	line-height: 1.1;
	margin-top: 6px;
	font-variant-numeric: tabular-nums;
}

.stat-value a {
	color: inherit;
}

.stat-value a:hover {
	color: var(--accent);
	text-decoration: none;
}

.stat-meta {
	font-size: 12px;
	color: var(--text-muted);
	margin-top: 4px;
}

.stat-meta .up {
	color: var(--accent);
	font-weight: 700;
}

.stat-meta .down {
	color: var(--danger);
	font-weight: 700;
}

/* --------------------------------------------------------------------------
   Badges — square, like everything else
   -------------------------------------------------------------------------- */

.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: 0.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, 163, 51, 0.3);
}

.badge-danger {
	background: var(--danger-bg);
	color: var(--danger);
	border-color: rgba(255, 77, 99, 0.32);
}

.badge-info {
	background: var(--info-bg);
	color: var(--info);
	border-color: rgba(110, 168, 255, 0.3);
}

.badge-muted {
	background: var(--surface-3);
	color: var(--text-muted);
	border-color: var(--border-strong);
}

/* Live indicator: the one non-brand red, because "on air" has to read
   instantly and the accent is used for too much else. */
.live-dot {
	display: inline-block;
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--live);
	box-shadow: 0 0 0 3px rgba(255, 26, 52, 0.18);
	flex-shrink: 0;
}

.badge-live {
	background: rgba(255, 26, 52, 0.12);
	color: #ff7183;
	border-color: rgba(255, 26, 52, 0.32);
}

/* --------------------------------------------------------------------------
   Buttons — square, heavy, tall. The site's two shapes:
     .btn-primary  solid accent fill
     .btn / .btn-ghost   outline that fills on hover (its .btn-tos pattern)
   -------------------------------------------------------------------------- */

.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: 0.02em;
	cursor: pointer;
	white-space: nowrap;
	transition: background 0.12s, border-color 0.12s, color 0.12s;
}

.btn:hover {
	background: var(--surface-3);
	border-color: var(--text-faint);
	text-decoration: none;
}

.btn-primary {
	background: var(--accent);
	border-color: var(--accent);
	color: var(--accent-ink);
}

.btn-primary:hover {
	background: #5ff0f0;
	border-color: #5ff0f0;
	color: var(--accent-ink);
}

/* Outline accent: transparent with an accent edge, filling on hover. */
.btn-ghost {
	border-color: var(--border);
	color: var(--text-muted);
}

.btn-ghost:hover {
	border-color: var(--accent);
	background: transparent;
	color: var(--accent);
}

.btn-accent {
	border-color: var(--accent);
	color: var(--accent);
}

.btn-accent:hover {
	background: var(--accent);
	color: var(--accent-ink);
	border-color: var(--accent);
}

.btn-danger {
	border-color: rgba(255, 77, 99, 0.5);
	color: var(--danger);
}

.btn-danger:hover {
	background: var(--danger);
	border-color: var(--danger);
	color: #fff;
}

.btn-warn {
	border-color: rgba(255, 163, 51, 0.5);
	color: var(--warn);
}

.btn-warn:hover {
	background: var(--warn);
	border-color: var(--warn);
	color: #241503;
}

.btn-sm {
	padding: 3px 11px;
	font-size: 12px;
	border-width: 1px;
}

.btn-lg {
	padding: 14px 34px;
	font-size: 14px;
	font-weight: 800;
}

.btn:disabled {
	opacity: 0.4;
	cursor: not-allowed;
}

.btn:disabled:hover {
	background: transparent;
	border-color: var(--border-strong);
	color: var(--text);
}

.btn-group {
	display: inline-flex;
	gap: 7px;
	flex-wrap: wrap;
}

/* --------------------------------------------------------------------------
   Forms — square fields
   -------------------------------------------------------------------------- */

label {
	display: block;
	font-size: 11px;
	font-weight: 800;
	letter-spacing: 1px;
	text-transform: uppercase;
	color: var(--text-faint);
	margin-bottom: 6px;
}

input[type="text"],
input[type="search"],
input[type="password"],
input[type="number"],
input[type="datetime-local"],
input[type="date"],
select,
textarea {
	width: 100%;
	padding: 8px 11px;
	background: var(--bg);
	border: 1px solid var(--border-strong);
	border-radius: 0;
	color: var(--text);
	font-family: inherit;
	font-size: 13px;
	transition: border-color 0.12s;
}

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

input:focus,
select:focus,
textarea:focus {
	outline: none;
	border-color: var(--accent);
}

textarea {
	resize: vertical;
	min-height: 70px;
	line-height: 1.5;
}

select {
	appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2397a2a8' d='M6 8.5 2 4.5h8z'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 10px center;
	padding-right: 28px;
}

.field {
	margin-bottom: 14px;
}

.field:last-child {
	margin-bottom: 0;
}

.field-hint {
	font-size: 11px;
	color: var(--text-faint);
	margin-top: 5px;
	text-transform: none;
	letter-spacing: 0;
	font-weight: 400;
}

.field-row {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
	gap: 14px;
}

.check {
	display: flex;
	align-items: center;
	gap: 8px;
	font-size: 13px;
	color: var(--text);
	font-weight: 600;
	text-transform: none;
	letter-spacing: 0;
	margin: 0;
	cursor: pointer;
}

.check input[type="checkbox"] {
	width: 15px;
	height: 15px;
	accent-color: var(--accent);
	cursor: pointer;
	margin: 0;
}

/* Filter bar ------------------------------------------------------------- */

.filters {
	display: flex;
	align-items: flex-end;
	gap: 10px;
	flex-wrap: wrap;
	padding: 14px 16px;
	background: var(--surface);
	border: 1px solid var(--border);
	margin-bottom: 14px;
}

.filters .field {
	margin: 0;
	min-width: 150px;
}

.filters .field.grow {
	flex: 1;
	min-width: 220px;
}

/* Chips: square tabs, with a solid accent underline when active. */
.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: 0.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);
}

.chip-count {
	font-variant-numeric: tabular-nums;
	opacity: 0.7;
	font-weight: 600;
}

/* Chips used as plain links inside a card body, not as tabs. */
.chip-row {
	border-bottom: none;
	gap: 8px;
	margin: 0;
}

.chip-row .chip {
	border: 1px solid var(--border);
	border-bottom-width: 1px;
	padding: 5px 12px;
	margin: 0;
}

.chip-row .chip:hover {
	border-color: var(--accent);
	color: var(--accent);
	background: transparent;
}

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

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

td.actions {
	text-align: right;
	white-space: nowrap;
}

td.actions form {
	display: inline;
}

.cell-user {
	display: flex;
	align-items: center;
	gap: 9px;
	min-width: 0;
}

/* A marker sitting beside a name must not wrap away from it, so this row does
   not wrap and lets the name truncate instead. */
.cell-user-row {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-wrap: nowrap;
	min-width: 0;
}

/* The name and any marker beside it are one unbreakable run, so the dot can
   never end up on a different line from the name it describes. The id that
   follows is free to wrap, which is what gives these cells two lines.
   (The old overflow/ellipsis here did nothing: an inline span cannot clip.) */
.cell-user-name {
	font-weight: 700;
	color: var(--text);
	white-space: nowrap;
}

.cell-user-name .live-dot {
	margin-left: 7px;
	vertical-align: 0.05em;
}

.cell-sub {
	font-size: 11px;
	color: var(--text-faint);
}

.trunc {
	max-width: 280px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	display: inline-block;
	vertical-align: bottom;
}

.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;
}

/* --------------------------------------------------------------------------
   Pagination
   -------------------------------------------------------------------------- */

.pager {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 12px 16px;
	border-top: 1px solid var(--border);
	font-size: 12px;
	color: var(--text-muted);
	flex-wrap: wrap;
}

.pager .spacer {
	flex: 1;
}

/* --------------------------------------------------------------------------
   Flash messages — a solid tone bar on the left edge
   -------------------------------------------------------------------------- */

.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-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, 163, 51, 0.32);
	border-left-color: var(--warn);
	color: #ffcd93;
}

.flash-danger {
	background: var(--danger-bg);
	border-color: rgba(255, 77, 99, 0.34);
	border-left-color: var(--danger);
	color: #ffa8b2;
}

.flash-info {
	background: var(--info-bg);
	border-color: rgba(110, 168, 255, 0.32);
	border-left-color: var(--info);
	color: #bcd6ff;
}

/* --------------------------------------------------------------------------
   Charts (server-rendered SVG)
   -------------------------------------------------------------------------- */

.chart {
	width: 100%;
	height: auto;
	display: block;
	overflow: visible;
}

.chart-line {
	fill: none;
	stroke: var(--accent);
	stroke-width: 2;
	stroke-linejoin: round;
	stroke-linecap: round;
}

.chart-area {
	fill: url(#accentFade);
	stroke: none;
}

.chart-bars {
	fill: var(--accent);
	opacity: 0.85;
}

.chart-axis {
	display: flex;
	justify-content: space-between;
	font-size: 11px;
	color: var(--text-faint);
	margin-top: 7px;
	font-variant-numeric: tabular-nums;
}

/* Meters: square bars */
/* An <svg> so the fill width can be a presentation attribute rather than an
   inline style, which the CSP blocks. */
.meter {
	display: block;
	width: 100%;
	height: 6px;
}

.meter-track {
	fill: var(--surface-3);
}

.meter-fill {
	fill: var(--accent);
}

.meter-fill.is-warn {
	fill: var(--warn);
}

.meter-fill.is-danger {
	fill: var(--danger);
}

.meter-row {
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto;
	gap: 10px;
	align-items: center;
	padding: 8px 0;
}

.meter-row + .meter-row {
	border-top: 1px solid var(--border);
}

.meter-name {
	font-size: 13px;
	font-weight: 600;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.meter-val {
	font-size: 12px;
	color: var(--text-muted);
	font-variant-numeric: tabular-nums;
	font-weight: 700;
}

/* --------------------------------------------------------------------------
   Detail pages
   -------------------------------------------------------------------------- */

.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;
}

/* The site trails its page headings with a small accent dot. */
.page-head h1::after {
	content: "";
	width: 8px;
	height: 8px;
	display: inline-block;
	background: var(--accent);
	border-radius: 50%;
	margin-left: 9px;
	vertical-align: 0.15em;
}

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

.page-head .spacer {
	flex: 1;
}

.detail-grid {
	display: grid;
	grid-template-columns: minmax(0, 2fr) minmax(300px, 1fr);
	gap: 14px;
	align-items: start;
}

@media (max-width: 1100px) {
	.detail-grid {
		grid-template-columns: minmax(0, 1fr);
	}
}

dl.props {
	margin: 0;
	display: grid;
	grid-template-columns: minmax(120px, auto) minmax(0, 1fr);
}

dl.props dt {
	padding: 8px 0;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.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;
}

.secret {
	font-family: var(--mono);
	font-size: 12px;
	background: var(--bg);
	border: 1px solid var(--border);
	padding: 3px 8px;
	display: inline-block;
	overflow-wrap: anywhere;
	color: var(--text-muted);
}

/* Withheld values read as deliberately absent, not as missing data. */
.withheld {
	font-family: var(--mono);
	font-size: 12px;
	color: var(--text-faint);
	letter-spacing: 2px;
}

details.reveal summary {
	cursor: pointer;
	font-size: 11px;
	font-weight: 800;
	letter-spacing: 1px;
	text-transform: uppercase;
	color: var(--accent);
	list-style: none;
	user-select: none;
}

details.reveal summary::-webkit-details-marker {
	display: none;
}

details.reveal summary::before {
	content: "Show";
}

details.reveal[open] summary::before {
	content: "Hide";
}

details.reveal[open] summary {
	margin-bottom: 6px;
}

.key-row {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 12px 0;
	border-bottom: 1px solid var(--border);
	flex-wrap: wrap;
}

.key-row:last-child {
	border-bottom: none;
}

.key-main {
	flex: 1;
	min-width: 220px;
}

.ext-list {
	margin: 9px 0 0;
	padding: 0 0 0 14px;
	border-left: 2px solid var(--border);
	list-style: none;
}

.ext-list li {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 5px 0;
	font-size: 12px;
	flex-wrap: wrap;
}

/* A bordered block used for standalone notices inside a card. */
.notice {
	padding: 11px 13px;
	border: 1px solid var(--border-strong);
	border-left: 4px solid var(--text-faint);
	background: var(--surface-2);
	font-size: 12px;
	color: var(--text-muted);
}

.notice.is-accent {
	border-color: var(--accent-line);
	border-left-color: var(--accent);
	background: var(--accent-wash);
}

/* Something the operator should act on, e.g. an ingest problem. */
.notice.is-warn {
	border-left-color: var(--warn);
	color: var(--text);
}

/* --------------------------------------------------------------------------
   Login
   -------------------------------------------------------------------------- */

.login-shell {
	min-height: 100vh;
	display: grid;
	place-items: center;
	padding: 24px;
	background:
		radial-gradient(900px 480px at 50% -10%, rgba(59, 226, 226, 0.08), transparent 70%),
		var(--bg);
}

.login-card {
	width: 100%;
	max-width: 390px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-top: 3px solid var(--accent);
	padding: 32px 30px;
}

.login-brand {
	display: flex;
	align-items: center;
	gap: 11px;
	justify-content: center;
	margin-bottom: 20px;
}

.login-card h1 {
	font-size: 17px;
	font-weight: 800;
	text-align: center;
	margin-bottom: 4px;
}

.login-card .sub {
	text-align: center;
	color: var(--text-faint);
	font-size: 12px;
	margin-bottom: 22px;
}

.login-card .btn {
	width: 100%;
	margin-top: 8px;
}

.login-note {
	margin-top: 16px;
	margin-bottom: 0;
}

/* Single sign-on: a distinct block inside the login card, not just a ruled-off
   run of buttons. The inset background and border make it read as a separate
   way in rather than a continuation of the password form.

   The negative side margins let the block span the card's full width, so the
   grouping is unmistakable at a glance. */
.sso {
	margin: 30px -30px 0;
	padding: 18px 30px 20px;
	background: var(--bg);
	border-top: 1px solid var(--border);
	border-bottom: 1px solid var(--border);
}

/* With no local form above it there is nothing to separate from, so it stops
   being a distinct block and is simply the content. */
.sso.is-only {
	margin: 0 0 4px;
	padding: 0;
	background: none;
	border: none;
}

.sso-title {
	margin-bottom: 12px;
	text-align: center;
}

.sso .sso-title {
	color: var(--text-muted);
}

.sso-buttons {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

/* Real buttons: the same square, heavy shape as every other action, laid out
   icon / label / role rather than centred. */
.btn-sso {
	width: 100%;
	justify-content: flex-start;
	gap: 11px;
	padding: 11px 14px;
	/* One step lighter than the block behind it, so the buttons sit on the
	   inset panel rather than disappearing into it. */
	background: var(--surface-2);
	border-color: var(--border-strong);
}

.btn-sso:hover {
	background: var(--surface-3);
	border-color: var(--accent);
	color: var(--text);
}

.provider-icon {
	width: 18px;
	height: 18px;
	flex-shrink: 0;
}

.sso-label {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* The role each provider grants, so it is obvious which door you are using. */
.sso-role {
	margin-left: auto;
	font-size: 9px;
	font-weight: 800;
	letter-spacing: 1.5px;
	text-transform: uppercase;
	color: var(--text-faint);
	border: 1px solid var(--border-strong);
	padding: 1px 6px;
	flex-shrink: 0;
}

.btn-sso:hover .sso-role {
	border-color: var(--accent);
	color: var(--accent);
}

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

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

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

.small {
	font-size: 12px;
}

.tiny {
	font-size: 11px;
}

.nowrap {
	white-space: nowrap;
}

.strong {
	font-weight: 700;
}

.right {
	text-align: right;
}

.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;
}

.inline-form {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	flex-wrap: wrap;
}

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

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

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

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 900px) {
	.shell {
		grid-template-columns: minmax(0, 1fr);
	}

	.sidebar {
		position: static;
		height: auto;
		border-right: none;
		border-bottom: 1px solid var(--border);
	}

	.nav {
		display: flex;
		flex-wrap: wrap;
		gap: 2px;
		padding: 10px;
	}

	.nav-group {
		margin: 0;
		display: contents;
	}

	.nav a {
		border-left: none;
		border-bottom: 3px solid transparent;
		padding: 7px 12px;
	}

	.nav a.active {
		border-bottom-color: var(--accent);
	}

	.nav-label,
	.sidebar-foot {
		display: none;
	}

	.content,
	.topbar-inner {
		padding-left: 14px;
		padding-right: 14px;
	}
}

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

/* --------------------------------------------------------------------------
   Live updates

   The pill says whether what is on screen is being kept current. A dashboard
   that has silently stopped receiving updates looks identical to one where
   nothing is happening, and those are very different things to a person
   deciding whether to act.
   -------------------------------------------------------------------------- */

.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="expired"] {
	color: var(--danger);
	border-color: var(--danger);
}

@keyframes live-pulse {
	0%, 100% { opacity: 1; }
	50% { opacity: 0.25; }
}

/* A value that just changed. Brief and low-contrast on purpose: the point is
   to catch the eye of someone already looking at the number, not to make a
   busy streams page flicker. */
.live-changed {
	animation: live-flash 1.2s ease-out;
}

@keyframes live-flash {
	0% { background: var(--accent-wash); }
	100% { background: transparent; }
}

/* A row whose stream has ended since the page loaded. Dimmed rather than
   removed: deleting a row under the cursor of someone about to click
   Disconnect is worse than showing them a stale one that says so. */
.live-ended {
	opacity: 0.45;
}

.live-ended td {
	text-decoration: line-through;
	text-decoration-color: var(--text-faint);
}

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

/* --------------------------------------------------------------------------
   Avatar palette

   colorFor() picks one of these by hashing the name, so an account keeps the
   same tint everywhere. It is a class rather than a computed `hsl()` in a style
   attribute because the CSP forbids inline styles: the old version was silently
   blocked, leaving every avatar unpainted.

   Sixteen hues, evenly spaced, at the saturation and lightness the previous
   generated colour used.
   -------------------------------------------------------------------------- */

.avatar-c0 { background: hsl(0 62% 58%); }
.avatar-c1 { background: hsl(22 62% 58%); }
.avatar-c2 { background: hsl(45 62% 58%); }
.avatar-c3 { background: hsl(68 62% 58%); }
.avatar-c4 { background: hsl(90 62% 58%); }
.avatar-c5 { background: hsl(112 62% 58%); }
.avatar-c6 { background: hsl(135 62% 58%); }
.avatar-c7 { background: hsl(158 62% 58%); }
.avatar-c8 { background: hsl(180 62% 58%); }
.avatar-c9 { background: hsl(202 62% 58%); }
.avatar-c10 { background: hsl(225 62% 58%); }
.avatar-c11 { background: hsl(248 62% 58%); }
.avatar-c12 { background: hsl(270 62% 58%); }
.avatar-c13 { background: hsl(292 62% 58%); }
.avatar-c14 { background: hsl(315 62% 58%); }
.avatar-c15 { background: hsl(338 62% 58%); }

/* For a row with no account behind it: deleted users, broadcasts. */
.avatar-muted {
	background: var(--surface-3);
	color: var(--text-faint);
}

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

   These exist because the CSP forbids inline styles, so the handful of
   one-off layout tweaks the templates used to carry as style attributes need
   somewhere to live. Each is exactly what the attribute it replaced said —
   they are not a design system, and new rules belong in a real component
   class rather than here.
   -------------------------------------------------------------------------- */

.u-minw0 { min-width: 0; }
.u-m0 { margin: 0; }
.u-mb2 { margin-bottom: 2px; }
.u-mb18 { margin-bottom: 18px; }
.u-mt4 { margin-top: 4px; }
.u-between { justify-content: space-between; }
.u-center { text-align: center; }
.u-left { text-align: left; }
.u-flex1 { flex: 1; }
.u-gap7 { gap: 7px; }
.u-gap8 { gap: 8px; }
.u-gap10 { gap: 10px; }
.u-h33 { height: 33px; }
.u-fs18 { font-size: 18px; }
.u-fs34 { font-size: 34px; letter-spacing: -0.03em; }
.u-w150 { width: 150px; }
.u-w-auto-110 { width: auto; min-width: 110px; }
.u-w-auto-130 { width: auto; min-width: 130px; }
/* Wrap rather than overflow or truncate. For values that have to be readable in
   full — URLs, keys, anything meant to be copied — where an ellipsis would make
   the value useless. */
.u-break { overflow-wrap: anywhere; }

.u-minw120 { min-width: 120px; }
.u-maxw110 { max-width: 110px; }
.u-bt { border-top: 1px solid var(--border); }
.u-pre { display: block; white-space: pre-wrap; }

.u-listrow {
	padding: 7px 0;
	border-bottom: 1px solid var(--border);
}

.u-listrow-tight {
	padding: 5px 0;
	border-bottom: 1px solid var(--border);
}

/* --------------------------------------------------------------------------
   Permission editor

   A row is a whole clickable label: the checkbox alone is a small target, and
   the help text is the part that actually tells someone what they are granting.
   -------------------------------------------------------------------------- */

.perm-row {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr) auto;
	gap: 12px;
	align-items: start;
	padding: 11px 0;
	border-bottom: 1px solid var(--border);
	cursor: pointer;
}

.perm-row:last-child {
	border-bottom: 0;
}

.perm-row:hover {
	background: var(--surface-2);
}

.perm-row input[type="checkbox"] {
	margin-top: 3px;
	width: 15px;
	height: 15px;
	accent-color: var(--accent);
	flex: none;
}

/* Locked rows are the two invariants the server enforces. Shown rather than
   hidden, because "why can support not have this" is a fair question and the
   reason line answers it. */
.perm-row.is-locked {
	cursor: not-allowed;
	opacity: 0.72;
}

.perm-main {
	display: flex;
	flex-direction: column;
	gap: 3px;
	min-width: 0;
}

.perm-label {
	display: flex;
	align-items: center;
	gap: 7px;
	font-weight: 700;
	flex-wrap: wrap;
}

.perm-help {
	font-size: 12px;
	color: var(--text-muted);
	line-height: 1.45;
	/* The help text is the whole point of the catalogue, and it is the longest
	   prose in the dashboard. It stops widening with the page: a permission is
	   granted by reading this sentence, and a 2000px line does not get read. */
	max-width: var(--prose-max);
}

.perm-locked {
	font-size: 11px;
	color: var(--warn);
}

.perm-key {
	font-size: 11px;
	color: var(--text-faint);
	white-space: nowrap;
}

@media (max-width: 720px) {
	.perm-key {
		display: none;
	}
}

/* --------------------------------------------------------------------------
   Health findings

   Ranked, with the diagnosis in bold and what it means underneath. The detail
   is written to be read out to a streamer as-is, so it wants to be legible
   rather than small.
   -------------------------------------------------------------------------- */

.findings {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.finding {
	display: flex;
	flex-direction: column;
	gap: 3px;
	padding: 11px 13px;
	background: var(--surface-2);
	border: 1px solid var(--border-strong);
	border-left: 4px solid var(--text-faint);
}

.finding.is-warn {
	border-left-color: var(--warn);
}

.finding.is-fail {
	border-left-color: var(--danger);
	background: var(--danger-bg);
}

.finding-title {
	font-size: 13px;
	font-weight: 700;
}

.finding.is-fail .finding-title {
	color: var(--danger);
}

.finding.is-warn .finding-title {
	color: var(--warn);
}

.finding-detail {
	font-size: 12px;
	color: var(--text-muted);
	line-height: 1.5;
}

/* --------------------------------------------------------------------------
   Network map

   Server-rendered SVG, like the charts: the geometry is computed in Go and the
   colours come from here, so nothing needs an inline style the CSP would
   block.
   -------------------------------------------------------------------------- */

.netmap {
	display: block;
	min-width: 100%;
}

.netmap-region {
	fill: var(--text-faint);
	font-size: 10px;
	font-weight: 800;
	letter-spacing: 1.5px;
	text-transform: uppercase;
}

.netmap-link {
	fill: none;
	stroke: var(--border-strong);
	stroke-width: 1;
	/* Square joins on the elbows; a round join leaves a visible nick at the
	   corner at this stroke width. */
	stroke-linejoin: miter;
	stroke-linecap: square;
}

/* A leg of the path whose far end is also carrying the stream. */
.netmap-link.is-on {
	stroke: var(--accent);
	stroke-width: 1.5;
	opacity: 0.55;
}

.netmap-node circle {
	fill: var(--surface-3);
	stroke: var(--border-strong);
	stroke-width: 2;
}

.netmap-node.is-on circle {
	fill: var(--accent-wash);
	stroke: var(--accent);
}

/* Carrying the stream but absent from the Server table — worth a second look
   either way round. */
.netmap-node.is-unknown circle {
	stroke: var(--warn);
	stroke-dasharray: 3 2;
}

.netmap-name {
	fill: var(--text-faint);
	font-size: 9px;
	font-family: var(--mono);
}

/* Row heading in the left gutter. */
.netmap-tier {
	fill: var(--text-faint);
	font-size: 9px;
	font-weight: 800;
	letter-spacing: 1.5px;
	text-transform: uppercase;
}

.netmap-node.is-on .netmap-name {
	fill: var(--text-muted);
}

.netmap-legend {
	display: flex;
	flex-wrap: wrap;
	gap: 16px;
	font-size: 11px;
	color: var(--text-muted);
}

.netmap-legend span {
	display: inline-flex;
	align-items: center;
	gap: 6px;
}

.netmap-key {
	width: 11px;
	height: 11px;
	border-radius: 50%;
	background: var(--surface-3);
	border: 2px solid var(--border-strong);
	flex: none;
}

.netmap-key.is-on {
	background: var(--accent-wash);
	border-color: var(--accent);
}

.netmap-key.is-unknown {
	border-color: var(--warn);
	border-style: dashed;
}

/* Something unusual rather than something wrong: shown, but never leading. */
.finding.is-info {
	border-left-color: var(--info);
}

.finding.is-info .finding-title {
	color: var(--info);
}
