/* ── Theme tokens ──────────────────────────────────────────────────────────
   Dark is the default. An explicit data-theme on <html> wins; otherwise the
   OS preference decides via the prefers-color-scheme block below. All colors
   are referenced through these vars so a theme swap needs no other CSS. */

/* The HTML `hidden` attribute must always win. A component that sets an explicit
   `display` (grid/flex/etc.) would otherwise override the browser default of
   `[hidden] { display: none }`, leaving "hidden" elements visible. This one rule
   guarantees the attribute is honored everywhere; `!important` is warranted
   because it must beat any component `display`. */
[hidden] { display: none !important; }

:root {
	color-scheme: dark;
	--bg: #0e1116;
	--panel: #161b22;
	--panel-2: #1c232d;
	--border: #2a333f;
	--text: #e6edf3;
	--muted: #8b949e;
	--accent: #4c8dff;
	--accent-contrast: #ffffff;
	--accent-dim: rgba(76, 141, 255, 0.15);
	--ok: #3fb950;
	--warn: #d29922;
	--err: #f85149;
	--shadow: rgba(0, 0, 0, 0.45);
	--radius: 10px;
}

/* Light palette — applied when the user (or OS) asks for light. */
:root[data-theme="light"] {
	color-scheme: light;
	--bg: #f6f8fa;
	--panel: #ffffff;
	--panel-2: #eef1f5;
	--border: #d5dbe2;
	--text: #1a1f26;
	--muted: #5b6672;
	--accent: #2f6fed;
	--accent-contrast: #ffffff;
	--accent-dim: rgba(47, 111, 237, 0.15);
	--ok: #1a7f37;
	--warn: #9a6700;
	--err: #cf222e;
	--shadow: rgba(20, 30, 45, 0.12);
}

/* Follow the OS when no explicit choice is stored on <html>. */
@media (prefers-color-scheme: light) {
	:root:not([data-theme="dark"]) {
		color-scheme: light;
		--bg: #f6f8fa;
		--panel: #ffffff;
		--panel-2: #eef1f5;
		--border: #d5dbe2;
		--text: #1a1f26;
		--muted: #5b6672;
		--accent: #2f6fed;
		--accent-contrast: #ffffff;
		--accent-dim: rgba(47, 111, 237, 0.15);
		--ok: #1a7f37;
		--warn: #9a6700;
		--err: #cf222e;
		--shadow: rgba(20, 30, 45, 0.12);
	}
}

* { box-sizing: border-box; }

/* Keyboard focus ring for any interactive element that doesn't define its own.
   Scoped to :focus-visible so it appears for keyboard navigation but not on
   mouse clicks; components with a bespoke focus style still override this. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

body {
	margin: 0;
	background: var(--bg);
	color: var(--text);
	font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

.wordmark {
	display: inline-flex;
	align-items: center;
	gap: 0.5em;
	font-weight: 700;
	letter-spacing: -0.02em;
	margin: 0;
	font-size: 1.25rem;
	color: var(--text);
	text-decoration: none;
}
/* Brand mark, reusing the generated favicon.svg as the single source (no inline
   copy to drift). A gentle turn on hover — skipped for reduced-motion. */
.wordmark::before {
	content: "";
	flex: none;
	width: 1.2em;
	height: 1.2em;
	background: url("/img/favicon.svg") center / contain no-repeat;
	transition: transform 0.5s ease;
}
.wordmark:hover { text-decoration: none; }
.wordmark:hover::before { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) {
	.wordmark::before { transition: none; }
	.wordmark:hover::before { transform: none; }
}

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

button, .btn {
	font: inherit;
	cursor: pointer;
	display: inline-block;
	border: 1px solid var(--border);
	background: var(--accent);
	color: var(--accent-contrast);
	padding: 0.5rem 1rem;
	border-radius: var(--radius);
	text-decoration: none;
	line-height: 1.2;
}
button.ghost, .btn.ghost, a.ghost {
	background: transparent;
	color: var(--text);
}
button:hover, .btn:hover { filter: brightness(1.08); text-decoration: none; }

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

/* ── Auth ── */
.auth {
	display: grid;
	place-items: center;
	min-height: 100vh;
}
.auth-card {
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	padding: 2rem;
	width: min(92vw, 360px);
	text-align: center;
}
.auth-card form { display: grid; gap: 0.75rem; margin-top: 1rem; text-align: left; }
.auth-card label { font-size: 0.85rem; color: var(--muted); }
.auth-card input {
	padding: 0.6rem 0.75rem;
	border-radius: var(--radius);
	border: 1px solid var(--border);
	background: var(--panel-2);
	color: var(--text);
}

/* ── Topbar ── */
.topbar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 1rem 1.5rem;
	border-bottom: 1px solid var(--border);
}
.topbar-actions { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; justify-content: flex-end; }
.theme-toggle { font-size: 1rem; line-height: 1; padding: 0.5rem 0.7rem; }

/* ── Search ── */
.topbar-search {
	flex: 1 1 auto;
	max-width: 460px;
	margin: 0 1rem;
}
.search-input {
	width: 100%;
	padding: 0.55rem 0.9rem;
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: 999px;
	color: var(--text);
	font-size: 0.95rem;
	outline: none;
	transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.search-input::placeholder { color: var(--muted); }
.search-input:focus {
	border-color: var(--accent);
	box-shadow: 0 0 0 3px var(--accent-dim);
}

.search-results { margin-top: 0.5rem; }
.search-results-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	margin-bottom: 1rem;
}
.search-summary { font-size: 0.9rem; }
.search-clear { font-size: 0.85rem; }

.search-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.75rem; }
.search-item { margin: 0; }
.search-link {
	display: flex;
	gap: 0.9rem;
	padding: 0.6rem;
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--surface, transparent);
	text-decoration: none;
	color: var(--text);
	transition: border-color 0.15s ease, background 0.15s ease;
}
.search-link:hover { border-color: var(--accent); text-decoration: none; }

.search-thumb {
	flex: 0 0 auto;
	width: 140px;
	aspect-ratio: 16 / 9;
	border-radius: 8px;
	overflow: hidden;
	background: var(--bg);
	display: flex;
	align-items: center;
	justify-content: center;
}
.search-thumb-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.search-thumb-glyph { color: var(--muted); font-size: 1.4rem; }

.search-body { display: flex; flex-direction: column; gap: 0.35rem; min-width: 0; }
.search-title { font-weight: 600; font-size: 1rem; line-height: 1.25; }
.search-snippet {
	display: flex;
	gap: 0.5rem;
	align-items: baseline;
	font-size: 0.9rem;
	color: var(--muted);
	line-height: 1.4;
}
.search-stamp {
	flex: 0 0 auto;
	font-variant-numeric: tabular-nums;
	color: var(--accent);
	font-weight: 600;
	background: var(--accent-dim);
	padding: 0.05rem 0.4rem;
	border-radius: 5px;
	font-size: 0.8rem;
}
.search-snippet-text mark {
	background: var(--accent-dim);
	color: var(--text);
	border-radius: 3px;
	padding: 0 0.1rem;
}
.search-where { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; }
.search-empty { padding: 2rem 0; text-align: center; }

@media (max-width: 640px) {
	.topbar-search { order: 3; flex-basis: 100%; margin: 0.6rem 0 0; max-width: none; }
	.topbar { flex-wrap: wrap; }
	.search-thumb { width: 96px; }
}

.wrap { max-width: 900px; margin: 0 auto; padding: 1.5rem; }

/* ── Uploader ── */
.uploader { margin-bottom: 2rem; }
.drop {
	display: grid;
	gap: 0.35rem;
	place-items: center;
	text-align: center;
	padding: 2.5rem 1rem;
	border: 1.5px dashed var(--border);
	border-radius: var(--radius);
	background: var(--panel);
	cursor: pointer;
	transition: border-color 0.15s, background 0.15s;
}
.drop.over { border-color: var(--accent); background: var(--panel-2); }
.upload-status { margin-top: 0.75rem; color: var(--muted); font-size: 0.9rem; }

/* ── Public video grid (YouTube-style) ── */
.wrap { max-width: 1200px; }
.video-grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
	gap: 1.25rem 1rem;
}
.video-card { display: flex; flex-direction: column; }

/* The poster is the hover-preview surface: a 16:9 image with a muted looping
   <video> that fades in over it on hover. */
.poster {
	position: relative;
	display: block;
	aspect-ratio: 16 / 9;
	border-radius: var(--radius);
	overflow: hidden;
	background: #000;
	text-decoration: none;
}
.poster-img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
.poster-glyph {
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
	color: var(--accent);
	font-size: 2rem;
	background: var(--panel-2);
}
.poster-glyph.small { font-size: 1.1rem; }
.poster .preview {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	opacity: 0;
	transition: opacity 0.2s ease;
	background: #000;
	pointer-events: none;
}
.poster.previewing .preview { opacity: 1; }
.card-meta { padding: 0.6rem 0.15rem 0; }
.card-title {
	display: block;
	font-weight: 600;
	line-height: 1.35;
	overflow: hidden;
	text-overflow: ellipsis;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
}

.empty-state { text-align: center; padding: 4rem 1rem; }

/* The home main holds the absolutely-positioned pull-to-refresh indicator, so it
   needs to be a positioning context. */
main.home-main { position: relative; }

/* Infinite scroll affordances */
.grid-sentinel { height: 1px; }
.grid-loading, .grid-end { text-align: center; padding: 1.5rem 1rem; font-size: 0.9rem; }

/* Pull-to-refresh indicator (touch). Sits above the grid and is revealed by a
   downward drag when scrolled to the top; JS drives its transform. */
.ptr-indicator {
	position: absolute;
	top: -3rem;
	left: 50%;
	transform: translateX(-50%);
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.4rem 0.9rem;
	border-radius: 999px;
	background: var(--panel);
	border: 1px solid var(--border);
	color: var(--muted);
	font-size: 0.85rem;
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.15s ease;
	z-index: 5;
}
.ptr-indicator.active { opacity: 1; }
.ptr-indicator.armed { color: var(--text); border-color: var(--accent, var(--text)); }
.ptr-spinner {
	width: 0.9rem;
	height: 0.9rem;
	border: 2px solid var(--border);
	border-top-color: var(--text);
	border-radius: 50%;
	display: inline-block;
}
.ptr-indicator.refreshing .ptr-spinner { animation: ptr-spin 0.7s linear infinite; }
@keyframes ptr-spin { to { transform: rotate(360deg); } }
.empty-title { font-size: 1.2rem; font-weight: 600; margin-bottom: 0.35rem; }

/* ── Admin ── */
.wrap { max-width: 1200px; }
/* Admin shell: fixed sidebar nav + scrolling content. Replaces the old flat tab
   row, which stretched edge-to-edge once the section count grew past ~10. */
.admin-shell {
	display: grid;
	grid-template-columns: 232px minmax(0, 1fr);
	gap: 1.75rem;
	align-items: start;
}
.admin-content { min-width: 0; }

.admin-tabs {
	position: sticky;
	top: 1.25rem;
	display: flex;
	flex-direction: column;
	gap: 1.1rem;
	max-height: calc(100vh - 2.5rem);
	overflow-y: auto;
	padding-right: 0.25rem;
}
.tab-group { display: flex; flex-direction: column; gap: 0.12rem; }
.tab-group-label {
	font-size: 0.68rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--muted);
	padding: 0 0.65rem 0.3rem;
}
.tab-group-foot { margin-top: auto; padding-top: 0.6rem; border-top: 1px solid var(--border); }

.tab {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	width: 100%;
	text-align: left;
	background: transparent;
	border: none;
	border-left: 2px solid transparent;
	color: var(--muted);
	padding: 0.5rem 0.65rem;
	border-radius: 7px;
	font-weight: 500;
	font-size: 0.92rem;
	cursor: pointer;
	transition: background 0.12s ease, color 0.12s ease;
}
.tab:hover { background: var(--panel-2); color: var(--text); }
.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tab .tab-ic { width: 18px; height: 18px; flex: 0 0 auto; opacity: 0.85; }
.tab .tab-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tab.active {
	color: var(--text);
	background: var(--accent-dim);
	border-left-color: var(--accent);
	font-weight: 600;
	border-radius: 0 7px 7px 0;
}
.tab.active .tab-ic { opacity: 1; color: var(--accent); }

/* Mobile nav toggle + backdrop (hidden on desktop). */
.admin-nav-toggle { display: none; }
.admin-nav-backdrop { display: none; }
.section-heading { font-size: 1rem; color: var(--muted); font-weight: 600; margin: 1.5rem 0 0.75rem; }
.user-tag { color: var(--muted); font-size: 0.9rem; }

.admin-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
.admin-item {
	display: grid;
	grid-template-columns: 140px 1fr auto;
	align-items: start;
	gap: 1rem;
	background: var(--panel);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	padding: 0.7rem 0.9rem;
}
.admin-thumb {
	position: relative;
	width: 140px;
	aspect-ratio: 16 / 9;
	border-radius: 8px;
	overflow: hidden;
	background: var(--panel-2);
}
.admin-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.admin-meta { display: grid; gap: 0.4rem; min-width: 0; }
.title-input {
	font: inherit;
	font-weight: 600;
	background: transparent;
	border: 1px solid transparent;
	border-radius: 6px;
	padding: 0.3rem 0.4rem;
	color: var(--text);
	width: 100%;
}
.title-input:hover { border-color: var(--border); }
.title-input:focus { outline: none; border-color: var(--accent); background: var(--panel-2); }
.title-input.saved { border-color: var(--ok); }
.admin-sub { display: flex; align-items: center; gap: 1rem; font-size: 0.85rem; }
/* Visibility control: a SINGLE button showing the current state. Its label + color
   ARE the state — green "Public" = live/visible, grey "Private" = hidden — and
   clicking it flips to the other (with a confirm). !important defeats the global
   `button{background:var(--accent)}` rule that would otherwise paint it blue. */
/* Three-state visibility control (Private / Unlisted / Public). The wrapper
   carries a vis-<state> class so the select is color-coded at a glance:
   private = muted, unlisted = amber/warn (link-only), public = ok/green. */
.vis-control {
	display: inline-flex; align-items: center; gap: 0.35rem;
	font-size: 0.78rem; color: var(--muted); font-weight: 600;
}

/* Checkbox + label rows (notification prefs, etc). The checkbox sits BESIDE the
   text and aligns to its first line so multi-line labels don't push it to the top
   or stack it above the text. NOTE: `.stack-form label` sets display:grid (which
   stacks children), and that compound selector outranks a bare `.checkbox-row`, so
   these rules are scoped to `.stack-form label.checkbox-row` to win on specificity. */
.checkbox-row,
.stack-form label.checkbox-row {
	display: flex;
	align-items: flex-start;
	gap: 0.55rem;
	cursor: pointer;
	line-height: 1.4;
}
.checkbox-row input[type="checkbox"],
.stack-form label.checkbox-row input[type="checkbox"] {
	flex: 0 0 auto;
	width: 1rem;
	height: 1rem;
	margin: 0;
	margin-top: 0.15rem; /* nudge onto the text's first-line baseline */
	padding: 0;          /* undo .stack-form input padding/border/background */
	border: none;
	background: none;
	border-radius: 0;
}
.checkbox-row span,
.stack-form label.checkbox-row span { flex: 1 1 auto; }
.vis-select {
	appearance: none; cursor: pointer; font-weight: 600;
	padding: 0.25rem 1.6rem 0.25rem 0.7rem; font-size: 0.8rem; line-height: 1.4;
	border-radius: 999px; border: 1px solid transparent; color: #fff;
	background-repeat: no-repeat; background-position: right 0.55rem center;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='%23fff' d='M0 0h10L5 6z'/%3E%3C/svg%3E");
	transition: background-color 0.15s ease;
}
.vis-control.vis-private  .vis-select { background-color: var(--muted); color: var(--panel);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='%23000' d='M0 0h10L5 6z'/%3E%3C/svg%3E"); }
.vis-control.vis-unlisted .vis-select { background-color: var(--warn, #c78a12); color: #fff; }
.vis-control.vis-public   .vis-select { background-color: var(--ok); color: #fff; }
.vis-select:hover { filter: brightness(1.08); }
.vis-status { font-size: 0.78rem; margin-left: 0.5rem; transition: opacity 0.3s ease; }
.vis-status.saving { color: var(--muted); }
.vis-status.saved { color: var(--ok); }
.vis-status.error { color: var(--err); }
.admin-actions { display: flex; align-items: center; gap: 0.5rem; }

.stack-form { display: grid; gap: 0.9rem; max-width: 360px; }
.stack-form label { display: grid; gap: 0.3rem; font-size: 0.85rem; color: var(--muted); }
.stack-form input {
	padding: 0.6rem 0.75rem;
	border-radius: var(--radius);
	border: 1px solid var(--border);
	background: var(--panel-2);
	color: var(--text);
}
.form-msg { font-size: 0.85rem; }
.form-msg.ok { color: var(--ok); }
.form-msg.err { color: var(--err); }
.auth-foot { margin-top: 1rem; font-size: 0.9rem; }
.watch-title { font-size: 1.2rem; margin: 1rem 0 0.25rem; width: min(100%, 1100px); }

/* ── Subscriptions & notifications (Epic R) ── */
.hidden { display: none !important; }

/* Channel row on the watch page: uploader identity + Subscribe control. */
.channel-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; width: min(100%, 1100px); margin: 0.25rem 0 0.75rem; flex-wrap: wrap; }
.channel-identity { display: inline-flex; align-items: center; gap: 0.6rem; text-decoration: none; color: inherit; }
.channel-meta { display: inline-flex; flex-direction: column; line-height: 1.2; }
.channel-name { font-weight: 600; }
.channel-subs { font-size: 0.8rem; }
.channel-sub-actions { display: inline-flex; align-items: center; gap: 0.4rem; }
.sub-btn { border-radius: 999px; padding: 0.45rem 1.1rem; font-weight: 600; }
.sub-btn.subscribed { background: var(--border); color: var(--text); }
.sub-bell { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; border-radius: 999px; }
.sub-bell.bell-off { color: var(--muted); }

/* Header notification bell + dropdown inbox. */
.notif-wrap { position: relative; display: inline-flex; }
.notif-bell { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; padding: 0; color: var(--text); }
.notif-bell svg { width: 22px; height: 22px; display: block; flex: none; }

/* ── Subscriptions management page (Epic R) ── */
.subs-main { max-width: 900px; }
.subs-heading { font-size: 1.5rem; margin: 1rem 0 1rem; }
.subs-list { list-style: none; margin: 0; padding: 0; }
.subs-item { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.8rem 0; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.subs-channel { display: inline-flex; align-items: center; gap: 0.9rem; min-width: 0; }
.subs-channel-meta { display: inline-flex; flex-direction: column; line-height: 1.3; min-width: 0; }
.subs-channel-name { font-weight: 600; font-size: 1.02rem; overflow: hidden; text-overflow: ellipsis; }
.subs-channel-sub { font-size: 0.82rem; }
.subs-actions { display: inline-flex; align-items: center; gap: 0.5rem; flex: none; }
.subs-bell { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 999px; }
.subs-bell.bell-off { color: var(--muted); }
.subs-unsub { border-radius: 999px; padding: 0.5rem 1.1rem; background: var(--border); color: var(--text); font-weight: 600; }
.subs-empty { padding: 2rem 0; }
.subs-empty p { margin: 0 0 0.5rem; }
.subs-empty .btn { margin-top: 0.6rem; }
.subs-channel .avatar-lg { width: 48px; height: 48px; }

/* ── Creator channel page (Epic R) ── */
.channel-main { max-width: 1100px; }
.channel-live-banner { display: block; padding: 0.7rem 1rem; margin: 0.5rem 0 1rem; border-radius: 8px; background: color-mix(in srgb, var(--err) 15%, transparent); border: 1px solid var(--err); color: var(--text); text-decoration: none; font-weight: 600; }
.channel-live-banner .live-dot { color: var(--err); }
.channel-header { display: flex; align-items: center; gap: 1.2rem; padding: 1rem 0 1.4rem; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.channel-avatar { width: 88px; height: 88px; flex: none; }
.channel-header-meta { flex: 1 1 auto; min-width: 0; }
.channel-title { font-size: 1.6rem; margin: 0 0 0.3rem; }
.channel-stats { margin: 0; font-size: 0.9rem; }
.channel-videos-heading { font-size: 1.15rem; margin: 1.2rem 0 0.8rem; }
.channel-empty { padding: 1rem 0; }

/* Channel banner + tabs + about (Epic R channel page enhancements). */
.channel-banner { width: 100%; margin: 0.5rem 0 1rem; border-radius: 12px; overflow: hidden; aspect-ratio: 4 / 1; background: var(--border); }
.channel-banner-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.channel-tabs { display: flex; gap: 0.25rem; border-bottom: 1px solid var(--border); margin: 1.2rem 0 1rem; overflow-x: auto; }
.channel-tab { appearance: none; background: transparent; border: none; border-bottom: 2px solid transparent; color: var(--muted); font: inherit; font-weight: 600; padding: 0.6rem 0.9rem; cursor: pointer; white-space: nowrap; }
.channel-tab:hover { color: var(--text); }
.channel-tab.is-active { color: var(--text); border-bottom-color: var(--accent); }
.channel-subhead { font-size: 1.05rem; margin: 1rem 0 0.7rem; }
.channel-about { max-width: 800px; }
.channel-bio { white-space: pre-wrap; line-height: 1.5; margin: 0 0 1.2rem; }
.channel-about-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 0.8rem; margin: 0; }
.channel-about-stats dt { font-size: 0.78rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; }
.channel-about-stats dd { margin: 0.15rem 0 0; font-size: 1.1rem; font-weight: 600; }
.channel-playlist-list { list-style: none; margin: 0; padding: 0; }
.channel-playlist-row a { display: flex; align-items: center; justify-content: space-between; padding: 0.7rem 0; border-bottom: 1px solid var(--border); text-decoration: none; color: inherit; }
.channel-playlist-title { font-weight: 600; }
.channel-playlist-count { font-size: 0.85rem; }
.notif-badge { position: absolute; top: 2px; right: 2px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px; background: var(--err); color: #fff; font-size: 0.62rem; font-weight: 700; line-height: 16px; text-align: center; }
.notif-panel { position: absolute; top: calc(100% + 8px); right: 0; width: min(360px, 92vw); max-height: 70vh; overflow-y: auto; background: var(--bg); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 8px 30px var(--shadow, rgba(0,0,0,0.4)); z-index: 60; }
.notif-panel-head { display: flex; align-items: center; justify-content: space-between; padding: 0.7rem 0.9rem; border-bottom: 1px solid var(--border); font-weight: 600; position: sticky; top: 0; background: var(--bg); }
.notif-mark-all { font-size: 0.78rem; }
.notif-list { list-style: none; margin: 0; padding: 0; }
.notif-item { border-bottom: 1px solid var(--border); }
.notif-item:last-child { border-bottom: none; }
.notif-link, .notif-item > .notif-body { display: block; padding: 0.7rem 0.9rem; text-decoration: none; color: inherit; }
.notif-link { display: flex; flex-direction: column; gap: 0.2rem; }
.notif-item .notif-body { display: block; }
.notif-time { font-size: 0.72rem; }
.notif-unread { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.notif-unread .notif-body { font-weight: 600; }
.notif-empty { padding: 1.2rem 0.9rem; text-align: center; }

/* Origin badge on a video that was recorded from a live stream (source='live'). */
.source-badge { display: inline-block; vertical-align: middle; font-size: 0.62em; font-weight: 700; letter-spacing: 0.04em; padding: 0.15em 0.5em; border-radius: 4px; margin-right: 0.4em; }
.source-live { background: var(--err, #e00); color: #fff; }

.status { font-size: 0.8rem; color: var(--muted); }
.status-ready { color: var(--ok); }
.status-processing, .status-uploaded { color: var(--warn); }
.status-error { color: var(--err); }
.badge-hls { font-size: 0.65rem; font-weight: 700; letter-spacing: 0.03em; background: var(--accent); color: var(--accent-contrast, #fff); border-radius: 4px; padding: 0.05rem 0.35rem; text-transform: uppercase; }

/* Like / dislike control */
.reactions { display: flex; gap: 0.5rem; margin: 0.5rem 0 0.75rem; }
.reaction-btn { display: inline-flex; align-items: center; gap: 0.35rem; }
.reaction-btn .reaction-icon { font-size: 0.9rem; line-height: 1; }
.reaction-btn .reaction-count { font-variant-numeric: tabular-nums; }
.reaction-btn.active { border-color: var(--accent); color: var(--accent); }
.reaction-btn.active .reaction-icon { color: var(--accent); }

/* Admin Users / Reactions list items */
.admin-item-main { display: flex; flex-direction: column; gap: 0.15rem; }
.admin-item-title { font-weight: 600; }
.admin-item-actions { display: flex; align-items: center; gap: 0.5rem; }
.admin-item-actions select { padding: 0.3rem 0.5rem; }
.admin-more { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.8rem; }
.user-role-row { margin-top: 0.4rem; }
.user-role-row select { padding: 0.25rem 0.4rem; margin-left: 0.3rem; }

/* User rows have no thumbnail, so they don't use the video 3-column grid — they
   span the full width with content on the left and the action on the right, and
   the permission checkboxes lay out as a readable multi-column grid instead of a
   single cramped column. */
#user-list .admin-item,
#suspension-list .admin-item {
	grid-template-columns: 1fr auto;
	align-items: start;
}
.user-perms {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
	gap: 0.5rem 1.25rem;
	margin-top: 0.6rem;
	width: 100%;
}
.perm-toggle-label {
	display: flex;
	align-items: flex-start;
	gap: 0.45rem;
	font-size: 0.85rem;
	line-height: 1.35;
}
.perm-toggle-label input { margin: 0.15rem 0 0 0; flex: 0 0 auto; }
.user-msg { display: inline-block; margin-top: 0.3rem; font-size: 0.78rem; min-height: 1em; }
.user-msg.err { color: var(--err); }
.reaction-msg { display: inline-block; margin-top: 0.3rem; font-size: 0.78rem; }
.reaction-msg.err { color: var(--err); }
.backup-actions { display: flex; align-items: center; gap: 0.6rem; margin: 0.6rem 0; flex-wrap: wrap; }

/* Saved-credential banner (SFTP password / private key): a clear "✓ saved" line
   with an inline Clear link, shown in place of the input once a secret is stored. */
.cred-status { display: inline-flex; align-items: center; gap: 0.5rem; color: var(--text); font-size: 0.85rem; padding: 0.35rem 0; }
.link-btn { background: none; border: none; color: var(--accent, #3b82f6); cursor: pointer; padding: 0; font: inherit; text-decoration: underline; }
.link-btn:hover { opacity: 0.8; }

/* Stacked config form (used by the Backups tab and the advertiser portal). Each
   direct <label> is a block: caption on top, control beneath. .adv-grid lays a
   group of those labels out in responsive columns so related fields sit side by
   side instead of running together on one line. */
.adv-form { display: flex; flex-direction: column; gap: 0.9rem; max-width: 760px; margin-top: 0.6rem; }
.adv-form > label,
.adv-grid > label {
	display: flex;
	flex-direction: column;
	gap: 0.3rem;
	font-size: 0.85rem;
	font-weight: 600;
	color: var(--text);
}
.adv-form label.inline {
	flex-direction: row;
	align-items: center;
	gap: 0.5rem;
	font-weight: 500;
}
.adv-form input:not([type="checkbox"]),
.adv-form select,
.adv-form textarea,
.adv-grid input:not([type="checkbox"]),
.adv-grid select {
	width: 100%;
	padding: 0.45rem 0.55rem;
	border: 1px solid var(--border);
	border-radius: var(--radius);
	background: var(--bg);
	color: var(--text);
	font: inherit;
}
.adv-form textarea { resize: vertical; min-height: 4.5rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.8rem; }
.adv-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
	gap: 0.9rem 1.1rem;
	align-items: end;
}
/* Labels stretch to the tallest cell and push their control to the bottom, so a
   long wrapping caption (e.g. the unattended-passphrase note) doesn't shove its
   input out of line with the shorter fields beside it. */
.adv-grid > label { height: 100%; justify-content: flex-end; }

/* Recommendations rail on the watch page */
.recommendations { margin: 1.5rem 0; width: min(100%, 1100px); }
.recommendations .card-title { display: block; text-decoration: none; color: inherit; }
.rec-reason { font-size: 0.75rem; }

/* Ad UI on the player */
.ad-ui { position: absolute; inset: 0; pointer-events: none; }
.ad-clickthrough { position: absolute; inset: 0; pointer-events: auto; cursor: pointer; }
.ad-badge { position: absolute; top: 0.6rem; left: 0.6rem; background: rgba(0,0,0,0.75); color: #fff; font-size: 0.7rem; font-weight: 700; padding: 0.15rem 0.4rem; border-radius: 3px; letter-spacing: 0.04em; pointer-events: none; }
.ad-skip { position: absolute; bottom: 3.5rem; right: 0.8rem; pointer-events: auto; background: rgba(0,0,0,0.8); color: #fff; border: 1px solid rgba(255,255,255,0.4); border-radius: 4px; padding: 0.4rem 0.75rem; cursor: pointer; font-size: 0.85rem; }
.ad-skip:hover { background: rgba(0,0,0,0.95); }
.ad-overlay { position: absolute; left: 50%; transform: translateX(-50%); bottom: 3.5rem; max-width: 80%; }
.ad-overlay-link img { display: block; max-width: 100%; height: auto; border-radius: 4px; }
.ad-overlay-close { position: absolute; top: -0.6rem; right: -0.6rem; width: 1.4rem; height: 1.4rem; border-radius: 50%; border: none; background: rgba(0,0,0,0.85); color: #fff; cursor: pointer; line-height: 1; }
.ad-banner { display: flex; align-items: center; gap: 0.5rem; margin: 0.5rem 0; }
.ad-banner-link img { display: block; max-width: 100%; height: auto; border-radius: 4px; }
.ad-banner-tag { font-size: 0.65rem; font-weight: 700; color: var(--muted, #888); border: 1px solid var(--border); border-radius: 3px; padding: 0.05rem 0.3rem; }

/* Share panel */
.share-panel { display: grid; gap: 0.5rem; margin: 0.5rem 0 0.75rem; padding: 0.6rem; background: var(--panel-2); border-radius: 8px; width: min(100%, 560px); }
.share-row { display: flex; align-items: center; gap: 0.5rem; }
.share-label { font-size: 0.75rem; color: var(--muted); width: 4.5rem; flex: none; }
.share-input { flex: 1; font: inherit; font-size: 0.85rem; padding: 0.35rem 0.5rem; border-radius: 6px; border: 1px solid var(--border); background: var(--panel); color: var(--text); }

/* Save-to-playlist popover (watch page) */
.save-panel { display: grid; gap: 0.6rem; margin: 0.5rem 0 0.75rem; padding: 0.7rem 0.8rem; background: var(--panel-2); border-radius: 8px; width: min(100%, 420px); }
.save-panel-head { font-weight: 600; font-size: 0.9rem; }
.save-playlist-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.15rem; max-height: 15rem; overflow-y: auto; }
.save-pl-label { display: flex; align-items: center; gap: 0.5rem; padding: 0.35rem 0.4rem; border-radius: 6px; cursor: pointer; }
.save-pl-label:hover { background: var(--panel); }
.save-pl-check { width: 1rem; height: 1rem; flex: none; cursor: pointer; }
.save-pl-title { flex: 1; font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.save-pl-vis { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.02em; flex: none; }
.save-new-row { display: flex; gap: 0.5rem; align-items: stretch; border-top: 1px solid var(--border); padding-top: 0.6rem; }
.save-new-input { flex: 1 1 auto; min-width: 0; font: inherit; font-size: 0.85rem; padding: 0.4rem 0.55rem; border-radius: 6px; border: 1px solid var(--border); background: var(--panel); color: var(--text); }
.save-new-row .btn { flex: 0 0 auto; white-space: nowrap; }
.save-msg { font-size: 0.8rem; min-height: 1em; margin: 0; }
.delete { background: transparent; border: 1px solid transparent; color: var(--muted); padding: 0.5rem 1rem; line-height: 1.2; border-radius: var(--radius); }
.delete:hover { color: var(--err); }

/* ── Theater / watch ── */
.theater { background: var(--bg); }
.theater-top { margin-bottom: 1.5rem; }
.player-wrap { display: grid; place-items: center; padding: 0 1rem 2rem; }
.player-stage { position: relative; width: min(100%, 1100px); display: grid; place-items: center; }
#player {
	width: 100%;
	max-height: 78vh;
	background: #000;
	border-radius: var(--radius);
	box-shadow: 0 4px 24px var(--shadow);
}

/* ── Theater mode ── widens the player toward the window edges; content below is
   pushed down. Toggled by body.theater-mode (set in watch.js). Overrides the default
   1100px stage cap and the 78vh height cap. Nothing in playback depends on this — it's
   purely layout, and removing these rules restores the default view exactly. */
body.theater-mode .player-wrap { padding-left: 0; padding-right: 0; }
body.theater-mode .player-stage { width: 100%; max-width: 100vw; }
body.theater-mode #player { max-height: 86vh; border-radius: 0; box-shadow: none; }

/* Theater toggle button — a small overlay in the top-right of the player stage, out
   of the way of Plyr's bottom control bar. Appears on hover/focus of the stage. */
.theater-btn {
	position: absolute; top: 0.6rem; right: 0.6rem; z-index: 6;
	display: inline-flex; align-items: center; justify-content: center;
	width: 34px; height: 34px; padding: 0;
	background: rgba(0, 0, 0, 0.55); color: #fff;
	border: none; border-radius: 6px; cursor: pointer;
	opacity: 0; transition: opacity 0.15s ease;
}
.player-stage:hover .theater-btn,
.theater-btn:focus-visible { opacity: 1; }
.theater-btn:hover { background: rgba(0, 0, 0, 0.75); }
.theater-btn[aria-pressed="true"] { color: var(--accent, #4da3ff); }
/* Theater and fullscreen are mutually exclusive, so hide the button in fullscreen.
   Standard :fullscreen / :-webkit-full-screen on the stage; split into separate rules
   because a browser that doesn't know one pseudo-class discards the whole comma
   selector. */
.player-stage:fullscreen .theater-btn { display: none; }
.player-stage:-webkit-full-screen .theater-btn { display: none; }

.unmute-overlay {
	position: absolute;
	top: 1rem;
	left: 1rem;
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.55rem 0.9rem;
	background: rgba(0, 0, 0, 0.72);
	color: #fff;
	border: none;
	border-radius: 999px;
	font: inherit;
	font-size: 0.9rem;
	font-weight: 600;
	cursor: pointer;
	backdrop-filter: blur(4px);
	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
	transition: background 0.15s ease, transform 0.15s ease;
	z-index: 2;
}
.unmute-overlay:hover { background: rgba(0, 0, 0, 0.85); transform: scale(1.03); }
.unmute-icon { font-size: 1.1rem; line-height: 1; }

/* Big centered play button shown on a cold standalone load (shared/short link)
   in place of autoplay. Clicking it starts playback with sound. */
.cold-play {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 5rem;
	height: 5rem;
	display: grid;
	place-items: center;
	background: rgba(0, 0, 0, 0.6);
	color: #fff;
	border: none;
	border-radius: 50%;
	cursor: pointer;
	backdrop-filter: blur(4px);
	box-shadow: 0 2px 16px rgba(0, 0, 0, 0.5);
	transition: background 0.15s ease, transform 0.15s ease;
	z-index: 3;
}
.cold-play:hover { background: rgba(0, 0, 0, 0.78); transform: translate(-50%, -50%) scale(1.06); }
.cold-play-icon { font-size: 2rem; line-height: 1; margin-left: 0.25rem; }

/* Plyr integration: Plyr wraps <video> in a .plyr container. Make it fill the
   stage so our absolutely-positioned overlays (cold-play, ad UI, unmute) still
   line up over the video, and keep those overlays stacked above Plyr's own
   chrome. Plyr's control bar sits at z-index ~3 internally; our gesture/ad
   overlays must sit above it. */
.player-stage .plyr {
	width: 100%;
	/* Round the player corners (Plyr wraps the <video>, so the radius must live on
	   the Plyr container, not #player). overflow:hidden clips the video content to
	   the rounded corners. Both are dropped in fullscreen (below) so Plyr can size
	   the wrapper to fill the screen without clipping. */
	border-radius: var(--radius);
	overflow: hidden;
}
.player-stage .plyr__video-wrapper {
	background: #000;
	border-radius: inherit;
	overflow: hidden;
}
/* Android fix: on many mobile browsers the <video> paints on its own
   compositor layer that ignores the wrapper's overflow:hidden clip, so once
   playback starts the video's sharp corners show through the rounded wrapper.
   Putting the radius on the video element itself clips that layer too. Lifted
   in fullscreen (below). */
.player-stage .plyr video,
.player-stage #player {
	border-radius: var(--radius);
}
/* Video-zoom transform target. The gesture layer (watch.js) sets --zoom-scale
   and --zoom-x/--zoom-y; at 1x this is the identity transform (no visual
   change). transform-origin center keeps zoom anchored to the middle before
   panning. GPU-friendly + no layout impact. */
.player-stage .plyr video,
.player-stage #player {
	transform: translate(var(--zoom-x, 0px), var(--zoom-y, 0px)) scale(var(--zoom-scale, 1));
	transform-origin: center center;
}
/* While actively pinching/dragging, disable the transition so the picture
   tracks the fingers 1:1; the class is removed on release so the snap-back to
   1x animates smoothly. */
.player-stage .plyr video,
.player-stage #player { transition: transform 0.18s ease; }
.player-stage.zoom-active .plyr video,
.player-stage.zoom-active #player { transition: none; }
/* When zoomed in, hint the drag-to-pan affordance. */
.player-stage.zoomed .plyr__video-wrapper { cursor: grab; }
.player-stage.zoom-active.zoomed .plyr__video-wrapper { cursor: grabbing; }

/* ── Zoom control ──
   HIDDEN during clean playback so it doesn't sit on the video (esp. mobile).
   Desktop: revealed by real pointer hover over the player (@media hover:hover
   below), staying up the whole time the mouse is there. Mobile/touch: revealed
   by the .shown class watch.js adds while Plyr's control bar is up or while
   zoomed. Icon-only + compact. Rides into fullscreen (child of .player-stage). */
.zoom-ui {
	position: absolute;
	right: 0.6rem;
	bottom: 3.25rem;             /* clear of Plyr's control bar */
	z-index: 11;
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	gap: 0.4rem;
	font: inherit;
	pointer-events: none;        /* container passes through; children opt back in */
	opacity: 0;
	transition: opacity 0.2s ease;
}
.zoom-ui .zoom-toggle,
.zoom-ui .zoom-panel { pointer-events: auto; }
/* When shown, the control is fully interactive. Hidden = opacity 0 AND the
   toggle can't catch clicks, so clicks fall through to normal play/pause. */
.zoom-ui:not(.shown) .zoom-toggle,
.zoom-ui:not(.shown) .zoom-panel { pointer-events: none; }

/* DESKTOP (real pointer only — @media hover:hover never matches touch, so this
   can't get stuck the way a plain :hover rule did on mobile): show the zoom
   control whenever the mouse is over the player, and KEEP it up the whole time
   the pointer is there — independent of Plyr's fast control-bar auto-hide, which
   was hiding it before you could click. */
@media (hover: hover) {
	.player-stage:hover .zoom-ui { opacity: 1; pointer-events: none; }
	.player-stage:hover .zoom-ui .zoom-toggle,
	.player-stage:hover .zoom-ui .zoom-panel { pointer-events: auto; }
}
/* MOBILE / touch + any device while actively zoomed: show it with the controls
   (a deliberate tap brings the bar up) or whenever zoomed (so you can get back to
   1x). .shown is added by watch.js in both cases. */
.zoom-ui.shown { opacity: 1; }
.zoom-ui.shown .zoom-toggle,
.zoom-ui.shown .zoom-panel { pointer-events: auto; }
/* Reveal the control (and its focus ring) when a keyboard user tabs into it, so
   the focus target isn't invisible. */
.zoom-ui:focus-within { opacity: 1; }
.zoom-ui:focus-within .zoom-toggle,
.zoom-ui:focus-within .zoom-panel { pointer-events: auto; }
.zoom-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	padding: 0;
	background: rgba(0, 0, 0, 0.55);
	color: #fff;
	border: none;
	border-radius: 999px;
	cursor: pointer;
	backdrop-filter: blur(4px);
	box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
	transition: background 0.15s ease;
}
.zoom-toggle:hover { background: rgba(0, 0, 0, 0.82); }
.zoom-icon { font-size: 1.1rem; line-height: 1; }
/* The level readout is only shown once you're actually zoomed (keeps the resting
   state to a single small circle). */
.zoom-level { display: none; }
.zoom-ui.is-zoomed .zoom-level {
	display: inline-block;
	margin-left: 0.3rem;
	font-size: 0.75rem;
	font-weight: 600;
}
.zoom-ui.is-zoomed .zoom-toggle {
	width: auto;
	padding: 0 0.6rem;
	background: var(--accent);
	color: var(--accent-contrast, #fff);
}
.zoom-panel {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.45rem 0.65rem;
	background: rgba(0, 0, 0, 0.78);
	border-radius: 999px;
	backdrop-filter: blur(4px);
	box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
}
.zoom-panel[hidden] { display: none; }
.zoom-range { width: 120px; accent-color: var(--accent); cursor: pointer; }
.zoom-reset {
	background: transparent;
	border: 1px solid rgba(255, 255, 255, 0.45);
	color: #fff;
	border-radius: 999px;
	padding: 0.15rem 0.55rem;
	font-size: 0.75rem;
	cursor: pointer;
}
.zoom-reset:hover { background: rgba(255, 255, 255, 0.15); }
/* In fullscreen, hand sizing back to Plyr: no rounding, no clipping, no width cap —
   otherwise the container is clipped/mis-sized and the video doesn't fill the
   screen (black bars). This Plyr build uses the native :fullscreen pseudo-class and
   a .plyr--fullscreen-fallback class (NOT a .plyr--fullscreen-active class), so we
   must target both. */
.player-stage .plyr:fullscreen,
.player-stage .plyr:fullscreen .plyr__video-wrapper,
.player-stage .plyr--fullscreen-fallback,
.player-stage .plyr--fullscreen-fallback .plyr__video-wrapper {
	border-radius: 0;
	overflow: visible;
}
/* The normal-view #player cap (max-height:78vh) must not apply in fullscreen, or
   it leaves a black bar below the video. Lift the cap and let Plyr size the video
   (object-fit:contain guards against any stretch). */
.player-stage .plyr:fullscreen #player,
.player-stage .plyr:fullscreen video,
.player-stage .plyr--fullscreen-fallback #player,
.player-stage .plyr--fullscreen-fallback video {
	max-height: none;
	border-radius: 0;
	object-fit: contain;
}
/* Zoom transform still applies in fullscreen (the gesture works there too), but
   the corner rounding must not — the rule above zeroes the radius; the zoom
   transform var carries through from the base .plyr video rule. */
.player-stage > #cold-play { z-index: 12; }
.player-stage > #ad-ui,
.player-stage > #ad-overlay,
.player-stage > #unmute-overlay { z-index: 11; }
.delete.btn, button.delete { color: var(--muted); }
#player-message { margin-top: 1rem; }

@media (prefers-reduced-motion: reduce) {
	/* Also stop keyframe animations (e.g. the pull-to-refresh spinner), not just
	   transitions; hover-preview autoplay is separately gated in app.js. */
	*, *::before, *::after { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
}

/* ── Admin details editor ── */
.details-toggle { background: transparent; border: none; color: var(--accent); padding: 0; font-size: 0.85rem; cursor: pointer; white-space: nowrap; }
/* The details editor spans the full row (all grid columns) so its fields get the
   whole card width instead of being trapped in the narrow middle column. */
.admin-details { grid-column: 1 / -1; margin-top: 0.6rem; display: grid; gap: 0.6rem; padding: 0.75rem 0.9rem; background: var(--panel-2); border-radius: 8px; }
.admin-details .field { display: grid; gap: 0.25rem; font-size: 0.8rem; color: var(--muted); }
.admin-details .field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; }
.admin-details textarea, .admin-details input {
	font: inherit; font-size: 0.9rem; padding: 0.4rem 0.5rem; border-radius: 6px;
	border: 1px solid var(--border); background: var(--panel); color: var(--text); width: 100%;
}
.admin-details textarea { resize: vertical; }
.details-msg { font-size: 0.8rem; margin-left: 0.5rem; }
.details-msg.ok { color: var(--ok); }
.details-msg.err { color: var(--err); }

/* Share links (time-boxed sharing) */
.share-links-block { margin-top: 0.7rem; border-top: 1px solid var(--border); padding-top: 0.7rem; }
.share-links-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.share-links-head .field-label { font-size: 0.8rem; font-weight: 600; color: var(--muted); }
.share-links-panel { margin-top: 0.6rem; display: grid; gap: 0.6rem; }
.share-links-help { font-size: 0.78rem; margin: 0; }
.share-links-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.sl-item { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.6rem; padding: 0.5rem 0.6rem; background: var(--panel); border-radius: 8px; }
.sl-main { min-width: 0; flex: 1; display: grid; gap: 0.2rem; }
.sl-row1 { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.sl-url code { font-size: 0.78rem; word-break: break-all; color: var(--text); }
.sl-window { font-size: 0.75rem; }
.sl-actions { display: flex; gap: 0.3rem; flex-shrink: 0; flex-wrap: wrap; justify-content: flex-end; }
.sl-actions .btn { padding: 0.2rem 0.55rem; font-size: 0.75rem; }
.sl-status { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em; padding: 0.1rem 0.4rem; border-radius: 999px; }
.sl-status.sl-active { background: color-mix(in srgb, var(--ok) 22%, transparent); color: var(--ok); }
.sl-status.sl-scheduled { background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--accent); }
.sl-status.sl-expired, .sl-status.sl-disabled { background: color-mix(in srgb, var(--muted) 25%, transparent); color: var(--muted); }
.sl-label-txt { font-weight: 600; font-size: 0.82rem; }
.sl-badge { font-size: 0.68rem; padding: 0.08rem 0.38rem; border-radius: 999px; background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); }
.check-field { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; color: var(--muted); }
.check-field input { width: auto; }
.share-link-new { display: grid; gap: 0.5rem; padding-top: 0.3rem; }
.sl-msg { font-size: 0.8rem; }
.sl-msg.err { color: var(--err); }
.sl-empty { font-size: 0.8rem; }

/* Shared with specific people (per-user sharing) */
.shared-with-block { margin-top: 0.7rem; border-top: 1px solid var(--border); padding-top: 0.7rem; }
.shared-with-panel { margin-top: 0.6rem; display: grid; gap: 0.6rem; }
.shared-with-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.sw-item { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; padding: 0.4rem 0.6rem; background: var(--panel); border-radius: 8px; }
.sw-who { font-size: 0.85rem; }
.sw-item .btn { padding: 0.2rem 0.55rem; font-size: 0.75rem; }
.shared-with-new { display: grid; gap: 0.5rem; }
.sw-msg { font-size: 0.8rem; }
.sw-msg.err { color: var(--err); }

/* Playlists + Groups — admin (shared panel styling) */
/* Inputs/selects/textareas inside the detail panels + create rows all share the
   admin-details field styling so they're padded, full-width, and sit on the panel
   background consistently (reused rather than duplicated per control). */
.pl-detail input, .pl-detail select, .pl-detail textarea,
.group-detail input, .group-detail select, .group-detail textarea,
.playlist-create input, .playlist-create select,
.group-create input, .group-create select,
.pl-search-row input, .group-share-new select, .group-add-row input {
	font: inherit; font-size: 0.9rem; padding: 0.5rem 0.65rem; border-radius: 6px;
	border: 1px solid var(--border); background: var(--panel); color: var(--text); width: 100%;
}
.pl-detail textarea, .group-detail textarea { resize: vertical; }

/* Create rows (New playlist / New group) — input grows, control keeps its size. */
.playlist-create, .group-create { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; margin-bottom: 1rem; }
.playlist-create input[type="text"], .group-create input[type="text"] { flex: 1 1 16rem; min-width: 12rem; width: auto; }
.playlist-create select, .group-create select { flex: 0 0 auto; width: auto; }
.playlist-create .btn, .group-create .btn { flex: 0 0 auto; white-space: nowrap; }

.playlist-admin-list, .group-admin-list { display: grid; gap: 0.6rem; }
/* Playlist/group rows reuse .admin-item but have no 140px thumbnail column —
   drop it so the row is [main 1fr | actions auto] and the detail panel below
   gets the full width instead of being squeezed into the middle grid cell. */
.playlist-row.admin-item, .group-row.admin-item { grid-template-columns: 1fr auto; }
.playlist-row .admin-item-main, .group-row .admin-item-main { min-width: 0; }
.playlist-row .admin-sub, .group-row .admin-sub { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin-top: 0.3rem; }
.pl-vis-badge { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em; padding: 0.1rem 0.4rem; border-radius: 999px; }
.pl-vis-public { background: color-mix(in srgb, var(--ok) 22%, transparent); color: var(--ok); }
.pl-vis-unlisted { background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--accent); }
.pl-vis-private { background: color-mix(in srgb, var(--muted) 25%, transparent); color: var(--muted); }
.pl-open { font-size: 0.8rem; }
.pl-manage, .group-manage { font-size: 0.8rem; background: none; border: 1px solid var(--border); border-radius: 6px; padding: 0.15rem 0.5rem; cursor: pointer; color: var(--text); }
.pl-detail, .group-detail { margin-top: 0.7rem; display: grid; gap: 0.8rem; padding: 0.8rem; background: var(--panel-2); border-radius: 8px; }
.pl-edit { display: grid; gap: 0.5rem; }
.pl-edit .field, .group-detail .field { display: grid; gap: 0.25rem; font-size: 0.8rem; color: var(--muted); }
.pl-items { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.35rem; }
.pl-item { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; padding: 0.4rem 0.6rem; background: var(--panel); border-radius: 6px; }
.pl-item-title { font-size: 0.85rem; }
.pl-item-actions { display: flex; gap: 0.25rem; flex-shrink: 0; }
.pl-item-actions .btn { padding: 0.15rem 0.5rem; font-size: 0.75rem; }

/* Add-a-video row: select grows, button keeps its label on one line. */
.pl-add-row { display: flex; gap: 0.5rem; margin-top: 0.5rem; align-items: stretch; }
.pl-add-row select { flex: 1 1 auto; min-width: 0; }
.pl-add-row .btn { flex: 0 0 auto; white-space: nowrap; }
.pl-search-add { margin-top: 0.8rem; display: grid; gap: 0.4rem; }
.pl-search-row { display: flex; gap: 0.5rem; align-items: stretch; }
.pl-search-row input { flex: 1 1 auto; min-width: 0; }
.pl-search-row .btn { flex: 0 0 auto; white-space: nowrap; }
.pl-search-results { list-style: none; margin: 0.3rem 0 0; padding: 0; display: grid; gap: 0.3rem; }
.pl-search-item { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; padding: 0.35rem 0.6rem; background: var(--panel); border-radius: 6px; font-size: 0.85rem; }
.pl-search-item .btn { padding: 0.15rem 0.5rem; font-size: 0.75rem; flex: 0 0 auto; white-space: nowrap; }

/* Groups */
.group-members { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.35rem; }
.group-add-row { display: flex; gap: 0.5rem; align-items: stretch; flex-wrap: wrap; }
.group-add-row input { flex: 1 1 auto; min-width: 10rem; }
.group-add-row .btn { flex: 0 0 auto; white-space: nowrap; }
.group-share-block { margin-top: 0.7rem; border-top: 1px solid var(--border); padding-top: 0.7rem; }
.group-share-panel { margin-top: 0.6rem; display: grid; gap: 0.6rem; }
.group-share-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.group-share-new { display: flex; gap: 0.5rem; align-items: stretch; flex-wrap: wrap; }
.group-share-new select { flex: 1 1 auto; min-width: 10rem; }
.group-share-new .btn { flex: 0 0 auto; white-space: nowrap; }
.gs-msg { font-size: 0.8rem; }
.gs-msg.err { color: var(--err); }
.gm-msg { font-size: 0.8rem; }

/* Playlists — public page */
.playlist-main { max-width: 900px; }
.playlist-header { margin-bottom: 1.5rem; display: grid; gap: 0.5rem; }
.playlist-title { margin: 0; font-size: 1.6rem; }
.playlist-desc { margin: 0; color: var(--muted); }
.playlist-meta { display: flex; gap: 0.4rem; font-size: 0.85rem; }
.playlist-vis { text-transform: capitalize; }
.playlist-header .btn { justify-self: start; margin-top: 0.4rem; }
.playlist-items { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.playlist-item-link { display: flex; align-items: center; gap: 0.8rem; padding: 0.5rem; border-radius: 8px; text-decoration: none; color: inherit; }
.playlist-item-link:hover { background: var(--panel-2); }
.playlist-item-num { width: 1.5rem; text-align: center; color: var(--muted); font-size: 0.9rem; flex-shrink: 0; }
.playlist-item-thumb { width: 120px; height: 68px; flex-shrink: 0; border-radius: 6px; overflow: hidden; background: var(--panel-2); display: flex; align-items: center; justify-content: center; }
.playlist-item-thumb img { width: 100%; height: 100%; object-fit: cover; }
.playlist-item-title { font-size: 0.95rem; }

/* ── Watch metadata ── */
.watch-category { color: var(--accent); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.04em; margin: 0.25rem 0 0; width: min(100%, 1100px); }
.watch-description { color: var(--text); width: min(100%, 1100px); margin: 0.5rem 0; line-height: 1.5; white-space: pre-wrap; }
.watch-tags { list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem; padding: 0; margin: 0.5rem 0; width: min(100%, 1100px); }
.watch-tags .tag { background: var(--panel-2); border: 1px solid var(--border); border-radius: 999px; padding: 0.2rem 0.6rem; font-size: 0.8rem; color: var(--muted); }

/* ── Node fleet ── */
.node-list { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: 0.6rem; }
.node-item { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 0.9rem; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 0.7rem 0.9rem; }
.node-status { width: 10px; height: 10px; border-radius: 50%; background: var(--muted); }
.node-status.node-online { background: var(--ok); }
.node-status.node-offline { background: var(--err); }
.node-meta { display: grid; gap: 0.15rem; min-width: 0; }
.node-name { font-weight: 600; }
.node-badge { font-size: 0.7rem; background: var(--panel-2); border: 1px solid var(--border); border-radius: 999px; padding: 0.05rem 0.4rem; color: var(--muted); font-weight: 500; }
.node-badge.node-region { background: color-mix(in srgb, var(--accent, #4a9) 15%, transparent); color: var(--accent, #4a9); border-color: color-mix(in srgb, var(--accent, #4a9) 30%, transparent); }
.node-url { font-size: 0.8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.node-health { font-size: 0.78rem; text-align: right; }
.node-cache { font-size: 0.72rem; opacity: 0.8; margin-top: 0.1rem; }

/* ── Settings form ── */
.settings-form { display: grid; gap: 1.2rem; margin: 1rem 0; }
.settings-section { border: 1px solid var(--border); border-radius: var(--radius); padding: 0.9rem 1.1rem; }
.settings-section legend { font-weight: 600; padding: 0 0.4rem; }
.settings-field { display: flex; align-items: center; gap: 0.75rem; padding: 0.45rem 0; }
.settings-field > span { color: var(--text); flex: 0 0 15rem; }
.settings-field input[type="text"], .settings-field input[type="number"], .settings-field input[type="password"], .settings-field input[type="email"], .settings-field select { flex: 1 1 auto; min-width: 0; max-width: 24rem; padding: 0.4rem 0.5rem; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); color: var(--text); font: inherit; }
.settings-field input[type="checkbox"] { flex: 0 0 auto; order: -1; width: 1.1rem; height: 1.1rem; margin: 0; }
.settings-field.is-checkbox > span { flex: 1 1 auto; }
/* On phones, stack the label above its control so a fixed-width label can't
   starve the input. Checkbox rows keep their inline layout (they're short). */
@media (max-width: 480px) {
	.settings-field:not(.is-checkbox) { flex-direction: column; align-items: stretch; }
	.settings-field:not(.is-checkbox) > span { flex: none; }
	.settings-field input[type="text"], .settings-field input[type="number"], .settings-field input[type="password"], .settings-field input[type="email"], .settings-field select { max-width: none; }
}
.settings-actions { display: flex; align-items: center; gap: 1rem; margin-top: 0.5rem; }

/* ── Per-video feature toggle in admin rows ── */
.feat-toggle { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.82rem; color: var(--muted); margin-left: 0.8rem; }
.feat-toggle select { padding: 0.15rem 0.35rem; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); color: var(--text); font-size: 0.82rem; }

/* ── Comments ── */
.comments { max-width: 900px; margin: 2rem auto 3rem; width: 100%; }
.comments-heading { font-size: 1.15rem; margin-bottom: 0.9rem; }
.comment-compose { display: flex; gap: 0.6rem; margin-bottom: 1.2rem; }
.comment-compose textarea { flex: 1; padding: 0.6rem 0.7rem; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); color: var(--text); resize: vertical; font: inherit; }
.comment-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 1rem; }
.comment-item { border-bottom: 1px solid var(--border); padding-bottom: 0.8rem; }
.comment-head { display: flex; gap: 0.6rem; align-items: center; margin-bottom: 0.2rem; }
.comment-author { font-weight: 600; }
.comment-author-link { color: inherit; text-decoration: none; }
.comment-author-link:hover { text-decoration: underline; }
.comment-avatar-link { display: inline-flex; }
.comment-when { font-size: 0.78rem; }
.comment-body { white-space: pre-wrap; word-break: break-word; }

/* Profile photos (avatars). Round, cover-fit; .avatar-empty renders a neutral
   placeholder circle when there's no image (or the <img> failed to load). */
.avatar-img { border-radius: 50%; object-fit: cover; background: var(--panel); flex: 0 0 auto; }
.avatar-sm { width: 28px; height: 28px; }
.avatar-lg { width: 96px; height: 96px; }
/* Empty state: no <img> src, so render a neutral circle with a person silhouette
   instead of the browser's broken-image glyph / alt text. */
.avatar-empty {
	background-color: var(--border);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23888'%3E%3Ccircle cx='12' cy='8.5' r='4'/%3E%3Cpath d='M4 21c0-4.4 3.6-7 8-7s8 2.6 8 7z'/%3E%3C/svg%3E");
	background-size: 62%; background-position: center; background-repeat: no-repeat;
	font-size: 0; color: transparent;
}
.comment-avatar { align-self: center; }
.avatar-edit { margin-bottom: 1.4rem; }
.avatar-edit-row { display: flex; gap: 1rem; align-items: flex-start; }
.avatar-edit-controls { display: flex; flex-direction: column; gap: 0.4rem; align-items: flex-start; }
.banner-edit-row { display: flex; gap: 1rem; align-items: flex-start; flex-wrap: wrap; margin-top: 0.5rem; }
.banner-edit-controls { display: flex; flex-direction: column; gap: 0.4rem; align-items: flex-start; }
.banner-preview { width: 240px; max-width: 100%; aspect-ratio: 4 / 1; object-fit: cover; border-radius: 8px; background: var(--border); border: 1px solid var(--border); }
.banner-preview.banner-empty { background: var(--border); }
.watch-download { margin: 0.8rem 0; }

/* ── Moderation ── */
.link-btn { background: none; border: none; color: var(--accent, #3b82f6); cursor: pointer; font: inherit; font-size: 0.8rem; padding: 0; margin-right: 0.8rem; }
.link-btn:hover { text-decoration: underline; }
.comment-mod { margin-top: 0.4rem; display: flex; gap: 0.2rem; flex-wrap: wrap; }
.comment-mod .link-btn { color: var(--muted); }
.comment-mod .comment-ban, .comment-mod .comment-ban-site { color: var(--err, #dc2626); }
.comment-report { margin-top: 0.3rem; }
.comment-report .comment-report-btn { color: var(--muted); font-size: 0.78rem; }
.report-item .report-count { color: var(--err, #dc2626); font-weight: 600; }
.report-item .report-reasons { display: block; margin-top: 0.25rem; font-size: 0.82rem; }
.report-item .admin-item-actions { flex-wrap: wrap; gap: 0.4rem; }
.ban-add { display: flex; gap: 0.6rem; align-items: center; margin: 1rem 0; flex-wrap: wrap; }
.ban-add input { padding: 0.5rem 0.7rem; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); color: var(--text); min-width: 16rem; }
.ban-list { list-style: none; padding: 0; margin: 1rem 0 0; display: grid; gap: 0.5rem; }
.ban-item { display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 0.9rem; border: 1px solid var(--border); border-radius: var(--radius); padding: 0.6rem 0.9rem; }
.ban-name { font-weight: 600; }
.ban-when { font-size: 0.78rem; }

/* Upload progress (tus resumable) */
.upload-progress { list-style: none; padding: 0; margin: 12px 0 0; display: flex; flex-direction: column; gap: 8px; }
.upload-item { display: flex; align-items: center; gap: 10px; font-size: 13px; }
.upload-item-name { flex: 0 0 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upload-bar { flex: 1; height: 8px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.upload-bar-fill { height: 100%; width: 0; background: var(--accent); transition: width 0.2s ease; }
.upload-pct { flex: 0 0 46px; text-align: right; color: var(--muted); }
.upload-item.ok .upload-bar-fill { background: var(--ok); }
.upload-item.ok .upload-pct { color: var(--ok); }
.upload-item.err .upload-bar-fill { background: var(--err); }
.upload-item.err .upload-pct { color: var(--err); }
.upload-cancel { flex: 0 0 auto; background: none; border: 1px solid var(--border); color: var(--muted); border-radius: 5px; padding: 2px 8px; font-size: 12px; cursor: pointer; }
.upload-cancel:hover:not(:disabled) { color: var(--text); border-color: var(--text); }
.upload-cancel:disabled { opacity: 0.5; cursor: default; }
.upload-item.canceled .upload-bar-fill { background: var(--muted); }
.upload-item.canceled .upload-pct { color: var(--muted); }

/* Storage report panel */
.storage-bar { height: 14px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 7px; overflow: hidden; margin-bottom: 10px; }
.storage-bar-fill { height: 100%; background: var(--accent); }

/* Audit table */
.audit-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.audit-table th, .audit-table td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--border); }
.audit-table th { color: var(--muted); font-weight: 600; }
/* Let the audit table scroll horizontally on narrow screens instead of pushing
   the whole page wider than the viewport (long target paths/ids). */
@media (max-width: 600px) {
	.audit-table { display: block; overflow-x: auto; white-space: nowrap; }
}

/* API keys panel */
.apikey-add { display: flex; gap: 8px; margin-bottom: 12px; }
.apikey-new { background: var(--panel-2); border: 1px solid var(--accent); border-radius: 8px; padding: 10px; margin-bottom: 12px; }
.apikey-new code { display: block; margin-top: 6px; word-break: break-all; color: var(--accent); }
.apikey-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
.apikey-list li { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 10px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; font-size: 13px; }
.btn-small { padding: 3px 10px; font-size: 12px; }

/* Settings action status (save / SMTP test feedback) */
#settings-status.ok { color: var(--ok); }
#settings-status.err { color: var(--err); }
.settings-actions .btn[disabled] { opacity: 0.6; cursor: default; }

/* Storage actions (sweep / migrate) */
.storage-actions { display: flex; align-items: center; gap: 0.8rem; margin: 0.8rem 0; }
.storage-migrate { border-top: 1px solid var(--border); margin-top: 1rem; padding-top: 1rem; }
.storage-migrate h3 { margin: 0 0 0.3rem; font-size: 0.95rem; }
.storage-migrate-row { display: flex; gap: 0.6rem; margin: 0.6rem 0; }
.storage-migrate-row input { flex: 1; max-width: 28rem; padding: 0.4rem 0.5rem; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); color: var(--text); font: inherit; }
#migrate-status.ok { color: var(--ok); }
#migrate-status.err { color: var(--err); }
#storage-status.err, #migrate-status.err { color: var(--err); }
.node-remove { margin-left: auto; }

/* Storage locations */
.locations { border-top: 1px solid var(--border); margin-top: 1.2rem; padding-top: 1rem; }
.locations h3 { margin: 0 0 0.3rem; font-size: 0.98rem; }
.location-list { list-style: none; padding: 0; margin: 0.8rem 0; }
.location-item { display: flex; align-items: center; gap: 0.8rem; padding: 0.6rem 0.7rem; border: 1px solid var(--border); border-radius: 8px; margin-bottom: 0.5rem; background: var(--panel); }
.loc-meta { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.loc-name { font-weight: 600; }
.loc-sub { font-size: 0.82rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.loc-actions { display: flex; gap: 0.4rem; flex-shrink: 0; flex-wrap: wrap; justify-content: flex-end; }
/* The Epic Q2 serving-status line under an S3 location is descriptive, so let it
   wrap instead of clipping like the single-line path/bucket .loc-sub above it. */
.loc-sub.loc-serving { white-space: normal; }
.loc-badge { font-size: 0.7rem; padding: 0.1rem 0.4rem; border-radius: 4px; vertical-align: middle; }
.loc-badge.active { background: var(--accent); color: var(--accent-contrast); }
.location-add { margin-top: 0.8rem; }
.location-add summary { cursor: pointer; color: var(--accent); font-size: 0.9rem; }
.location-form { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.7rem; max-width: 34rem; }
.location-form label { display: flex; flex-direction: column; gap: 0.2rem; font-size: 0.85rem; }
.location-form label.is-checkbox { flex-direction: row; align-items: center; gap: 0.4rem; }
.location-form input, .location-form select { padding: 0.4rem 0.5rem; border: 1px solid var(--border); border-radius: 6px; background: var(--panel-2); color: var(--text); font: inherit; }
.location-form input[type="checkbox"] { width: auto; }
#location-status.ok { color: var(--ok); }
#location-status.err { color: var(--err); }

/* Directory picker modal */
.path-input-row { display: flex; gap: 0.4rem; align-items: stretch; }
.path-input-row input { flex: 1; }

/* Reusable confirmation dialog (non-blocking native <dialog>). */
.confirm-dialog { width: min(90vw, 26rem); border: 1px solid var(--border); border-radius: 12px; padding: 1.25rem; background: var(--panel); color: var(--text); box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
.confirm-dialog::backdrop { background: rgba(0,0,0,0.55); }
.confirm-message { margin: 0 0 1.1rem; font-size: 0.95rem; line-height: 1.4; white-space: pre-line; }
.confirm-actions { display: flex; justify-content: flex-end; gap: 0.6rem; }
.btn-danger { background: #d13438; border-color: #d13438; color: #fff; }
.btn-danger:hover { background: #b52a2e; border-color: #b52a2e; }
.dir-picker { width: min(90vw, 34rem); max-height: 80vh; border: 1px solid var(--border); border-radius: 12px; padding: 0; background: var(--panel); color: var(--text); box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
.dir-picker::backdrop { background: rgba(0,0,0,0.55); }
.dir-picker-head { display: flex; align-items: center; justify-content: space-between; padding: 0.9rem 1rem; border-bottom: 1px solid var(--border); }
.dir-picker-head h3 { margin: 0; font-size: 1rem; }
.dir-close { background: none; border: none; color: var(--muted); font-size: 1.5rem; line-height: 1; cursor: pointer; padding: 0 0.2rem; }
.dir-close:hover { color: var(--text); }
.dir-current { padding: 0.6rem 1rem; border-bottom: 1px solid var(--border); background: var(--panel-2); }
.dir-current code { font-size: 0.85rem; color: var(--accent); word-break: break-all; }
.dir-entries { list-style: none; margin: 0; padding: 0.4rem; overflow-y: auto; max-height: 45vh; }
.dir-entry { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.6rem; border-radius: 6px; cursor: pointer; font-size: 0.9rem; }
.dir-entry:hover { background: var(--panel-2); }
.dir-entry.dir-up { color: var(--muted); }
.dir-empty { padding: 0.8rem 0.6rem; font-size: 0.85rem; }
.dir-picker-foot { display: flex; align-items: center; justify-content: space-between; gap: 0.8rem; padding: 0.8rem 1rem; border-top: 1px solid var(--border); flex-wrap: wrap; }
.dir-picker-foot .muted { font-size: 0.8rem; }
.dir-picker-actions { display: flex; gap: 0.5rem; }

/* Directory picker — volume switcher (home / drives / mounts) */
.dir-volumes { display: flex; flex-wrap: wrap; gap: 0.4rem; padding: 0.7rem 1rem 0; }
.dir-vol { font-size: 0.8rem; cursor: pointer; border: 1px solid var(--border); background: transparent; color: var(--muted); border-radius: 5px; padding: 0.25rem 0.6rem; white-space: nowrap; }
.dir-vol:hover { border-color: var(--accent); color: var(--accent); }
.dir-vol.active { border-color: var(--accent); color: var(--accent); background: var(--accent-dim, rgba(76,141,255,0.15)); }

/* Up next card (autoplay-next) */
.upnext-card { position: absolute; right: 1rem; bottom: 1rem; width: min(340px, 80%); background: rgba(13,17,23,0.94); border: 1px solid var(--border); border-radius: 12px; padding: 0.9rem; box-shadow: 0 12px 40px rgba(0,0,0,0.5); z-index: 20; }
/* The up-next card floats over the (always-black) video, so its text is fixed
   light in BOTH themes — themed tokens would go dark-on-dark in light mode. */
.upnext-label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: rgba(230,237,243,0.6); margin-bottom: 0.5rem; }
.upnext-body { display: flex; gap: 0.7rem; text-decoration: none; color: #e6edf3; align-items: center; }
.upnext-poster { width: 96px; height: 54px; object-fit: cover; border-radius: 6px; flex-shrink: 0; background: var(--panel-2); }
.upnext-meta { min-width: 0; }
.upnext-title { font-weight: 600; font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.upnext-count { font-size: 0.8rem; margin-top: 0.2rem; }
.upnext-actions { display: flex; gap: 0.5rem; margin-top: 0.7rem; }
.upnext-actions .btn { font-size: 0.8rem; padding: 0.35rem 0.7rem; }
/* On the dark up-next card a transparent ghost button reads as a black block next
   to the blue primary. Give it a clearer outline + subtle fill so it looks like a
   proper secondary button. */
.upnext-actions .btn.ghost {
	background: rgba(255, 255, 255, 0.08);
	border-color: rgba(255, 255, 255, 0.35);
	color: #fff;
}
.upnext-actions .btn.ghost:hover { background: rgba(255, 255, 255, 0.16); }

/* Admin per-video stats */
.admin-stats { display: flex; flex-wrap: wrap; gap: 0.8rem; font-size: 0.78rem; margin-top: 0.35rem; }
.admin-stats span { white-space: nowrap; }

/* SPA watch overlay (gesture-preserving full-page transition) */
.watch-overlay { position: fixed; inset: 0; z-index: 100; overflow-y: auto; background: var(--bg); }
body.watch-overlay-open { overflow: hidden; }

/* Analytics dashboard */
.analytics-controls { display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem; }
.analytics-cards { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-bottom: 1.5rem; }
.analytics-card { background: var(--panel-2, rgba(255,255,255,0.03)); border: 1px solid var(--border); border-radius: 10px; padding: 0.9rem 1.2rem; min-width: 120px; }
.analytics-card .ac-value { font-size: 1.6rem; font-weight: 700; line-height: 1.1; }
.analytics-card .ac-label { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); margin-top: 0.2rem; }
.analytics-card .ac-sub { font-size: 0.75rem; margin-top: 0.15rem; }
.analytics-charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1.2rem; margin-bottom: 1.5rem; }
.chart-box { background: var(--panel-2, rgba(255,255,255,0.03)); border: 1px solid var(--border); border-radius: 10px; padding: 1rem; }
.chart-box h3 { margin: 0 0 0.6rem; font-size: 0.9rem; }
.chart-box canvas { max-height: 240px; }
.analytics-top-list { list-style: none; padding: 0; margin: 0; }
.analytics-top-list li { display: flex; align-items: center; gap: 0.8rem; padding: 0.5rem 0; border-bottom: 1px solid var(--border); }
.analytics-top-list .at-rank { font-weight: 700; color: var(--muted); width: 1.5rem; }
.analytics-top-list .at-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.analytics-top-list .at-stat { font-size: 0.8rem; color: var(--muted); white-space: nowrap; }

/* ══ Responsive / mobile ═══════════════════════════════════════════════════ */

/* Tablet and large phones */
@media (max-width: 768px) {

	/* iOS Safari auto-zooms the page when a focused text field's font is under 16px,
	   and never zooms back out. Pin form controls to 16px on small screens so tapping
	   the search box or a login field doesn't jarringly zoom the layout. */
	input[type="text"], input[type="search"], input[type="email"], input[type="password"],
	input[type="number"], input[type="url"], input:not([type]), textarea, select {
		font-size: 16px;
	}

	.wrap { padding: 1rem; }

	.topbar { padding: 0.8rem 1rem; }

	.topbar .wordmark { font-size: 1.15rem; }

	.topbar-actions { gap: 0.4rem; }

	/* Public video grid: smaller minimum so 1–2 columns fit narrow screens. */
	.video-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 1rem; }

	/* Admin list rows stack vertically: thumb on top, meta, then actions. */
	.admin-item {
		grid-template-columns: 1fr;
		gap: 0.7rem;
	}

	.admin-thumb { width: 100%; max-width: 240px; }

	/* The sub-row of controls wraps instead of overflowing. */
	.admin-sub { flex-wrap: wrap; gap: 0.6rem 1rem; }

	.admin-actions { flex-wrap: wrap; }

	.admin-actions .btn,
	.admin-actions .delete { flex: 1 1 auto; text-align: center; }

	/* Details editor: single column so fields aren't cramped. */
	.admin-details .field-row { grid-template-columns: 1fr; }

	/* Admin shell collapses to a single column; the sidebar becomes an
	   off-canvas drawer opened by the hamburger toggle. */
	.admin-shell { grid-template-columns: 1fr; gap: 1rem; }

	.admin-nav-toggle {
		display: inline-flex;
		align-items: center;
		gap: 0.5rem;
		background: var(--panel);
		border: 1px solid var(--border);
		color: var(--text);
		border-radius: 8px;
		padding: 0.55rem 0.8rem;
		font-weight: 600;
		cursor: pointer;
	}
	.admin-nav-toggle svg { width: 18px; height: 18px; }

	.admin-tabs {
		position: fixed;
		top: 0;
		left: 0;
		bottom: 0;
		width: 264px;
		max-height: none;
		margin: 0;
		padding: 1.1rem 0.9rem;
		background: var(--panel);
		border-right: 1px solid var(--border);
		box-shadow: 0 0 40px var(--shadow);
		transform: translateX(-100%);
		transition: transform 0.2s ease;
		z-index: 60;
	}
	.admin-tabs.open { transform: translateX(0); }

	.admin-nav-backdrop {
		display: block;
		position: fixed;
		inset: 0;
		background: rgba(0, 0, 0, 0.5);
		z-index: 55;
	}
	.admin-nav-backdrop[hidden] { display: none; }

	.tab { font-size: 0.98rem; padding: 0.6rem 0.7rem; }

	/* Analytics cards: 2-up on tablet. */
	.analytics-card { flex: 1 1 calc(50% - 0.8rem); min-width: 0; }

	.analytics-charts { grid-template-columns: 1fr; }

	/* Theater/watch: reclaim horizontal space. */
	.player-wrap { padding: 0 0.5rem 1.5rem; }

	.theater-top { margin-bottom: 1rem; }

	.comments { margin: 1.5rem auto 2rem; }
}

/* Small phones */
@media (max-width: 480px) {

	.wrap { padding: 0.75rem; }

	/* Single-column video grid on the smallest screens. */
	.video-grid { grid-template-columns: 1fr; }

	/* Comment composer stacks so the button isn't squeezed. */
	.comment-compose { flex-direction: column; }

	.comment-compose .btn { width: 100%; }

	/* Analytics cards full width, one per row. */
	.analytics-card { flex-basis: 100%; }

	.analytics-card .ac-value { font-size: 1.35rem; }

	/* Up-next card: keep it inside the viewport. */
	.upnext-card { left: 0.5rem; right: 0.5rem; bottom: 0.5rem; width: auto; }

	/* Auth / stacked forms fill the width. */
	.stack-form,
	.auth-card { max-width: 100%; }

	/* Slightly smaller headings so nothing overflows. */
	h1 { font-size: 1.5rem; }
}

/* Touch devices: previews rely on hover, which touch can't do. Show the poster
   image at full opacity so tiles look intentional (tap opens the video). */
@media (hover: none) {

	.poster .preview { display: none; }

	.poster-img { opacity: 1; }
}

/* Diagnostics panel */
.diag-block { margin: 0 0 28px; padding: 16px; border: 1px solid var(--border, #333); border-radius: 8px; }
.diag-block h3 { margin: 0 0 6px; }
.diag-block select, .diag-block .btn { margin: 8px 8px 8px 0; }
.diag-env { font-size: 12px; margin: 8px 0; word-break: break-all; }
.diag-log { background: #111; color: #cfe; padding: 12px; border-radius: 6px; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px; line-height: 1.5; max-height: 340px; overflow: auto; white-space: pre-wrap; }
.diag-result { margin-left: 8px; font-size: 14px; }

/* Scrub-bar cursor: show the pointer (finger) over the progress slider so hover-
   scrubbing for preview thumbnails feels like YouTube. Scoped to the progress
   input only — the rest of the controls keep their default cursors. */
.plyr__progress input[type="range"],
.plyr__progress__input,
.plyr .plyr__progress__container {
	cursor: pointer;
}

/* ── Comments: threading, replies, @mentions, owner toggle ─────────────────── */
.comments-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.comments-toggle { display: inline-flex; align-items: center; gap: 0.4rem; cursor: pointer; color: var(--muted); font-size: 0.9rem; }
.comments-toggle input { cursor: pointer; }

.comment-mention { color: var(--accent, #3b82f6); font-weight: 600; }

.comment-actions { display: flex; gap: 0.75rem; margin-top: 0.25rem; }

/* One-level reply thread, indented under its parent. The nested <ul> is ready for
   deeper nesting later without markup changes. */
.comment-replies { list-style: none; margin: 0.5rem 0 0; padding: 0 0 0 1.5rem; border-left: 2px solid var(--border, rgba(255,255,255,0.1)); display: flex; flex-direction: column; gap: 0.75rem; }
.comment-reply { margin-top: 0; }

.reply-compose { margin-top: 0.5rem; display: flex; flex-direction: column; gap: 0.4rem; }
.reply-compose textarea { width: 100%; resize: vertical; }
.reply-compose-actions { display: flex; align-items: center; gap: 0.75rem; }

/* @mention autocomplete dropdown, positioned under the composer textarea. */
.mention-menu { position: absolute; z-index: 30; left: 0; right: auto; min-width: 12rem; max-height: 12rem; overflow-y: auto; margin: 0.2rem 0 0; padding: 0.25rem 0; list-style: none; background: var(--panel, #1b1b1f); border: 1px solid var(--border, rgba(255,255,255,0.15)); border-radius: 6px; box-shadow: 0 6px 20px rgba(0,0,0,0.35); }
.mention-option { padding: 0.4rem 0.75rem; cursor: pointer; color: var(--text); }
.mention-option:hover { background: var(--accent, #3b82f6); color: #fff; }

/* ── Live streaming (Epic H) ── */
.live-page { max-width: 780px; }
.live-warning { background: var(--panel-2); border: 1px solid var(--border); border-left: 3px solid var(--warn); border-radius: 8px; padding: 0.75rem 0.9rem; color: var(--text); }
.live-card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem 1.1rem; margin: 0 0 1.1rem; }
.live-card h2 { margin: 0 0 0.4rem; font-size: 1.05rem; }
.live-field { display: grid; gap: 0.3rem; margin: 0.75rem 0 0; font-size: 0.85rem; color: var(--muted); }
.live-field span { color: var(--muted); }
.live-field input[type="text"], .live-field input[type="password"], .live-field textarea, .live-field select { padding: 0.5rem 0.6rem; border-radius: 8px; border: 1px solid var(--border); background: var(--panel-2); color: var(--text); font: inherit; width: 100%; }
.live-field textarea { resize: vertical; }
.live-copyrow { display: flex; gap: 0.4rem; align-items: center; }
.live-copyrow input { flex: 1 1 auto; min-width: 0; }
.live-copyrow .btn { flex: 0 0 auto; white-space: nowrap; }
.live-actions { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.9rem; }
.live-status { padding: 0.6rem 0.75rem; border-radius: 8px; background: var(--panel-2); color: var(--muted); }
.live-status.is-live { border-left: 3px solid var(--err); color: var(--text); font-weight: 600; }
.live-record-row span { display: inline-flex; align-items: center; gap: 0.45rem; color: var(--text); }

/* Live player badge + jump-to-live control. */
/* Reserve a stable 16:9 area so the stage holds its height BEFORE any video loads.
   Without this, an empty <video> collapses to ~100px while the "waiting for stream"
   overlay is showing, then the box jumps to full size once the stream provides
   dimensions. The pipeline normalizes output to 16:9, so this matches the video and
   avoids the pop-in. Fullscreen overrides height below, so this only applies in the
   normal in-page view. */
.live-stage { position: relative; border-radius: var(--radius); overflow: hidden; aspect-ratio: 16 / 9; background: #000; }
.live-stage #player { width: 100%; height: 100%; object-fit: contain; }
/* The video's own radius is now redundant (the stage clips), but harmless. In
   fullscreen we clear the rounding + clipping + the reserved ratio so nothing is
   cropped or constrained. */
.live-stage.is-fullscreen, .live-stage:fullscreen, .live-stage:-webkit-full-screen { border-radius: 0; aspect-ratio: auto; }
.live-badge { position: absolute; top: 0.7rem; left: 0.7rem; z-index: 5; background: var(--err); color: #fff; font-weight: 700; font-size: 0.7rem; letter-spacing: 0.04em; padding: 0.2rem 0.5rem; border-radius: 4px; }

/* "Stream hasn't started yet" overlay — YouTube-style, shown while the viewer
   waits for the broadcast to go live. Non-terminal: the page keeps polling and
   attaches automatically, so the copy reassures the viewer not to refresh. */
.live-waiting { position: absolute; inset: 0; z-index: 6; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.5rem; text-align: center; padding: 1rem; background: rgba(0, 0, 0, 0.72); color: #fff; }
.live-waiting-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--err); animation: live-waiting-pulse 1.4s ease-in-out infinite; }
.live-waiting-text { font-weight: 700; font-size: 1rem; }
.live-waiting-sub { font-size: 0.82rem; opacity: 0.8; }
@keyframes live-waiting-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.4; transform: scale(0.8); } }
@media (prefers-reduced-motion: reduce) { .live-waiting-dot { animation: none; } }

/* ── Live chat (Epic H slice 7) ── */
/* Viewer theater: player + chat side-by-side on wide screens, stacked on narrow. */
.live-layout { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 1rem; align-items: start; }
.live-layout .player-wrap { padding-bottom: 0; }
.live-chat { display: flex; flex-direction: column; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); min-height: 320px; max-height: 70vh; overflow: hidden; }
.live-chat .chat-head { display: flex; align-items: center; justify-content: space-between; font-weight: 700; font-size: 0.9rem; padding: 0.6rem 0.8rem; border-bottom: 1px solid var(--border); }
.chat-close { display: none; background: none; border: none; color: var(--muted); cursor: pointer; font-size: 1rem; line-height: 1; padding: 0 0.2rem; }
.chat-close:hover { color: var(--text); }
.chat-list { flex: 1 1 auto; overflow-y: auto; padding: 0.6rem 0.8rem; display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.9rem; line-height: 1.35; }
.chat-msg { word-wrap: break-word; overflow-wrap: anywhere; }
.chat-name { font-weight: 600; color: var(--accent); }
.chat-body { color: var(--text); }
.chat-mod { white-space: nowrap; }
.chat-mod button { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 0.72rem; padding: 0 0.15rem; }
.chat-mod button:hover { color: var(--err); }
.chat-notice { padding: 0.35rem 0.8rem; font-size: 0.8rem; color: var(--warn); border-top: 1px solid var(--border); }
.chat-form { display: flex; gap: 0.4rem; padding: 0.6rem 0.8rem; border-top: 1px solid var(--border); }
.chat-input { flex: 1 1 auto; min-width: 0; padding: 0.4rem 0.6rem; border: 1px solid var(--border); border-radius: 6px; }
.chat-send { flex: 0 0 auto; }
/* Broadcaster page: chat sits in a normal card, not the theater sidebar. */
.live-chat-broadcaster { max-height: 340px; }
@media (max-width: 900px) {
	.live-layout { grid-template-columns: minmax(0, 1fr); }
	.live-chat { max-height: 50vh; }
}

/* ── Custom control bar ── replaces native <video controls>. Sits along the bottom
   of the stage, auto-hides when idle. Scrubber row on top, buttons row below. */
.live-controls {
	position: absolute; left: 0; right: 0; bottom: 0; z-index: 8;
	display: flex; flex-direction: column; gap: 0.35rem;
	padding: 0.6rem 0.7rem 0.5rem;
	background: linear-gradient(to top, rgba(0,0,0,0.75), rgba(0,0,0,0));
	opacity: 0; transition: opacity 0.2s ease; pointer-events: none;
}
.live-stage.controls-active .live-controls { opacity: 1; pointer-events: auto; }
.lc-row { display: flex; align-items: center; gap: 0.6rem; }
.lc-btn {
	background: none; border: none; color: #fff; cursor: pointer;
	font-size: 1.1rem; line-height: 1; padding: 0.2rem 0.35rem; border-radius: 4px;
	/* Ensure a comfortable touch target without enlarging the glyph. */
	min-width: 40px; min-height: 40px;
	display: inline-flex; align-items: center; justify-content: center;
}
.lc-btn:hover { background: rgba(255,255,255,0.15); }
.lc-volume { width: 80px; accent-color: #fff; cursor: pointer; }
.lc-time { color: #fff; font-size: 0.78rem; font-variant-numeric: tabular-nums; }
.lc-spacer { flex: 1 1 auto; }

/* Live "go to edge" button — grey when behind, solid red when at the live edge. */
.lc-live-btn {
	background: rgba(255,255,255,0.15); color: #fff; border: none; cursor: pointer;
	font-weight: 700; font-size: 0.7rem; letter-spacing: 0.04em;
	padding: 0.2rem 0.5rem; border-radius: 4px;
}
.lc-live-btn.at-live { background: var(--err, #e00); color: #fff; }

/* Scrubber (YouTube-style timeline over the seekable/DVR window). */
.lc-scrub { padding: 0.4rem 0; cursor: pointer; touch-action: none; }
.lc-scrub-track { position: relative; height: 4px; border-radius: 2px; background: rgba(255,255,255,0.28); }
.lc-scrub-buffered { position: absolute; left: 0; top: 0; height: 100%; border-radius: 2px; background: rgba(255,255,255,0.4); width: 0; }
.lc-scrub-played { position: absolute; left: 0; top: 0; height: 100%; border-radius: 2px; background: var(--err, #e00); width: 0; }
.lc-scrub-handle { position: absolute; top: 50%; left: 0; width: 12px; height: 12px; border-radius: 50%; background: var(--err, #e00); transform: translate(-50%, -50%); opacity: 0; transition: opacity 0.15s ease; }
.lc-scrub:hover .lc-scrub-handle, .live-stage.controls-active .lc-scrub-handle { opacity: 1; }
.lc-scrub:hover .lc-scrub-track { height: 6px; }

/* When the STAGE is the fullscreen element it fills the screen. Switch to FLEX so the
   video and the chat panel split the row cleanly (grid place-items:center was
   letterboxing + centering the video, leaving stray black bars). :fullscreen and
   :-webkit-full-screen MUST be separate rules — a browser that doesn't know one
   pseudo discards the whole comma-joined selector. .is-fullscreen (JS-added) carries
   the same styling as a belt-and-suspenders path. */
.live-stage.is-fullscreen,
.live-stage:fullscreen,
.live-stage:-webkit-full-screen {
	width: 100vw; height: 100vh; max-width: none; background: #000;
	display: flex; align-items: center; justify-content: center;
}
.live-stage.is-fullscreen #player,
.live-stage:fullscreen #player,
.live-stage:-webkit-full-screen #player {
	width: 100%; height: 100%;
	max-height: none;              /* base #player has max-height:78vh — must clear it */
	min-width: 0; border-radius: 0; box-shadow: none;
	object-fit: contain;
}

/* Fullscreen WITH chat open. The chat is pinned as a fixed right-side panel and the
   video area is inset by the same width so the video fills ALL the space left of the
   chat (full height, full remaining width) — a portrait source still letterboxes
   sideways, same as YouTube, but it's as large as the space allows, not shrunk. The
   panel is a column so the message list scrolls and the input row stays pinned. */
.live-stage.has-fs-chat:not(.chat-hidden) { padding-right: min(360px, 32vw); box-sizing: border-box; }
.live-stage.has-fs-chat:not(.chat-hidden) #player {
	width: 100%; height: 100%; max-height: none; object-fit: contain;
}
.live-stage.has-fs-chat:not(.chat-hidden) .live-chat {
	position: absolute; top: 0; right: 0; bottom: 0; z-index: 10;
	width: min(360px, 32vw); max-height: none; min-height: 0;
	display: flex; flex-direction: column;
	border-radius: 0; border: none; border-left: 1px solid rgba(255,255,255,0.14);
	background: #181818; color: #fff;
}
.live-stage.has-fs-chat .chat-head { flex: 0 0 auto; color: #fff; border-bottom-color: rgba(255,255,255,0.14); }
.live-stage.has-fs-chat .chat-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; color: #f1f1f1; }
.live-stage.has-fs-chat .chat-form { flex: 0 0 auto; border-top-color: rgba(255,255,255,0.14); }
.live-stage.has-fs-chat .chat-name { color: #8ab4ff; }
.live-stage.has-fs-chat .chat-input {
	background: #121212; color: #fff; border: 1px solid rgba(255,255,255,0.18);
}
.live-stage.has-fs-chat .chat-close { display: block; }        /* close only in FS */
.live-stage.has-fs-chat.chat-hidden .live-chat { display: none; } /* closed = hidden */

/* Keep the control bar clear of the chat panel when it's open. */
.live-stage.has-fs-chat:not(.chat-hidden) .live-controls { right: min(360px, 32vw); }

/* "Show chat" reopen button — JS toggles [hidden]; only appears in FS with chat closed. */
.live-showchat-btn {
	position: absolute; top: 0.7rem; right: 0.7rem; z-index: 10;
	background: rgba(0,0,0,0.6); color: #fff; border: none; border-radius: 999px;
	padding: 0.35rem 0.8rem; font-size: 0.85rem; cursor: pointer;
}
.live-showchat-btn[hidden] { display: none; }




/* "Live now" strip on the home page. */
.live-now { margin: 0 0 1.5rem; }
.live-now-heading { display: flex; align-items: center; gap: 0.5rem; font-size: 1.05rem; margin: 0 0 0.6rem; }
.live-now-heading::before { content: "●"; color: var(--err); }
.live-now-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0.9rem; }
.live-now-card { display: block; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 0.8rem 0.9rem; text-decoration: none; color: var(--text); }
.live-now-card:hover { border-color: var(--accent); }
.live-now-card .live-badge { position: static; display: inline-block; margin-bottom: 0.4rem; }

/* ── Browser "Go Live" capture UI (Epic H browser ingest) ── */
.golive-card { }
.golive-preview-wrap { position: relative; background: #000; border-radius: var(--radius); overflow: hidden; aspect-ratio: 16 / 9; margin: 0.6rem 0 0.9rem; }
.golive-preview { width: 100%; height: 100%; object-fit: contain; display: block; background: #000; }
.golive-preview.mirror { transform: scaleX(-1); }
.golive-controls { display: grid; gap: 0.7rem; }
.golive-source { display: flex; flex-wrap: wrap; align-items: center; gap: 0.9rem; font-size: 0.9rem; }
.golive-source label { display: inline-flex; align-items: center; gap: 0.35rem; cursor: pointer; }
.golive-buttons { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.golive-go { background: var(--err); border-color: var(--err); color: #fff; font-weight: 700; }
.golive-go:disabled { opacity: 0.5; cursor: not-allowed; }
.golive-stop { background: var(--panel-2); border: 1px solid var(--err); color: var(--err); font-weight: 700; }
.golive-timer { position: absolute; top: 0.7rem; right: 0.7rem; z-index: 5; background: rgba(0,0,0,0.6); color: #fff; font-variant-numeric: tabular-nums; font-size: 0.8rem; padding: 0.15rem 0.5rem; border-radius: 4px; }
.golive-unsupported { margin-top: 0.6rem; color: var(--warn); font-size: 0.9rem; }
.golive-watch-link { margin-top: 0.5rem; }
.live-advanced { }
.live-advanced summary { cursor: pointer; list-style: revert; }
.live-advanced summary h2 { display: inline; font-size: 1.05rem; }
.live-advanced summary::-webkit-details-marker { display: inline; }

/* ── Browser Go Live: device pickers, source status, share link ── */
.golive-preview-empty { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); max-width: 80%; text-align: center; color: var(--muted); font-size: 0.9rem; margin: 0; padding: 0 1rem; }
.golive-devices { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; }
/* Stream key is masked with CSS rather than type="password" so browsers don't offer
   to "save your password" for it (it's a stream key, not a login credential). The
   Show button toggles the .key-masked class. -webkit-text-security covers Chrome/
   Safari; text-security-disc covers Firefox where supported; both degrade to plain
   text if unsupported, which is acceptable for a field behind a Show/Hide toggle. */
.key-masked { -webkit-text-security: disc; text-security: disc; }
@media (max-width: 560px) { .golive-devices { grid-template-columns: 1fr; } }
.golive-screen-mic { display: flex; flex-direction: column; gap: 0.5rem; margin: 0.6rem 0; }
.golive-screen-mic .live-field { max-width: 22rem; }
.live-check { display: inline-flex; align-items: center; gap: 0.45rem; cursor: pointer; }
.live-check input { margin: 0; }
.golive-audio-hint { font-size: 0.82rem; line-height: 1.4; margin: 0.2rem 0 0; }
.golive-current { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; font-size: 0.9rem; color: var(--text); background: var(--panel-2); border-radius: 8px; padding: 0.5rem 0.7rem; margin: 0; }
.golive-current .golive-change { flex: 0 0 auto; padding: 0.25rem 0.6rem; font-size: 0.8rem; }
.golive-share-card .live-copyrow { margin-top: 0.3rem; }
.golive-share-row { align-items: center; }
.golive-watch-url { flex: 1 1 auto; min-width: 0; display: block; padding: 0.5rem 0.7rem; background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; color: var(--link, var(--text)); text-decoration: none; word-break: break-all; font-family: inherit; }
.golive-watch-url:hover { text-decoration: underline; }
.golive-private-note { margin-top: 0.5rem; }
