/* style.css */

:root {
	--bg: #0d0d0f;
	--bg-glow: #17151f; /* matches loading.css's boot-screen glow */
	--surface: #16161a;
	--surf2: #1e1e24;
	--border: #2a2a35;
	--accent: #7c6af7;
	--accent-soft: #a79bfa;
	--a2: #e8934a;
	--a3: #40c9a8;
	--text: #e8e8f0;
	--muted: #6b6b85;
	--ok: #40c9a8;
	--err: #e8576a;
	--warn: #e8b84a;
	--r: 8px;
	--mono: 'Roboto Condensed', system-ui, sans-serif;
	--sans: 'Roboto Condensed', system-ui, sans-serif;

/* Light-mode override for the auth/login page's color-changer (see the
   .auth-theme-toggle-btn in view-auth.js and toggleAuthColorTheme() in
   auth.js). Scoped to the <html> attribute that button sets, and Settings >
   Appearance in the dashboard (same 'beuclub_auth_theme' key), so the login
   page and the dashboard always share one theme. */
}

html[data-auth-theme="light"] {
	--bg: #f4f4f8;
	--bg-glow: #ffffff;
	--surface: #ffffff;
	--surf2: #ececf4;
	--border: #dcdce6;
	--accent: #6c4fe0;
	--accent-soft: #8f7af0;
	--text: #17151f;
	--muted: #6b6b85;
}

*,
*::before,
*::after {
	box-sizing: border-box;
	margin: 0;
	padding: 0;
	/* Kills the blue/grey tap-highlight rectangle mobile browsers draw
	   on any tap target by default (buttons, links, anything with a
	   click handler). The press feedback below replaces it. */
	-webkit-tap-highlight-color: transparent;
}

/* Universal "press" feedback: anything actually clickable shrinks
   slightly while held down and eases back to full size on release,
   instead of (or in addition to) whatever hover/focus state it has.
   [data-onclick] covers the custom inline-handler.js pattern used
   for almost every button/link in the app (see inline-handler.js),
   so this one rule reaches the whole site without listing every
   individual button class. */
button,
a,
[data-onclick],
input[type="submit"],
input[type="button"],
.btn {
	transition: transform .12s ease;
}

button:active,
a:active,
[data-onclick]:active,
input[type="submit"]:active,
input[type="button"]:active,
.btn:active {
	transform: scale(0.96);
}

html {
	-webkit-text-size-adjust: 100%;
	/* auto (not clip) so a viewport narrower than .app's 390px floor
	   (see .app below) scrolls horizontally instead of cutting the UI
	   off -- clip would just hide whatever doesn't fit. */
	overflow-x: auto;
	scrollbar-gutter: stable;
	scrollbar-width: thin;
	scrollbar-color: rgba(255,255,255,0.18) var(--bg);
}

@supports not (scrollbar-gutter: stable) {
	html { overflow-y: scroll; }
}

html.no-scroll {
	overflow: hidden;
}

/* Same background-freeze as html.no-scroll (used by the Profile modal),
   just keyed off body.modal-open instead -- mentions.js, club.js, and
   profile.js add this class to <body> while their own modals are open
   (via lockBodyScroll()/unlockBodyScroll() in helpers.js).
   BUGFIX: this used to be just `overflow: hidden` -- enough for a
   desktop mouse wheel, but not a touch drag/rubber-band scroll already
   in progress on a touchscreen (e.g. opening the Mentions inbox
   mid-scroll), which could still visibly move Home underneath the
   dialog. position:fixed (matching body.ext-web-open below, same
   reasoning) actually stops that; the scroll offset is recorded in
   --modal-open-scroll-y by lockBodyScroll() and restored by
   unlockBodyScroll() so closing the dialog doesn't jump the page back
   to the top. */
body.modal-open {
	position: fixed;
	top: calc(-1 * var(--modal-open-scroll-y, 0px));
	left: 0;
	right: 0;
	overflow: hidden;
}

html::-webkit-scrollbar {
	width: 10px;
}
html::-webkit-scrollbar-track {
	background: var(--bg);
}
html::-webkit-scrollbar-thumb {
	background: rgba(255,255,255,0.18);
	border-radius: 6px;
	border: 2px solid var(--bg);
	background-clip: padding-box;
}
html::-webkit-scrollbar-thumb:hover {
	background: rgba(255,255,255,0.3);
	background-clip: padding-box;
}

/* Light theme: the thumb above is white-ish (made for the dark page) and vanished on a light page --
   same slim thumb, dark tint, so the page scrollbar is visible in both themes. */
html[data-auth-theme="light"] { scrollbar-color: rgba(23, 21, 31, .32) var(--bg); }
html[data-auth-theme="light"]::-webkit-scrollbar-thumb { background: rgba(23, 21, 31, .28); background-clip: padding-box; }
html[data-auth-theme="light"]::-webkit-scrollbar-thumb:hover,
html[data-auth-theme="light"]::-webkit-scrollbar-thumb:active { background: rgba(23, 21, 31, .5); background-clip: padding-box; }

body {
	background: var(--bg);
	color: var(--text);
	font-family: var(--sans);
	min-height: 100vh;
	min-height: 100dvh;
	display: flex;
	flex-direction: column;
	-webkit-font-smoothing: antialiased;
	/* Horizontal scrolling below the 390px floor is handled by <html>
	   above. Do NOT set overflow-x here: any non-visible overflow on
	   <body> makes it a scroll container (it never actually scrolls,
	   html does), and position:sticky then measures against that
	   inert box -- the header and .sp-tabs stopped sticking. */
	width: 100%;
	animation: page-fade-in .32s ease both;
}

@keyframes page-fade-in {
	from { opacity: 0; }
	to { opacity: 1; }
}

body.page-fade-out {
	opacity: 0;
	transition: opacity .15s ease;
}

@media (prefers-reduced-motion: reduce) {
	body { animation: none; }
	body.page-fade-out { transition: none; }
}

/* Flat theme background -- the same as the login page. The old 40px purple
   grid pattern that used to sit behind the dashboard was removed. */
body::before {
	display: none;
}

body.has-pixel-dots::before {
	opacity: 0;
}

#aboutPixelDots {
	position: fixed;
	inset: 0;
	z-index: 0;
	pointer-events: none;
}

.app {
	visibility: visible;
	position: relative;
	z-index: 1;
	width: min(75%, 1100px);
	max-width: 95vw;
	/* Floor for the app UI -- below this it doesn't keep shrinking/
	   squishing cards; the page scrolls horizontally instead (see the
	   html/body overflow-x: auto above). min-width naturally wins over
	   max-width/width when they conflict, so this also overrides the
	   100%/100vw values the @media blocks below set at narrow widths. */
	min-width: 390px;
	margin: 0 auto;
	padding: 0 clamp(10px, 3vw, 20px) var(--bottom-nav-h, calc(76px + env(safe-area-inset-bottom)));
	flex: 1;
	display: flex;
	flex-direction: column;
	min-height: 0;
}

#appRoot {
	visibility: visible;
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
}

@media (max-width: 1100px) {
	.app {
		width: 90%;
		max-width: 90vw;
	}
}

@media (max-width: 900px) {
	.app {
		width: 95%;
		max-width: 95vw;
	}
}

@media (max-width: 600px) {
	.app {
		width: 100%;
		max-width: 100vw;
		padding: 0 10px var(--bottom-nav-h, calc(68px + env(safe-area-inset-bottom)));
	}
}

header {
	position: sticky;
	top: 0;
	z-index: 120;
	background: rgba(13,13,15,.92);
	backdrop-filter: blur(10px);
	display: flex;
	align-items: center;
	justify-content: space-between;
	/* Same space above and below the search row, no divider line and no margin: the page content starts
	   exactly --header-pad under it on every tab (Home, Shorts, Friends, Calls, Settings, Profile). */
	padding: var(--header-pad, 12px) 0;
	gap: 10px;
	flex-wrap: wrap;
}

.brand {
	display: flex;
	align-items: center;
	gap: 8px;
	font-weight: 800;
	font-size: clamp(14px, 3.5vw, 18px);
	letter-spacing: -.4px;
}

.brand-mark {
	width: clamp(26px, 5vw, 32px);
	height: clamp(26px, 5vw, 32px);
	border-radius: 7px;
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	color: var(--accent);
}

.brand-mark svg {
	width: 100%;
	height: 100%;
}

.brand-version {
	font-size: 10px;
	font-weight: 700;
	font-family: var(--mono);
	color: var(--accent);
	background: color-mix(in srgb, var(--accent) 12%, transparent);
	border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
	border-radius: 4px;
	padding: 1px 5px;
	letter-spacing: .3px;
	align-self: center;
}

.header-r {
	display: flex;
	align-items: center;
	gap: 6px;
	flex-wrap: wrap;
}

#syncIndicator {
	font-size: 11px;
	font-family: var(--mono);
	color: var(--muted);
	padding: 4px 10px;
	border-radius: 20px;
	background: var(--surf2);
	border: 1px solid var(--border);
	transition: color .25s;
	user-select: none;
}

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

#syncIndicator.saved {
	color: var(--ok);
}

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

/* Loading state: while #dateLabel is still empty (init.js/calendar.js
   haven't set today's date text yet), the PILL'S OWN BACKGROUND shimmers
   -- same compositor-only transform-sweep technique as .skel::after --
   rather than the old approach of covering the date text with an opaque
   grey block (.date-label-skel) until it was ready. Removed the instant
   this resolves: init.js/calendar.js drop the .loading class the same
   moment they set #dateLabel's real text (see those files), so this is
   only ever visible for the brief window before that. #dateLabel itself
   keeps a reserved min-width while empty (below) so the pill doesn't
   collapse to just its chevron button during that window. */

/* #dateLabel renders fmtDateShort()'s "DD-MM-YYYY" (see init.js) -- 10
   characters, e.g. "30-08-2026". Reserve that exact width (10ch, in the
   pill's own monospace font so 1ch == 1 digit) at all times, not just
   while empty, so the pill doesn't visibly grow/reflow the instant the
   loading shimmer ends and the real date text replaces it. */
#dateLabel {
	display: inline-block;
	min-width: 10ch;
	vertical-align: middle;
}
#dateLabel:empty {
	height: 12px;
}

#dateChevronSvg {
	transition: transform .2s;
}

.field {
	margin-bottom: 14px;
}

label {
	display: block;
	font-size: 11px;
	font-weight: 600;
	color: var(--muted);
	margin-bottom: 5px;
	letter-spacing: .07em;
	text-transform: uppercase;
}

input,
select,
textarea {
	width: 100%;
	background: var(--bg);
	border: 1px solid var(--border);
	color: var(--text);
	border-radius: var(--r);
	padding: 9px 13px;
	font-size: 14px;
	font-family: var(--sans);
	outline: none;
	transition: border-color .18s, box-shadow .18s;
	-webkit-appearance: none;
	appearance: none;
}

input:focus,
select:focus,
textarea:focus {
	border-color: var(--accent);
	box-shadow: 0 0 0 3px rgba(124, 106, 247, .14);
}

input:disabled,
select:disabled,
textarea:disabled {
	opacity: .5;
	cursor: not-allowed;
}

select {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%236868a0'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 12px center;
	padding-right: 32px;
}

select option {
	background: var(--surface);
}

/* ------------------------------------------------------------
   College combo -- a searchable replacement for the plain
   <select id="...CollegeSelect"> used on the signup, edit-profile, and
   results-autofind forms. The native <select>'s own popup can't be
   styled (width, wrapping, a search box), so college-list.js hides the
   <select> (kept around as the source of truth for its value -- every
   existing populateCollegeSelect()/getCollegeFieldValue()/etc. caller
   keeps working unchanged) and builds this UI next to it instead.

   The trigger sits inline in the form; the panel itself is rendered as
   position:fixed and appended to <body> (see _openCollegeCombo() in
   college-list.js) rather than living inside .college-combo, so it
   isn't clipped by ancestors with overflow:hidden (e.g. .auth-box) and
   always matches the trigger's width exactly regardless of which form
   it's used on.
   ------------------------------------------------------------ */
.college-combo {
	position: relative;
}

.college-combo-trigger {
	width: 100%;
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 8px;
	background: var(--bg);
	border: 1px solid var(--border);
	color: var(--text);
	border-radius: var(--r);
	padding: 9px 13px;
	font-size: 14px;
	font-family: var(--sans);
	text-align: left;
	cursor: pointer;
}

.college-combo-trigger:focus,
.college-combo.is-open .college-combo-trigger {
	border-color: var(--accent);
	box-shadow: 0 0 0 3px rgba(124, 106, 247, .14);
	outline: none;
}

.college-combo-label {
	flex: 1;
	min-width: 0;
	white-space: normal;
	overflow-wrap: break-word;
	line-height: 1.3;
}

.college-combo-label.is-placeholder {
	color: grey;
}

.college-combo-caret {
	flex-shrink: 0;
	margin-top: 2px;
	color: var(--muted);
	transition: transform .15s;
}

.college-combo.is-open .college-combo-caret {
	transform: rotate(180deg);
}

.college-combo-panel {
	position: fixed;
	display: flex;
	flex-direction: column;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--r);
	box-shadow: 0 12px 32px rgba(0, 0, 0, .45);
	z-index: 3000;
	overflow: hidden;
}

.college-combo-search {
	margin: 8px 8px 4px;
	width: auto;
}

.college-combo-list {
	overflow-y: auto;
	padding: 4px;   /* equal inset on all four sides, like every other dropdown list */
}

.college-combo-option {
	border-radius: max(0px, calc(var(--r, 8px) - 4px));
	padding: 9px 13px;
	font-size: 13.5px;
	line-height: 1.35;
	color: var(--text);
	cursor: pointer;
	white-space: normal;
	overflow-wrap: break-word;
}

.college-combo-option:hover,
.college-combo-option.is-active {
	background: rgba(124, 106, 247, .14);
}

.college-combo-option.is-other {
	color: grey;
	/* font-style: italic; */
	border-top: 1px solid var(--border);
	/* margin-top: 2px; */
}

.college-combo-empty {
	padding: 14px 13px;
	font-size: 13px;
	color: var(--muted);
	text-align: center;
}

textarea {
	resize: vertical;
	font-family: var(--mono);
	font-size: 13px;
	line-height: 1.6;
}

.dur-row {
	display: flex;
	gap: 8px;
}

.dur-row input[type="number"] {
	flex: 1;
}

.dur-row select {
	width: 90px;
	flex-shrink: 0;
}

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	padding: 8px 16px;
	border-radius: var(--r);
	font-size: 13px;
	font-weight: 600;
	font-family: var(--sans);
	border: 1px solid var(--border);
	background: var(--surf2);
	color: var(--text);
	cursor: pointer;
	transition: all .14s;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

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

.btn.primary {
	background: var(--surf2);
	border-color: var(--border);
	color: var(--text);
}

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

.btn.ok {
	background: var(--ok);
	border-color: var(--ok);
	color: #0a1e1a;
}

.btn.danger {
	background: transparent;
	border-color: var(--err);
	color: var(--err);
}

.btn.danger:hover {
	background: rgba(232, 87, 106, .1);
}

.btn.sm {
	padding: 5px 10px;
	font-size: 12px;
	border-radius: 6px;
}

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

.btn.ghost:hover {
	background: var(--surf2);
	color: var(--text);
}

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

.btn-row {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
	margin-top: 16px;
}

.btn-block {
	width: 100%;
	justify-content: center;
}

.tabs {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(108px, 1fr));
	gap: 8px 10px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 6px;
	margin-bottom: 20px;
}

.tab-btn {
	width: 100%;
	padding: 7px 12px;
	border: none;
	background: none;
	color: var(--muted);
	cursor: pointer;
	font-size: clamp(11px, 2.5vw, 12px);
	font-weight: 600;
	font-family: var(--sans);
	border-radius: 7px;
	transition: all .18s;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	text-align: center;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.tabs.tracker-tabs {
	grid-template-columns: repeat(5, 1fr);
	gap: 8px 4px;
}

.tracker-tabs .tab-btn {
	padding: 7px 4px;
}

@media (max-width: 400px) {
	.tracker-tabs .tab-btn {
		font-size: 10px;
	}
}

.tab-btn.active {
	background: var(--surf2);
	color: var(--text);
}

.tab-btn:hover:not(.active) {
	color: var(--text);
}

.tab-panel {
	display: none;
}

.tab-panel.active {
	display: block;
}

.notice-topbar, .holiday-topbar, .result-topbar, .club-chat-header, .notice-pdf-topbar, .result-pdf-topbar {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 10px 14px;
	margin-bottom: 14px;
	background: rgba(13,13,15,.92);
	backdrop-filter: blur(10px);
	-webkit-backdrop-filter: blur(10px);
	border-bottom: 1px solid var(--border);
	border-radius: 0px;
}

.notice-topbar-title, .holiday-topbar-title, .result-topbar-title{
	font-size: 16px;
	font-weight: 700;
	color: var(--text);
	flex: 1;
	text-align: center;
	margin-left: -75px;
	pointer-events: none;
}

.notice-pdf-topbar, .result-pdf-topbar {
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: center;
}

.notice-pdf-title, .result-pdf-title {
	font-size: 16px;
	font-weight: 700;
	color: var(--text);
	text-align: center;
	line-height: 1.3;
	min-width: 0;
	overflow-wrap: break-word;
}

.notice-refresh-btn, .result-refresh-btn {
	display: inline-flex;
	align-items: center;
	gap: 5px;
}

.notice-refresh-btn.spinning svg, .result-refresh-btn.spinning svg {
	animation: list-refresh-spin .8s linear infinite;
}

@keyframes list-refresh-spin { to { transform: rotate(360deg); } }

.notice-meta-row, .result-meta-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	margin-bottom: 10px;
	flex-wrap: wrap;
}

.notice-note, .result-note {
	font-size: 11.5px;
	color: var(--muted);
	line-height: 1.5;
}

.notice-updated, .result-updated {
	font-size: 10.5px;
	font-weight: 700;
	color: var(--muted);
	white-space: nowrap;
	display: inline-flex;
	align-items: center;
	gap: 4px;
}

.notice-updated.live, .result-updated.live { color: var(--ok); }

.notice-topbar-actions, .result-topbar-actions {
	display: flex;
	align-items: center;
	gap: 2px;
	flex-shrink: 0;
}

.holiday-pdf-wrap {
	position: relative;
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 260px;
	width: 100%;
	border: 1px solid var(--border);
	border-right: none;
	overflow: hidden;
	background: var(--surf2);
	margin-bottom: 12px;
}

.holiday-pdf-frame {
	width: 100%;
	flex: 1;
	min-height: 0;
	border: none;
	display: block;
}

.holiday-pdf-fallback, .notice-board-link, .notice-pdf-fallback,
.result-board-link, .result-pdf-fallback {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	flex-shrink: 0;
	color: var(--accent);
	text-decoration: none;
	border-radius: 8px;
	transition: background .12s;
}

.holiday-pdf-fallback:hover, .notice-board-link:hover, .notice-pdf-fallback:hover,
.result-board-link:hover, .result-pdf-fallback:hover { background: rgba(255,255,255,.06); }

.notice-pdf-section, .result-pdf-section {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
}

.notice-pdf-wrap, .result-pdf-wrap {
	position: relative;
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
	width: 100%;
	border: 1px solid var(--border);
	border-right: none;
	border-radius: 10px 0 0 10px;
	overflow: hidden;
	background: var(--surf2);
}

.notice-pdf-frame, .result-pdf-frame {
	width: 100%;
	flex: 1;
	min-height: 0;
	border: none;
	display: block;
}

.notice-pdf-loading, .holiday-pdf-loading, .result-pdf-loading {
	position: absolute;
	inset: 0;
	z-index: 2;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 10px;
	background: var(--surf2);
}

.notice-pdf-loading-ring, .holiday-pdf-loading-ring, .result-pdf-loading-ring {
	--pct: 0;
	width: 52px;
	height: 52px;
	border-radius: 50%;
	background: conic-gradient(var(--accent) calc(var(--pct) * 1%), var(--border) 0);
	-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 5px), #000 calc(100% - 5px));
			mask: radial-gradient(farthest-side, transparent calc(100% - 5px), #000 calc(100% - 5px));
	transition: background .15s linear;
}

.notice-pdf-loading-ring.indeterminate, .holiday-pdf-loading-ring.indeterminate, .result-pdf-loading-ring.indeterminate {
	background: conic-gradient(var(--accent) 0deg 90deg, var(--border) 90deg 360deg);
	animation: list-refresh-spin 1s linear infinite;
}

.notice-pdf-loading-pct, .holiday-pdf-loading-pct, .result-pdf-loading-pct {
	font-size: 11.5px;
	font-weight: 700;
	color: var(--muted);
}

.result-tabs {
	display: flex;
	gap: 2px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 3px;
	margin-bottom: 12px;
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
}

.result-tabs::-webkit-scrollbar { display: none; }

.result-tab {
	flex: 1;
	min-width: 0;
	font-size: clamp(11px, 2.5vw, 12px);
	font-weight: 600;
	font-family: var(--sans);
	color: var(--muted);
	background: none;
	border: none;
	border-radius: 7px;
	padding: 7px 10px;
	cursor: pointer;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	transition: all .18s;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.result-tab:hover:not(.active) { color: var(--text); }

.result-tab.active {
	background: var(--surf2);
	color: var(--text);
}

.field-hint {
	display: block;
	margin-top: 5px;
	font-size: 10.5px;
	line-height: 1.4;
	color: var(--muted);
}

/* Display name input itself stays left-aligned (like every other text
   field in the app); only its "N changes left" hint underneath is
   pulled right, so the remaining-changes count reads like a counter
   tucked under the field rather than body copy. */
/* Was .field-hint-right (right-aligned as one block) -- now the
   "N changes left" text and the "N/25 characters" count are two
   separate spans (see profileNameHint's markup in shell.js), sitting at
   the same overall spot but split left/right instead of both crammed
   into one right-aligned line. Each half keeps its own color -- see the
   two ids below -- rather than the line switching color together once
   the limit was reached. */
.profile-name-hint {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 8px;
}
#profileNameChangesHint {
	color: var(--warn);
}
#profileNameCountHint {
	color: var(--muted);
}

.batch-select-row {
	display: flex;
	align-items: center;
	gap: 8px;
}
.batch-select-row select {
	flex: 1;
	min-width: 0;
}
.batch-select-dash {
	color: var(--muted);
	font-weight: 700;
	flex-shrink: 0;
}

.notice-list, .result-list {
	display: flex;
	flex-direction: column;
}

.notice-item, .holiday-item, .result-item {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 7px 0;
	border-bottom: 1px solid var(--border);
}

.notice-item:last-child, .holiday-item:last-child, .result-item:last-child { border-bottom: none; }

.notice-item-date, .result-item-date {
	flex-shrink: 0;
	width: 54px;
	text-align: center;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 8px;
	padding: 5px 2px;
}

.notice-item-date .d-num, .result-item-date .d-num {
	display: block;
	font-size: 15px;
	font-weight: 700;
	color: var(--text);
	line-height: 1.1;
}

.notice-item-date .d-mon, .result-item-date .d-mon {
	display: block;
	font-size: 9.5px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .04em;
	color: var(--muted);
}

.notice-item-body, .result-item-body { flex: 1; min-width: 0; }

.notice-item-title, .result-item-title {
	font-size: 13px;
	font-weight: 600;
	color: var(--text);
	line-height: 1.4;
}

.notice-item-view, .result-item-view {
	flex-shrink: 0;
	font-size: 11.5px;
	font-weight: 700;
	color: var(--accent);
	text-decoration: none;
	white-space: nowrap;
	background: none;
	border: none;
	padding: 0;
	font-family: inherit;
	cursor: pointer;
}

.notice-item-view:hover, .result-item-view:hover { text-decoration: underline; }

.notice-item.is-new .notice-item-title { color: var(--text); }

.notice-item-badges {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	margin-left: 7px;
	vertical-align: middle;
}

.notice-badge-new, .notice-badge-batch {
	display: inline-flex;
	align-items: center;
	gap: 3px;
	font-size: 9px;
	font-weight: 800;
	letter-spacing: .04em;
	text-transform: uppercase;
	padding: 2px 6px;
	border-radius: 5px;
	white-space: nowrap;
}

.notice-badge-new {
	color: #fff;
	background: var(--accent, #7c6af7);
}
.notice-badge-new::before {
	content: '';
	width: 5px;
	height: 5px;
	border-radius: 50%;
	background: #fff;
	flex-shrink: 0;
}

.notice-badge-batch {
	color: var(--ok);
	background: rgba(64,201,168,.15);
	border: 1px solid rgba(64,201,168,.35);
}

.notice-badge-app {
	display: inline-flex;
	align-items: center;
	gap: 3px;
	font-size: 9px;
	font-weight: 800;
	letter-spacing: .04em;
	text-transform: uppercase;
	padding: 2px 6px;
	border-radius: 5px;
	white-space: nowrap;
	color: var(--a2, #e8934a);
	background: rgba(232,147,74,.15);
	border: 1px solid rgba(232,147,74,.35);
}

.notice-item.is-app-notice {
	background: rgba(232,147,74,.06);
	border-radius: 8px;
	padding-left: 8px;
	padding-right: 8px;
	margin: 0 -8px;
	border-bottom-color: transparent;
}

.notice-item.is-batch-match {
	background: rgba(64,201,168,.06);
	border-radius: 8px;
	padding-left: 8px;
	padding-right: 8px;
	margin: 0 -8px;
	border-bottom-color: transparent;
}

.notice-item-actions {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	gap: 10px;
}

.quicklink-card { position: relative; }
.quicklink-unread-dot {
	position: absolute;
	top: 10px;
	right: 10px;
	width: 9px;
	height: 9px;
	border-radius: 50%;
	background: var(--accent, #7c6af7);
	box-shadow: 0 0 0 2px var(--surface, #14141a);
	display: none;
}
.quicklink-card.has-unread .quicklink-unread-dot { display: block; }

.result-item-actions {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	gap: 6px;
}

.result-tab.disabled {
	opacity: .45;
	cursor: not-allowed;
}

.result-tab.disabled:hover { color: var(--muted); }

.syllabus-branch-grid {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 10px;
}

.syllabus-branch-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 9px;
	padding: 16px 8px;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 12px;
	cursor: pointer;
	transition: all .15s;
	-webkit-tap-highlight-color: transparent;
}

.syllabus-branch-card:hover {
	border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
	transform: translateY(-1px);
}

.syllabus-branch-card.active {
	border-color: var(--accent);
	background: color-mix(in srgb, var(--accent) 12%, var(--surf2));
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);
}

.syllabus-branch-icon {
	width: 44px;
	height: 44px;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	color: #fff;
	font-weight: 800;
	font-size: 11.5px;
	letter-spacing: .01em;
	flex-shrink: 0;
}

.syllabus-branch-label {
	font-size: 12px;
	font-weight: 600;
	color: var(--text);
	line-height: 1.3;
}

@keyframes itemEnterLTR {
	from {
		opacity: 0;
		transform: translateX(-18px);
	}
	to {
		opacity: 1;
		transform: translateX(0);
	}
}
.item-enter {
	animation: itemEnterLTR .32s cubic-bezier(.22, .68, .32, 1) both;
}
@media (prefers-reduced-motion: reduce) {
	.item-enter {
		animation: none;
	}
}

.card {
	margin-top: 14px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 0px;
	padding: clamp(14px, 3vw, 18px) clamp(14px, 4vw, 20px);
}

.card + .card {
	margin-top: 14px;
}

.card-title {
	font-size: 13px;
	font-weight: 700;
	margin-bottom: 14px;
	display: flex;
	align-items: center;
	gap: 8px;
	letter-spacing: .01em;
	text-transform: uppercase;
	color: var(--muted);
}

.card-title small {
	color: var(--muted);
	font-weight: 400;
	font-size: 11px;
	text-transform: none;
	letter-spacing: 0;
}

.ic-done { 
	background: transparent;
}

.ic-done svg { 
	stroke: var(--accent);
}

.ic-partial {
	background: transparent;
}

.ic-partial svg {
	stroke: var(--ok);
}

.ic-left {
	background: transparent;
}

.ic-left svg {
	stroke: var(--a2);
}

.ic-prog {
	background: transparent;
}

.ic-prog svg {
	stroke: var(--accent);
}

input[type="time"],
input[type="date"] {
	color-scheme: dark;
}

input[type="number"] {
	color-scheme: dark;
}

input[type="file"]::file-selector-button,
input[type="file"]::-webkit-file-upload-button {
	margin-right: 10px;
	padding: 7px 14px;
	border-radius: var(--r);
	font-size: 12px;
	font-weight: 600;
	font-family: var(--sans);
	border: 1px solid var(--border);
	background: var(--surf2);
	color: var(--text);
	cursor: pointer;
	transition: all .14s;
}

input[type="file"]::file-selector-button:hover,
input[type="file"]::-webkit-file-upload-button:hover {
	background: var(--border);
}

input[type="time"]::-webkit-calendar-picker-indicator,
input[type="date"]::-webkit-calendar-picker-indicator {
	filter: invert(52%) sepia(60%) saturate(500%) hue-rotate(212deg) brightness(105%);
	cursor: pointer;
	opacity: 1;
}

.auth-screen {
	position: fixed;
	inset: 0;
	z-index: 200;
	/* .auth-box below has a 390px min-width floor (same rationale as
	   .app's min-width) so it stops squishing on very narrow viewports.
	   Because this screen is position:fixed (unlike .app, which is in
	   normal flow), the html/body overflow-x:auto above can't reliably
	   reach content that overflows *it*, so this container scrolls
	   itself horizontally instead once .auth-box no longer fits. */
	overflow-x: auto;
	overflow-y: auto;
	/* Facebook-style split screen: .auth-promo (the video) sits on the
	   left, a hairline .auth-divider between it and the form, and the
	   form column stays centered in whatever space it has on the
	   right. Below the .auth-promo breakpoint this collapses back to
	   the old single centered-box layout. */
	display: flex;
	align-items: stretch;
	justify-content: center;
	/* No background-color here on purpose. .auth-screen is a fixed,
	   full-viewport layer sitting on top of <body> (z-index above), so
	   a solid color here would completely hide auth.php's own
	   page-specific background override (a flat var(--bg), no grid
	   pattern -- see the inline \3c style> in auth.php). Leaving this
	   transparent lets that flat color show through instead of being
	   painted over redundantly.
	   (This rule is scoped to auth.php only -- #authScreen doesn't exist
	   on any other page, so nothing else relies on it being opaque.) */
}

/* Logo now lives top-left of the whole screen, independent of which
   side the form/video land on -- see .auth-screen-logo in
   view-auth.js. Absolutely positioned against .auth-screen (its
   nearest positioned ancestor), so it's lifted out of the row-flex
   flow entirely and doesn't compete for space with .auth-promo/
   .auth-box-wrap. */
.auth-screen-logo {
	position: absolute;
	top: 24px;
	left: 24px;
	z-index: 1;
	margin-bottom: 0;
}

.auth-box-wrap {
	flex: 1 1 480px;
	display: flex;
	align-items: center;
	/* Was justify-content: center, then stretch -- now back to center,
	   but combined with .auth-box's max-width: 480px below the effect is
	   different this time: for widths under the cap the card still
	   fills 100% of whatever space this wrap's padding leaves (left
	   margin from the divider above 1024px / from the left edge below
	   it, right margin from the screen's right edge), but once this
	   wrap has more room than 480px + its own padding, the capped card
	   just centers in the middle of that space instead of hugging the
	   left/divider side, with equal margin on both sides. */
	justify-content: center;
	padding: 20px 80px;
	/* Extra room at the bottom for the now-always-present fixed
	   language row (.auth-promo-langs) pinned to the viewport bottom,
	   so it never overlaps/covers the bottom of the login card on
	   short viewports. */
	padding-bottom: 64px;
	min-width: 390px;
}

.auth-box {
	width: 100%;
	/* Caps how wide the card can stretch to on very wide screens --
	   without it, the removed max-width: 400px left nothing else to stop
	   the card from growing edge-to-edge inside .auth-box-wrap on a
	   large monitor. .auth-box-wrap's own justify-content: stretch above
	   only fills width up to this cap; once the card hits it, it just
	   sits at its left edge (nearest the divider) with the rest of the
	   wrap's space left empty on the right, instead of centering. */
	max-width: 480px;
	/* Floor to match .app's -- below this the form's inputs/buttons start
	   squishing and wrapping badly instead of the screen just scrolling
	   horizontally (see .auth-screen's overflow-x:auto above). Lifted on
	   phone-width screens by the max-width: 480px media query further
	   down, since this floor is exactly what was forcing a horizontal
	   scrollbar on common Android widths (360-412px) below it. */
	min-width: 390px;
	flex-shrink: 0;
	/* Background/border intentionally removed -- the card now sits
	   directly on the screen's own background instead of looking like a
	   raised panel. */
	border-radius: 14px;
	padding: 28px 26px 24px;
	overflow: hidden;
}

/* Left-hand promo panel with beuclub's own promo video, mute toggle
   and scramble-decode slogan (all positioned against this as their
   nearest positioned ancestor -- see the rules below). The language
   row used to live in here too; it's now a sibling fixed to the whole
   viewport bottom instead (see .auth-promo-langs further down) so it
   stays visible even when this panel is hidden below the breakpoint.
   The video is stretched (object-fit: fill) to exactly match the
   panel bounds rather than being letterboxed or cropped. Hidden below
   the breakpoint in the media query further down, exactly like
   Facebook drops its illustration once the viewport can't fit both
   columns. */
.auth-promo {
	position: relative;
	flex: 1 1 55%;
	min-width: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	overflow: hidden;
	/* Was a flat #000 -- with a multi-MB video loading, that showed as a
	   plain black rectangle for however long the download took. Now
	   matches .beu-player's own surface tone (see video-player.css),
	   which the player's poster/shimmer sits on top of, so there's
	   never a moment of flat, dead black. */
	background: var(--surface);
}

/* .auth-promo-video is now the .beu-player wrapper itself (see
   view-auth.js) -- assets/css/video-player.css handles its sizing.
   This just restores the one non-default look this panel wants: fill
   (not cover/contain) stretches the frame to exactly match the
   panel's bounds edge-to-edge, no cropping or letterboxing. The
   tradeoff is the frame can look slightly stretched if the panel's
   aspect ratio doesn't match the source video, which is the
   explicitly requested behavior here. */
.auth-promo-video.beu-player .beu-player-video {
	object-fit: fill;
}

/* Login/signup promo panel wants square corners, not the rounded
   corners .beu-player uses by default elsewhere (e.g. home banner
   slides) -- override just for this instance. */
.auth-promo-video.beu-player {
	border-radius: 0;
}

/* Mute toggle -- top-right corner of the promo panel. Sound starts ON
   by default (see _attemptAuthPromoAutoplayWithSound() in auth.js,
   which tries an unmuted autoplay first and only falls back to muted
   -- reconciling this button's state either way -- if the browser
   rejects that). This button is the manual override either direction.
   Mirrors the .auth-screen-logo pattern: absolutely positioned
   against .auth-promo so it never competes with the video for layout
   space. */
.auth-promo-mute-btn {
	position: absolute;
	top: 16px;
	right: 16px;
	z-index: 2;
	width: 34px;
	height: 34px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	border: 1px solid rgba(255, 255, 255, 0.25);
	background: rgba(13, 13, 15, 0.55);
	backdrop-filter: blur(4px);
	color: #fff;
	cursor: pointer;
	transition: background 0.15s ease, border-color 0.15s ease;
}

.auth-promo-mute-btn:hover {
	background: rgba(13, 13, 15, 0.8);
	border-color: rgba(255, 255, 255, 0.45);
}

/* Icon swap: only one of the two SVGs shows at a time, toggled by the
   [aria-pressed] state that toggleAuthPromoMute() flips in auth.js
   (aria-pressed="true" means audio is ON, i.e. no longer muted). */
.auth-promo-mute-btn .mute-icon-on { display: none; }
.auth-promo-mute-btn[aria-pressed="true"] .mute-icon-off { display: none; }
.auth-promo-mute-btn[aria-pressed="true"] .mute-icon-on { display: flex; }

/* Slogan, bottom-left with margin above the (now page-level) language
   row. One line per <span> -- see view-auth.js -- stacked in a column
   instead of a single wrapped block, so the line breaks are always
   "Connect with" / "the people" / "who inspire you." regardless of
   panel width. Sits on its own stacking layer above the video; a soft
   bottom-up gradient behind it (see .auth-promo::after) keeps it
   legible over bright footage. */
.auth-promo-slogan {
	position: absolute;
	left: 32px;
	right: 32px;
	bottom: 64px;
	z-index: 2;
	display: flex;
	flex-direction: column;
	font-family: var(--sans);
	font-weight: 800;
	font-size: clamp(22px, 2.6vw, 34px);
	line-height: 1.22;
	letter-spacing: -0.01em;
}

.auth-promo-slogan .slogan-line {
	display: block;
}

.auth-promo-slogan .slogan-base {
	color: #fff;
}

.auth-promo-slogan .slogan-accent {
	color: rgb(107, 91, 239);
}

/* Darkening gradient behind the slogan so it stays readable regardless
   of what's playing in the video underneath. */
.auth-promo::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 45%;
	z-index: 1;
	background: linear-gradient(to top, rgba(0, 0, 0, 0.65), rgba(0, 0, 0, 0));
	pointer-events: none;
}

/* Language row -- pinned to the bottom of the WHOLE viewport (not
   .auth-promo -- see view-auth.js, it's a sibling of .auth-promo now,
   not a child), so it stays visible on top of the login form itself
   once the video panel is hidden below the breakpoint instead of
   disappearing along with it. Solid background matching the rest of
   the auth screen (not an overlay-on-video treatment anymore, since
   it's no longer always sitting on top of the video). The dropdown
   panel below (.auth-promo-langs) is `position: fixed` and positions
   itself against its own trigger button in JS, not against this bar,
   so this is just the visible bar/footer row itself. */
.auth-lang-bar {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 3;
	border-top: 1px solid var(--border);
	background: var(--bg);
}

/* The language list is now a real dropdown, not an always-visible flat
   row: [hidden] (set on #authPromoLangs in view-auth.js) collapses it
   to nothing, and toggleAuthLangDropdown() in auth.js flips that
   attribute off when the footer's "English" button is pressed.

   Positioned relative to the VIEWPORT (position: fixed) with its
   left/top set in JS (see _positionAuthLangDropdown() in auth.js)
   from the trigger button's own getBoundingClientRect() each time it
   opens -- not a fixed `right: 24px` offset from the whole bottom
   bar. The bar spans the full screen width and the button inside it
   can end up anywhere along that row once .auth-lang-bar-footer wraps
   on narrow screens, so anchoring the panel to a static point on the
   bar meant it could open nowhere near the button that triggered it.
   Anchoring to the button's own rect keeps the panel visually
   attached to it regardless of where that ends up.

   TWO COLUMNS BY DEFAULT, STACKING TO ONE WHEN THEY DON'T FIT -- this
   used to be a single flex container with `flex-direction: column;
   flex-wrap: wrap;`, relying on a JS-computed pixel `height` (see
   _computeLangDropdownDims() in auth.js) to be *exactly* tall enough
   for ceil(count/2) rows so the browser's own wrapping happened to
   break into 2 columns. That's inherently a guess dressed up as a
   calculation: if that height computation ran before layout/fonts had
   settled, was clamped by _positionAuthLangDropdown()'s
   available-space check, or simply didn't match the real rendered row
   height for any reason, the browser would just as validly wrap into
   1 column (rows never ran out of vertical room) or 3+ (rows ran out
   early) -- same list, same code, different outcome per load. That's
   the "sometimes 1 column, sometimes 2" bug the two-column split
   below replaces.

   The column split itself is structural, not numerical: the language
   buttons are split into two explicit .auth-lang-col children
   directly in the markup (see #authPromoLangs in view-auth.js) --
   column A gets the first half of the list, column B gets the second
   half, always. But two *fixed-width* columns cannot themselves
   shrink to fit a narrow window -- an earlier attempt tried to force
   them to via an inline `max-width` from _positionAuthLangDropdown()
   in auth.js, and because flex children with a fixed width don't
   shrink, that only squeezed the two unshrinkable columns into a box
   too narrow for them, clipping language names off mid-word instead
   of fixing anything.

   So narrow viewports get a structural answer too: when the two
   columns' combined width would exceed the real available space,
   _positionAuthLangDropdown() adds `.is-stacked` (below), which
   switches this container to `flex-direction: column`. Column A then
   renders above column B instead of beside it, so every button keeps
   its own full fixed width and every name stays fully readable --
   the list just gets taller, which the existing vertical scroll
   (overflow-y below) already handles. */
.auth-promo-langs {
	position: fixed;
	/* Above .auth-lang-bar-footer (see below): both are plain children
	   of .auth-lang-bar with z-index:auto, so without this the footer
	   -- later in the DOM -- painted on top of this panel wherever the
	   two ever overlapped (short viewports / the footer wrapping onto
	   two lines pushed the button, and this panel's clamped position,
	   close enough to collide). Only needs to beat the footer's own
	   (auto) stacking, so it stays well under modal-tier z-indexes. */
	z-index: 2;
	display: flex;
	flex-direction: row;
	align-items: flex-start;
	gap: 0 12px;
	/* Safety net only, for very short viewports -- _positionAuthLangDropdown()
	   in auth.js also sets an inline max-height clamped to the real
	   space above the trigger button. overflow-y scrolls the column(s)
	   (rather than hidden, which used to rely on the height always
	   being exactly right) so nothing is ever silently clipped if a
	   screen is too short for the full list -- this is what absorbs
	   the extra height .is-stacked below produces on narrow screens. */
	max-height: min(420px, calc(100vh - 96px));
	overflow-y: auto;
	/* Belt-and-braces only: .is-stacked (below) is what actually keeps
	   names readable on narrow screens by dropping to one column. This
	   just stops a single column from clipping too, in the extreme
	   case of a viewport narrower than even one button
	   (--lang-item-width) plus this panel's own padding/border. */
	overflow-x: auto;
	scrollbar-width: none;      /* Firefox */
	-ms-overflow-style: none;   /* old Edge/IE */
	padding: 8px;
	border-radius: var(--r);
	border: 1px solid var(--border);
	background: var(--surface);
	box-shadow: 0 12px 32px rgba(0, 0, 0, .35);
}

/* Set by _positionAuthLangDropdown() in auth.js whenever the two
   fixed-width columns wouldn't both fit in the real available
   viewport width. Column B drops underneath column A instead of
   beside it -- every button keeps its full, unshrunk width (see
   .auth-lang-col / .auth-promo-lang-item below) so no language name
   is ever clipped, no matter how narrow the window gets. The taller
   single-column result is handled by the existing overflow-y scroll
   above, not by anything new. */
.auth-promo-langs.is-stacked {
	flex-direction: column;
	gap: 4px 0;
}

.auth-promo-langs::-webkit-scrollbar {
	display: none; /* Chrome/Safari/Edge */
}

.auth-promo-langs[hidden] {
	display: none;
}

/* One of the two hard-split halves of the language list -- see the
   comment on .auth-promo-langs above. Each column just stacks its own
   buttons top-to-bottom; the two sit side by side because the parent
   is a plain flex row. */
.auth-lang-col {
	display: flex;
	flex-direction: column;
	/* Width pinned to the exact same value as its buttons (rather than
	   left to shrink-to-fit/auto) -- some engines recompute a flex
	   column's own auto width against the CURRENT available space in
	   its flex-row parent on every reflow (e.g. the resize listener in
	   auth.js repositioning the panel), which can hand back a number
	   larger than any child actually needs even though every button
	   inside is a fixed, unchanging width. Removing the "auto" entirely
	   removes whatever that's computed from. */
	width: var(--lang-item-width, 128px);
	flex: 0 0 auto;
}

.auth-promo-lang-item {
	display: block;
	/* Set from JS (_computeLangItemWidth() in auth.js) to the longest
	   language name in the list plus its own padding, so every button
	   -- in both columns -- lines up instead of each hugging its own
	   text. The px fallback only covers the instant before that JS
	   runs. */
	width: var(--lang-item-width, 128px);
	flex: 0 0 auto;
	background: none;
	border: none;
	border-radius: 6px;
	padding: 5px 8px;
	/* Without this, a <button>'s own default UA margin (present in
	   some browsers/engines even after a reset that only zeroes
	   padding/border) sits outside the `width` above -- margin never
	   counts toward an element's own box width, so it just pushes the
	   button's painted content past its column's fixed-width box.
	   Because overflow is visible by default, that spillover paints
	   straight past the panel's right edge too, WITHOUT enlarging the
	   panel's own getBoundingClientRect() -- which is exactly why
	   even measuring the panel's real rendered width in
	   _positionAuthLangDropdown() (see auth.js) didn't catch this:
	   the box was measured correctly, the leak was happening entirely
	   outside it. !important because this button also picks up any
	   later global `button` margin resets/overrides added to the
	   site -- this one has to win regardless of load order. */
	margin: 0 !important;
	font-family: var(--sans);
	font-size: 12px;
	color: var(--muted);
	cursor: pointer;
	text-align: left;
	white-space: nowrap;
}

.auth-promo-lang-item:hover {
	color: var(--text);
	background: var(--surf2);
}

/* Currently-selected language -- accent color + own tint so it reads
   as "active" rather than just another row. */
.auth-lang-btn.active {
	color: var(--accent, #1d9bf0);
	background: color-mix(in srgb, var(--accent) 12%, transparent);
	font-weight: 600;
}

/* Bottom-of-bar footer inside .auth-lang-bar -- copyright + legal
   links on the left, color-changer + current-language dropdown trigger
   on the right. Sits under .auth-promo-langs, its own top border so it
   reads as a distinct row rather than just more of the language list. */
.auth-lang-bar-footer {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: nowrap;
	gap: clamp(4px, 1.6vw, 20px);
	padding: 10px clamp(10px, 4vw, 24px) 12px;
	border-top: 1px solid var(--border);
	font-size: clamp(9px, 2.6vw, 11px);
	color: var(--muted);
}

.auth-footer-copy {
	white-space: nowrap;
	flex-shrink: 0;
}

.auth-footer-links {
	display: flex;
	gap: clamp(5px, 1.8vw, 14px);
	flex-wrap: nowrap;
	min-width: 0;
}

.auth-footer-links a {
	white-space: nowrap;
}

.auth-footer-links a {
	color: var(--muted);
	text-decoration: none;
}

.auth-footer-links a:hover {
	color: var(--text);
}

/* Dark/light color-changer -- sits just left of the language dropdown
   trigger in the footer row. Only one of the two icons is shown at a
   time; toggleAuthColorTheme() in auth.js flips the .is-light class
   (and the html[data-auth-theme] attribute the actual colors key off
   of) together so the icon always matches the palette on screen. */
.auth-theme-toggle-btn {
	display: flex;
	align-items: center;
	justify-content: center;
	width: clamp(20px, 6vw, 26px);
	height: clamp(20px, 6vw, 26px);
	margin-left: auto;
	flex-shrink: 0;
	background: none;
	border: 1px solid var(--border);
	border-radius: 50%;
	color: var(--muted);
	cursor: pointer;
}

.auth-theme-toggle-btn svg {
	width: clamp(11px, 3vw, 14px);
	height: clamp(11px, 3vw, 14px);
}

.auth-theme-toggle-btn:hover {
	color: var(--text);
}

.auth-theme-toggle-btn .theme-icon-moon { display: block; }
.auth-theme-toggle-btn .theme-icon-sun { display: none; }
.auth-theme-toggle-btn.is-light .theme-icon-moon { display: none; }
.auth-theme-toggle-btn.is-light .theme-icon-sun { display: block; }

.auth-footer-lang-btn {
	display: flex;
	align-items: center;
	gap: 5px;
	flex-shrink: 0;
	white-space: nowrap;
	background: none;
	border: none;
	padding: 2px 0;
	font-family: var(--sans);
	font-size: inherit;
	color: var(--muted);
	cursor: pointer;
}

.auth-footer-lang-btn:hover {
	color: var(--text);
}

/* Chevron starts pointing down (list closed, sitting above the
   button); toggleAuthLangDropdown() adds .is-open when the dropdown is
   expanded, flipping it to point up toward the now-visible list. */
.auth-footer-lang-chevron {
	transition: transform .15s ease;
}

.auth-footer-lang-btn.is-open .auth-footer-lang-chevron {
	transform: rotate(180deg);
}

/* "Switching languages" overlay -- shown for a brief minimum stretch
   (see showAuthLangSwitchOverlay()/hideAuthLangSwitchOverlay() in
   auth.js) any time setAuthLanguage() fires. Fixed over the WHOLE
   viewport (not just the language bar it lives in -- .auth-lang-bar
   itself has no transform, so this fixed child still positions
   against the real viewport) with a blurred backdrop, so the entire
   screen behind the spinner reads as softly out-of-focus rather than
   just covering the dropdown panel. */
.auth-lang-switch-overlay {
	position: fixed;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	background: color-mix(in srgb, var(--bg) 45%, transparent);
	backdrop-filter: blur(10px);
	-webkit-backdrop-filter: blur(10px);
	z-index: 2000;
}

/* Small plain spinner (no logo) -- just a rotating ring in the accent
   color. Reuses the pyq-spin keyframe so it rotates at the same speed
   as every other spinner in the app. */
.beu-brand-spinner {
	display: block;
	width: 28px;
	height: 28px;
	border-radius: 50%;
	border: 3px solid color-mix(in srgb, var(--accent) 22%, transparent);
	border-top-color: var(--accent);
	animation: pyq-spin .8s linear infinite;
}

/* ---------- Basic RTL support (Urdu / Arabic) ----------
   AUTH_I18N.applyToDom() (auth-i18n.js) sets dir="rtl" on #authScreen
   itself when the selected language's JSON entry says dir:"rtl", which
   flips paragraph/label text alignment below. This is intentionally
   modest -- it does not mirror the two-column layout or icon
   positions, just the direction text actually reads in, which is the
   part that matters most for comprehension. Form inputs are pinned
   back to LTR regardless, since email addresses, passwords and OTP
   digits are always typed left-to-right even inside an RTL page. */
.auth-screen[dir="rtl"] .field label,
.auth-screen[dir="rtl"] .auth-hint,
.auth-screen[dir="rtl"] .auth-subtitle,
.auth-screen[dir="rtl"] .auth-title,
.auth-screen[dir="rtl"] .auth-switch,
.auth-screen[dir="rtl"] .form-error {
	text-align: right;
}

.auth-screen[dir="rtl"] input[type="email"],
.auth-screen[dir="rtl"] input[type="password"],
.auth-screen[dir="rtl"] input[inputmode="numeric"] {
	direction: ltr;
	text-align: left;
}

/* Hairline vertical rule between the promo panel and the form, same
   idea as .divider elsewhere in the app just oriented vertically and
   stretched to the screen's full height. */
.auth-divider {
	width: 1px;
	flex-shrink: 0;
	background: var(--border);
}

@media (max-width: 1024px) {
	.auth-promo,
	.auth-divider {
		display: none;
	}
	.auth-box-wrap {
		flex: 1 1 auto;
		/* No divider/promo panel on this side anymore, so the margin
		   collapses down to a simple left/right edge margin instead of
		   the wider "margin from the divider" spacing used above the
		   breakpoint. */
		padding-left: 40px;
		padding-right: 40px;
	}
}

/* Phone-width override: the 390px min-width floor on .auth-box/
   .auth-box-wrap (kept above so the card doesn't squish on tablet/
   desktop-narrow windows) is wider than most Android phones
   (360-412px viewport widths are common), and was exactly what forced
   auth.php into a horizontal scrollbar there. Below this breakpoint the
   floor is dropped in favor of just shrinking to fit the viewport, with
   smaller side padding to match. */
@media (max-width: 480px) {
	.auth-box-wrap {
		min-width: 0;
		padding-left: 24px;
		padding-right: 24px;
	}
	.auth-box {
		min-width: 0;
	}
}

.auth-brand {
	justify-content: center;
	margin-bottom: 20px;
	font-weight: 700;
	font-size: 15px;
	display: flex;
	align-items: center;
	gap: 8px;
}

.auth-title {
	text-align: left;
	font-size: 22px;
	font-weight: 700;
	margin-bottom: 6px;
}
/* heading row: [round back arrow] + title (the arrow is only shown on the forgot / reset / sign-up screens) */
.auth-title-row { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.auth-title-row .auth-title { margin-bottom: 0; flex: 1; min-width: 0; }
.auth-title-row .auth-title-back { margin-left: -10px; }
.auth-screen[dir="rtl"] .auth-title-back svg { transform: scaleX(-1); }

/* Sits directly under .auth-title for the login/signup step only --
   the OTP/photo/forgot/reset steps already carry their own inline
   .auth-hint copy, so setAuthStep() in auth.js hides this one whenever
   a step other than "details" is showing (see there). */
.auth-subtitle {
	text-align: left;
	font-size: 13.5px;
	color: var(--muted);
	line-height: 1.5;
	margin-bottom: 18px;
}

/* Larger, heavier logo mark on the login/signup screen now that it's
   standing alone without the "beuclub" wordmark next to it (see
   view-auth.js) -- scoped to .auth-brand so the header/footer/sponsor-
   modal .brand-mark elsewhere stay their normal size. */
.auth-brand .brand-mark {
	width: clamp(39px, 7.5vw, 48px);
	height: clamp(39px, 7.5vw, 48px);
	border-radius: 14px;
}

.auth-form {
	margin-top: 6px;
}

.auth-submit {
	width: 100%;
	justify-content: center;
}

.auth-hint {
	font-size: 12.5px;
	color: var(--muted);
	line-height: 1.5;
	margin: 0 0 14px;
	text-align: left;
}

/* Small-print consent line sitting right under the login/signup submit
   button (see .auth-form + this in view-auth.js's "details" step). */
.auth-consent-note {
	font-size: 11.5px;
	color: var(--muted);
	line-height: 1.5;
	text-align: center;
	margin: 12px 0 4px;
}

.auth-consent-note a {
	color: var(--muted);
	text-decoration: underline;
}

.auth-consent-note a:hover {
	color: var(--text);
}

/* ---------- Social login (Google) ----------
   Sits above the email/password form on the login+signup ("details")
   step only -- see #authSocialRow's placement in view-auth.js and
   setAuthStep() in auth.js, which hides it on every other step (OTP,
   photo, forgot, reset) the same way #authSubtitle already is. */
.auth-social-row {
	display: flex;
	flex-direction: column;
	gap: 10px;
	margin-bottom: 16px;
}

.social-btn {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	width: 100%;
	height: 44px;
	border-radius: 999px;
	border: 1px solid transparent;
	background: #fff;
	color: #3c4043;
	font-family: var(--sans);
	font-size: 14.5px;
	font-weight: 600;
	cursor: pointer;
	transition: filter .15s ease, transform .1s ease;
}

.social-btn:hover {
	filter: brightness(0.96);
}

.social-btn:active {
	transform: scale(0.98);
}

.social-btn svg {
	width: 20px;
	height: 20px;
	flex-shrink: 0;
}

.social-btn.google {
	border-color: #dadce0;
}

.social-btn[disabled] {
	opacity: 0.6;
	cursor: default;
	pointer-events: none;
}

.auth-social-or {
	display: flex;
	align-items: center;
	gap: 10px;
	margin: 4px 0 16px;
	color: var(--muted);
	font-size: 12.5px;
}

.auth-social-or::before,
.auth-social-or::after {
	content: '';
	flex: 1;
	height: 1px;
	background: var(--border);
}

.auth-switch {
	text-align: center;
	margin-top: 16px;
	font-size: 12px;
	color: var(--muted);
}

.auth-switch-btn {
	background: none;
	border: none;
	color: var(--accent);
	font-weight: 600;
	font-size: 12px;
	cursor: pointer;
	margin-left: 4px;
}

.form-error {
	background: rgba(232, 87, 106, .1);
	border: 1px solid rgba(232, 87, 106, .3);
	color: var(--err);
	border-radius: 8px;
	padding: 8px 11px;
	font-size: 12px;
	margin-bottom: 12px;
}

/* ============================================================
   14-step signup wizard (#authSignupWizard in view-auth.js) --
   see assets/js/auth.js's signupWizard* functions.
   ============================================================ */
.signup-progress {
	margin-bottom: 18px;
}

.signup-progress-label {
	font-size: 11.5px;
	font-weight: 600;
	color: var(--muted);
	margin-bottom: 6px;
	letter-spacing: .02em;
}

.signup-progress-track {
	width: 100%;
	height: 5px;
	border-radius: 999px;
	background: var(--border);
	overflow: hidden;
}

.signup-progress-fill {
	height: 100%;
	background: var(--accent);
	border-radius: 999px;
	transition: width .25s ease;
}

.signup-step-title {
	font-size: 19px;
	font-weight: 700;
	color: var(--text);
	margin-bottom: 4px;
}

.signup-hint-sm {
	font-size: 11px;
	margin-top: -6px;
}

.req {
	color: var(--err);
}

.signup-pw-checklist {
	display: flex;
	flex-direction: column;
	gap: 4px;
	margin: 4px 0 14px;
}

.pw-check {
	font-size: 11.5px;
	color: var(--muted);
	padding-left: 18px;
	position: relative;
}

.pw-check::before {
	content: '';
	position: absolute;
	left: 0;
	top: 4px;
	width: 12px;
	height: 12px;
	border-radius: 50%;
	border: 1.5px solid var(--muted);
}

.pw-check.ok {
	color: var(--ok);
}

.pw-check.ok::before {
	border-color: var(--ok);
	background: var(--ok);
}

/* ---- Signup wizard step 4: username field, availability status, suggestions ---- */
.username-input {
	display: flex;
	align-items: center;
	gap: 8px;
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: var(--r);
	padding: 0 12px;
	transition: border-color .18s, box-shadow .18s;
}
.username-input:focus-within {
	border-color: var(--accent);
	box-shadow: 0 0 0 3px rgba(124, 106, 247, .14);
}
.username-input.is-ok { border-color: var(--ok); }
.username-input.is-ok:focus-within { box-shadow: 0 0 0 3px rgba(64, 201, 168, .16); }
.username-input.is-bad { border-color: var(--err); }
.username-input.is-bad:focus-within { box-shadow: 0 0 0 3px rgba(232, 87, 106, .16); }
.username-input input,
.username-input input:focus {
	flex: 1;
	min-width: 0;
	border: 0;
	background: transparent;
	box-shadow: none;
	padding: 9px 0;
}
.username-at {
	color: var(--muted);
	font-size: 14px;
	font-weight: 600;
	user-select: none;
}
.username-state {
	flex: none;
	width: 20px;
	height: 20px;
	border-radius: 50%;
	display: none;
	align-items: center;
	justify-content: center;
	font-size: 12px;
	font-weight: 700;
	line-height: 1;
	color: #fff;
}
.username-state.ok { display: flex; background: var(--ok); }
.username-state.ok::before { content: '\2713'; }
.username-state.bad { display: flex; background: var(--err); }
.username-state.bad::before { content: '!'; }
.username-state.busy {
	display: block;
	border: 2px solid var(--border);
	border-top-color: var(--accent);
	animation: username-spin .7s linear infinite;
}
@keyframes username-spin { to { transform: rotate(360deg); } }
.username-status {
	font-size: 12.5px;
	margin-top: 7px;
	color: var(--muted);
}
.username-status.is-ok { color: var(--ok); font-weight: 600; }
.username-status.is-bad { color: var(--err); }
.username-suggest { margin: 2px 0 14px; }
.username-suggest-title {
	font-size: 12.5px;
	color: var(--muted);
	margin-bottom: 8px;
}
.username-suggest-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.username-suggest-item {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	text-align: left;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: var(--r);
	padding: 9px 12px;
	color: var(--text);
	font-size: 14px;
	cursor: pointer;
	transition: border-color .15s, background .15s;
}
.username-suggest-item:hover,
.username-suggest-item:focus-visible { border-color: var(--accent); }
.username-suggest-ico { display: flex; color: var(--muted); }
.username-suggest-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.username-suggest-plus {
	flex: none;
	width: 20px;
	height: 20px;
	border-radius: 50%;
	background: var(--accent);
	color: #fff;
	font-size: 15px;
	line-height: 20px;
	text-align: center;
}
@media (prefers-reduced-motion: reduce) { .username-state.busy { animation-duration: 1.6s; } }

.option-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 10px;
	margin-bottom: 4px;
}

.option-grid-2col {
	grid-template-columns: 1fr 1fr;
}

.option-card {
	text-align: left;
	background: var(--surface);
	border: 1.5px solid var(--border);
	border-radius: var(--r);
	padding: 12px 14px;
	cursor: pointer;
	color: var(--text);
	font-family: var(--sans);
	transition: border-color .15s ease, background .15s ease;
}

.option-card:hover {
	border-color: var(--accent-soft);
}

.option-card.selected {
	border-color: var(--accent);
	background: rgba(124, 106, 247, .1);
}

.option-card-title {
	font-weight: 700;
	font-size: 14px;
}

.option-card-sub {
	font-size: 11.5px;
	color: var(--muted);
	margin-top: 2px;
}

.chip-row {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-bottom: 4px;
}

.chip {
	background: var(--surface);
	border: 1.5px solid var(--border);
	border-radius: 999px;
	padding: 7px 14px;
	font-size: 12.5px;
	color: var(--text);
	cursor: pointer;
	font-family: var(--sans);
	transition: border-color .15s ease, background .15s ease, color .15s ease;
}

.chip:hover {
	border-color: var(--accent-soft);
}

.chip.active {
	border-color: var(--accent);
	background: var(--accent);
	color: #fff;
}

/* "Tell us about yourself" (signup step 12, #signupBio) -- auto-grows
   via _updateSignupBioCounter() in auth.js instead of scrolling
   internally, so no resize handle or scrollbar is needed here; 160
   characters (the field's maxlength) fits comfortably within a few
   grown lines at this modal's width. */
#signupBio {
	resize: none;
	overflow-y: hidden;
	min-height: 84px;
}

.signup-bio-counter {
	text-align: right;
	font-size: 11px;
	color: var(--muted);
	margin-top: 4px;

}

.people-suggest-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
	max-height: 320px;
	overflow-y: auto;
	margin-bottom: 4px;
}

.people-suggest-loading, .people-suggest-empty {
	text-align: center;
	color: var(--muted);
	font-size: 12.5px;
	padding: 18px 0;
}

.people-suggest-item {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 8px 10px;
	border: 1px solid var(--border);
	border-radius: var(--r);
}

.people-suggest-info {
	flex: 1;
	min-width: 0;
}

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

.people-suggest-handle {
	font-size: 11px;
	color: var(--muted);
}

.people-suggest-follow-btn {
	background: var(--accent);
	color: #fff;
	border: none;
	border-radius: 999px;
	padding: 6px 14px;
	font-size: 12px;
	font-weight: 600;
	cursor: pointer;
	flex-shrink: 0;
}

.people-suggest-follow-btn.following {
	background: var(--surface);
	color: var(--muted);
	border: 1px solid var(--border);
}

.club-checkbox-label {
	display: flex;
	align-items: center;
	gap: 8px;
	font-size: 13px;
	color: var(--text);
	cursor: pointer;
}
.club-checkbox-label input[type="checkbox"] {
	width: 16px;
	height: 16px;
	accent-color: var(--accent);
	cursor: pointer;
}

.cf-turnstile {
	width: 100%;
	max-width: 100%;
	min-width: 0;
	margin-bottom: 12px;
	overflow: hidden;
	position: relative;
	contain: layout;
	box-sizing: border-box;
}
.cf-turnstile iframe {
	display: block;
	width: 100% !important;
	max-width: 100% !important;
	position: relative !important;
}

.turnstile-status {
	text-align: center;
	font-size: 12px;
	color: var(--muted);
	margin: -6px 0 12px;
}
.turnstile-status.is-error {
	color: var(--err);
	cursor: pointer;
	text-decoration: underline;
}

.password-wrap {
	position: relative;
}

.password-wrap input {
	padding-right: 40px;
}

.pw-toggle {
	position: absolute;
	right: 4px;
	top: 50%;
	transform: translateY(-50%);
	width: 30px;
	height: 30px;
	display: flex;
	align-items: center;
	justify-content: center;
	background: none;
	border: none;
	border-radius: 6px;
	color: var(--muted);
	cursor: pointer;
	padding: 0;
}

.pw-toggle:hover {
	color: var(--text);
	background: var(--surf2);
}

/* The global `button:active { transform: scale(0.96) }` rule above
   replaces (not combines with) this button's own centering
   `transform: translateY(-50%)`, so pressing it snapped the icon
   down out of its vertical-center position instead of just
   shrinking in place. Re-declare both transform functions together
   here so the press keeps the translateY centering and only adds
   the scale. */
.pw-toggle:active {
	transform: translateY(-50%) scale(0.9);
}

.pw-toggle .pw-eye-off {
	display: none;
}

.pw-toggle.is-visible .pw-eye {
	display: none;
}

.pw-toggle.is-visible .pw-eye-off {
	display: block;
}

.msgr-avatar {
	flex-shrink: 0;
	border-radius: 50%;
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--surf2);
	border: 1px solid var(--border);
	font-weight: 800;
	color: var(--accent);
	line-height: 1;
}

.msgr-avatar img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.msgr-avatar.initials-avatar {
	color: #fff;
}

.header-avatar-btn {
	width: 34px;
	height: 34px;
	border-radius: 50%;
	border: 1px solid var(--border);
	background: var(--surf2);
	cursor: pointer;
	padding: 0;
	overflow: hidden; /* clips .header-avatar-btn:empty's shimmer sweep to the circle */
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	position: relative;
}
.header-avatar-btn:empty {
	background: var(--surf2) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236868a0' stroke-width='2'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4 20c0-4 4-6 8-6s8 2 8 6'/%3E%3C/svg%3E") center/60% no-repeat;
}
/* Same compositor-only sweep as .date-btn.loading (styles.css) -- shown
   while renderHeaderAvatar() hasn't filled the button in yet, so the
   header's avatar slot reads as loading the same way the date pill does
   instead of just sitting there as a bare placeholder icon. */
.header-avatar-btn:empty::after {
	content: '';
	position: absolute;
	inset: 0;
	transform: translateX(-120%);
	background: linear-gradient(100deg, transparent 30%, var(--surface) 50%, transparent 70%);
	animation: skel-sweep 1.6s ease-in-out infinite;
	will-change: transform;
	pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
	.header-avatar-btn:empty::after { animation: none; content: none; }
}
.header-avatar-btn img,
.header-avatar-btn .msgr-avatar {
	width: 100%;
	height: 100%;
	border: none;
	border-radius: 50%;
}
.profile-photo-row {
	display: flex;
	align-items: center;
	gap: 14px;
	margin-top: -6px;
}
.profile-photo-preview {
	width: 64px;
	height: 64px;
	flex-shrink: 0;
}
.profile-photo-preview .msgr-avatar {
	width: 64px;
	height: 64px;
	font-size: 30px;
}
.profile-photo-acts {
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.modal-overlay {
	position: fixed; inset: 0;
	background: rgba(0,0,0,.55);
	/* Matches .modal-bg's blur (Sponsor modal) so every full-screen
	   overlay -- Profile, image crop, image viewer -- blurs the app
	   behind it the same way instead of just dimming it. */
	backdrop-filter: blur(4px);
	-webkit-backdrop-filter: blur(4px);
	z-index: 1100;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* The avatar picker can be opened from on top of an already-open
   profile modal ("Or pick an avatar instead"). Both are .modal-overlay
   (z-index 1100), so without this the picker -- earlier in the DOM
   than #profileModal -- would render BEHIND it and look like nothing
   happened when tapped. One tier above the base modal z-index puts it
   reliably on top regardless of DOM order. */
.avatar-picker-overlay {
	z-index: 1150;
}
.modal-box {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 14px;
	padding: 24px;
	width: 340px;
	max-width: 95vw;
	display: flex;
	flex-direction: column;
	gap: 14px;
}
.modal-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
}

#cropModal {
	z-index: 1150;
}

.crop-modal-box {
	align-items: center;
}

.crop-stage {
	position: relative;
	width: 260px;
	height: 260px;
	max-width: 100%;
	border-radius: 50%;
	overflow: hidden;
	background: #000;
	margin: 0 auto;
	touch-action: none;
	cursor: grab;
	box-shadow: 0 0 0 2000px rgba(0,0,0,.35);
}

.crop-stage:active {
	cursor: grabbing;
}

/* Rectangular variant used when cropping to the home-banner aspect ratio
   (see imagecrop.js's openBannerCropper()) -- same drag/zoom stage as the
   circular avatar cropper, just square corners and a wide box instead of
   a circle, sized to match .banner-carousel's own 2.35/1 aspect-ratio so
   what the person frames here is exactly what will show on the banner. */
.crop-stage.crop-stage-rect {
	width: 329px;
	height: 140px;
	border-radius: 10px;
}

.crop-stage img {
	position: absolute;
	top: 0;
	left: 0;
	transform-origin: 0 0;
	user-select: none;
	-webkit-user-drag: none;
	pointer-events: none;
	max-width: none;
}

.crop-zoom-row {
	display: flex;
	align-items: center;
	gap: 8px;
	color: var(--muted);
	padding: 0 4px;
}

.crop-zoom-slider {
	flex: 1;
	-webkit-appearance: none;
	appearance: none;
	height: 4px;
	border-radius: 2px;
	background: var(--border);
	outline: none;
}

.crop-zoom-slider::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: var(--accent);
	cursor: pointer;
	border: 2px solid #fff;
}

.crop-zoom-slider::-moz-range-thumb {
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: var(--accent);
	cursor: pointer;
	border: 2px solid #fff;
}

/* Avatar picker (live animated emoji avatars) -- see avatarpicker.js.
   Reachable from both the mandatory signup photo step and the profile
   modal's "Change photo" row via an "Or choose an avatar" link.
   Replaces the old grid of ~10 illustrated .png files with a
   searchable grid of animated emoji (Smileys & Emotion only -- an
   avatar is a face) -- each tile plays a real looping GIF, not a
   static glyph. */
/* see assets/js/avatarpicker.js _avatarPickerGridHtml() */
.avatar-picker-modal-box {
	width: 460px;
	max-height: 82vh;
	overflow-y: auto;
}
.avatar-picker-search-row {
	position: sticky;
	top: 0;
	z-index: 2;
	display: flex;
	align-items: center;
	gap: 8px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 12px;
	padding: 9px 12px;
	color: var(--muted);
	margin-bottom: 10px;
}
.avatar-picker-search-row svg {
	flex-shrink: 0;
	opacity: .8;
}
.avatar-picker-search {
	flex: 1;
	border: none;
	background: none;
	outline: none;
	font-size: 14px;
	font-family: var(--sans);
	color: var(--text);
}
.avatar-picker-search::placeholder {
	color: var(--muted);
}
.avatar-picker-group-label {
	display: flex;
	align-items: center;
	gap: 6px;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: .05em;
	text-transform: uppercase;
	color: var(--accent-soft);
	margin: 16px 0 10px;
	padding-bottom: 8px;
	border-bottom: 1px solid var(--border);
}
.avatar-picker-group-label:first-child {
	margin-top: 0;
}
.avatar-picker-grid {
	display: grid;
	grid-template-columns: repeat(6, 1fr);
	gap: 8px;
}
.avatar-picker-empty {
	text-align: center;
	color: var(--muted);
	font-size: 13px;
	padding: 30px 10px;
}
.avatar-picker-item {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	aspect-ratio: 1;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 12px;
	cursor: pointer;
	padding: 0;
	overflow: hidden;
	transition: border-color .15s, transform .12s, background .15s, box-shadow .15s;
}
.avatar-picker-item img {
	width: 78%;
	height: 78%;
	object-fit: contain;
}
.avatar-picker-item:hover,
.avatar-picker-item:focus-visible {
	border-color: var(--accent);
	background: color-mix(in srgb, var(--accent) 10%, var(--surf2));
	transform: translateY(-2px) scale(1.06);
	box-shadow: 0 6px 18px rgba(0,0,0,.25), 0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
}
.avatar-picker-item:active {
	transform: translateY(0) scale(.95);
}
@media (max-width: 480px) {
	.avatar-picker-modal-box { width: 100%; }
	.avatar-picker-grid { grid-template-columns: repeat(5, 1fr); gap: 6px; }
}

/* Live animated-emoji avatar rendering everywhere an avatar shows up
   (header, profile modal, club chat/DM messages, member lists,
   comments, People tab) -- see avatarMarkup()/emojiAvatarImgHtml() in
   helpers.js and avatarHtml() in profile.js. Sits inside the same
   circular .msgr-avatar/.club-member-avatar etc. wrapper a photo
   avatar would, just with the animated GIF filling it instead of a
   plain <img> photo. */
.emoji-avatar,
.emoji-avatar-wrap {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	line-height: 1;
	background: var(--surf2);
	overflow: hidden;
}
.emoji-avatar-img {
	width: 100%;
	height: 100%;
	object-fit: contain;
}
.auth-avatar-alt {
	text-align: center;
	margin-top: 2px;
}
.auth-avatar-alt-btn {
	background: none;
	border: none;
	color: var(--accent-soft);
	font-size: 13px;
	cursor: pointer;
	padding: 4px;
	font-family: var(--sans);
}
.auth-avatar-alt-btn:hover {
	color: var(--accent);
	text-decoration: underline;
}

.auth-terms-field {
	margin-bottom: 12px;
}
.auth-terms-check {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	font-size: 12.5px;
	color: var(--muted);
	line-height: 1.5;
	cursor: pointer;
}
.auth-terms-check input[type="checkbox"] {
	appearance: none;
	-webkit-appearance: none;
	width: 16px;
	height: 16px;
	margin-top: 2px;
	padding: 0;
	box-sizing: border-box;
	border-radius: 4px;
	border: 1.5px solid var(--border);
	background: var(--surf2);
	flex-shrink: 0;
	cursor: pointer;
	position: relative;
	transition: background .15s ease, border-color .15s ease, transform .1s ease;
}
.auth-terms-check input[type="checkbox"]:hover {
	border-color: var(--accent-soft, var(--accent));
}
.auth-terms-check input[type="checkbox"]:checked {
	background: var(--accent);
	border-color: var(--accent);
}
.auth-terms-check input[type="checkbox"]:active {
	transform: scale(.88);
}
.auth-terms-link {
	background: none;
	border: none;
	color: var(--accent);
	font-weight: 600;
	font-size: 12.5px;
	cursor: pointer;
	padding: 0;
	text-decoration: underline;
	font-family: var(--sans);
}
.auth-terms-field.field-error .auth-terms-check {
	color: var(--err);
}

/* Animated tick shared by the auth Terms checkbox above and the Sponsor
   beuclub checkbox below (see .sponsor-terms-check input[type="checkbox"]
   further down) -- both are appearance:none custom boxes, so nothing
   draws a check mark on its own. Drawn as a small rotated "L" of two
   borders rather than an image/SVG. It scales + fades in from the
   center on check and reverses on uncheck, instead of the box just
   instantly flipping color with no mark at all. */
.auth-terms-check input[type="checkbox"]::after,
.sponsor-terms-check input[type="checkbox"]::after {
	content: '';
	position: absolute;
	left: 4px;
	top: 1px;
	width: 5px;
	height: 9px;
	border: solid #fff;
	border-width: 0 2px 2px 0;
	transform: rotate(45deg) scale(0);
	opacity: 0;
	transition: transform .18s cubic-bezier(.34, 1.56, .64, 1), opacity .12s ease;
}
.auth-terms-check input[type="checkbox"]:checked::after,
.sponsor-terms-check input[type="checkbox"]:checked::after {
	transform: rotate(45deg) scale(1);
	opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
	.auth-terms-check input[type="checkbox"],
	.sponsor-terms-check input[type="checkbox"],
	.auth-terms-check input[type="checkbox"]::after,
	.sponsor-terms-check input[type="checkbox"]::after {
		transition: none;
	}
}
.auth-terms-modal-box {
	width: 480px;
	max-width: 92vw;
	/* Same 390px floor as .auth-box/.auth-screen -- without it, at
	   narrow widths 92vw shrinks below what the text/buttons can lay
	   out in (see the squished screenshot this was reported from), long
	   before the rest of the page's own 390px floor kicks in, since this
	   modal is its own position:fixed overlay (#authTermsModal below)
	   rather than living inside .auth-screen. min-width beats max-width
	   here when they conflict, which is exactly the point. */
	min-width: 390px;
	flex-shrink: 0;
	max-height: 82vh;
	display: flex;
	flex-direction: column;
}
#authTermsModal {
	/* Lets the overlay itself scroll horizontally once auth-terms-modal-box's
	   390px floor above no longer fits, instead of clipping it or letting it
	   overflow the viewport with no way to reach the rest of it. */
	overflow-x: auto;
}
.auth-terms-modal-body {
	overflow-y: auto;
	padding-right: 4px;
	margin-bottom: 10px;
}
.auth-terms-modal-body .card-title:first-child {
	margin-top: 0;
}
.auth-terms-header-actions {
	display: flex;
	align-items: center;
	gap: 6px;
	flex-shrink: 0;
}
#authTermsListenBtn.is-speaking {
	color: var(--accent);
	border-color: color-mix(in srgb, var(--accent) 45%, transparent);
	background: color-mix(in srgb, var(--accent) 14%, transparent);
}
/* Colored + widened so it's obviously "there's more below" rather than
   blending into the default thin gray scrollbar (see html::-webkit-scrollbar
   above) that's easy to miss on a dark modal. */
.auth-terms-modal-body::-webkit-scrollbar {
	width: 10px;
}
.auth-terms-modal-body::-webkit-scrollbar-track {
	background: color-mix(in srgb, var(--accent) 10%, transparent);
	border-radius: 6px;
}
.auth-terms-modal-body::-webkit-scrollbar-thumb {
	background: var(--accent);
	border-radius: 6px;
	border: 2px solid var(--surface);
	background-clip: padding-box;
}
.auth-terms-modal-body::-webkit-scrollbar-thumb:hover {
	background: var(--accent-soft);
	background-clip: padding-box;
}
.auth-terms-modal-body {
	scrollbar-color: var(--accent) color-mix(in srgb, var(--accent) 10%, transparent);
}
.auth-terms-scroll-hint {
	font-size: 12px;
	font-weight: 600;
	color: var(--accent);
	background: color-mix(in srgb, var(--accent) 14%, transparent);
	border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
	border-radius: 8px;
	text-align: center;
	padding: 7px 10px;
	margin-bottom: 8px;
	animation: authTermsHintPulse 1.6s ease-in-out infinite;
}
@keyframes authTermsHintPulse {
	0%, 100% { opacity: 1; }
	50% { opacity: 0.55; }
}

.profile-logout-btn {
	background: var(--err);
	border-color: var(--err);
	color: #fff;
}
.profile-logout-btn:hover {
	background: #c23a4d;
	border-color: #c23a4d;
}

.chat-reply-preview {
	font-size: 11px;
	color: var(--muted);
	background: var(--surf2);
	border-left: 3px solid var(--accent);
	border-radius: 4px;
	padding: 4px 8px;
	margin-bottom: 4px;
	display: flex;
	gap: 6px;
	flex-wrap: wrap;
}
.chat-reply-name { color: var(--accent); font-weight: 600; }
.chat-edited { font-size: 10px; color: var(--muted); font-style: italic; }
.chat-deleted { color: var(--muted); font-style: italic; }
.chat-msg-menu-wrap {
	position: absolute;
	top: 4px;
	right: -6px;
	opacity: 0;
	transition: opacity .15s;
}
.chat-msg { position: relative; }
.chat-msg:hover .chat-msg-menu-wrap,
.chat-msg-menu-wrap.open { opacity: 1; }
.chat-msg-me .chat-msg-menu-wrap { right: auto; left: -6px; }
.chat-msg-menu-btn {
	width: 22px;
	height: 22px;
	border-radius: 50%;
	border: none;
	background: rgba(0,0,0,.35);
	color: var(--text);
	font-size: 13px;
	line-height: 1;
	cursor: pointer;
}
.chat-msg-menu-btn:hover { background: rgba(0,0,0,.55); }
.chat-msg-menu {
	position: absolute;
	top: 24px;
	right: 0;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 8px;
	padding: 4px;
	min-width: 110px;
	z-index: 8;
	box-shadow: 0 8px 20px rgba(0,0,0,.4);
}
.chat-msg-me .chat-msg-menu { right: auto; left: 0; }
.chat-msg-menu button {
	display: block;
	width: 100%;
	text-align: left;
	font-size: 12px;
	background: none;
	border: none;
	color: var(--text);
	padding: 6px 8px;
	border-radius: 5px;
	cursor: pointer;
}
.chat-msg-menu button:hover { background: var(--border); }

.compose-context {
	font-size: 12px;
	padding: 5px 10px;
	border-radius: 6px;
	display: flex;
	align-items: center;
	gap: 6px;
}
.compose-context.reply { background: rgba(124,106,247,.12); border-left: 3px solid var(--accent); }
.compose-context.edit  { background: rgba(255,200,0,.08);   border-left: 3px solid #f5c518; }
.compose-context button {
	background: none; border: none; cursor: pointer;
	color: var(--muted); font-size: 14px; margin-left: auto;
}

.app-view {
	display: none;
}

.app-view.active {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
}

/* The sweep used to be driven by animating `background-position` on
   .skel itself (a 300%-wide gradient sliding from 200% to -200%). That
   forces the browser to repaint the element every frame instead of just
   compositing it, which on a lot of devices (especially inside a Tauri
   webview, with no fully hardware-accelerated 2D canvas) shows up as a
   stuttery, uneven shimmer rather than a smooth sweep — worse the more
   skeleton rows are on screen at once, since each one repaints
   independently every frame. Moving the highlight onto its own ::after
   layer and animating `transform: translateX()` instead lets the
   compositor handle it (GPU, no repaint), which is both smoother and
   cheaper with many rows on screen at once. */
@keyframes skel-sweep {
	0%   { transform: translateX(-120%); }
	100% { transform: translateX(120%); }
}
.skel {
	position: relative;
	overflow: hidden;
	background: var(--surf2);
	border-radius: var(--r);
	pointer-events: none;
}
.skel::after {
	content: '';
	position: absolute;
	inset: 0;
	transform: translateX(-120%);
	background: linear-gradient(100deg, transparent 30%, var(--surface) 50%, transparent 70%);
	animation: skel-sweep 1.6s ease-in-out infinite;
	will-change: transform;
}
.skel-line {
	height: 11px;
	border-radius: 5px;
}
.skel-circle {
	border-radius: 50%;
	flex-shrink: 0;
}
@media (prefers-reduced-motion: reduce) {
	.skel::after { animation: none; content: none; }
}

.skel-banner {
	position: absolute;
	inset: 0;
}

/* Mirrors .quicklink-card's own box model (padding, flex gap) instead
   of a flat colored block, so the skeleton occupies exactly the same
   position/dimensions as the real card it's standing in for -- see
   SKEL_QUICKLINK_CARD in view-home.js for the markup this styles. */
.skel-quicklink {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--r);
	padding: 16px 10px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 10px;
}
.skel-quicklink-icon {
	width: 40px;
	height: 40px;
	border-radius: 10px;
	flex-shrink: 0;
}
.skel-quicklink-sub {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	width: 100%;
}

.skel-comment {
	display: flex;
	gap: 10px;
}
.skel-comment .skel-circle {
	width: 34px;
	height: 34px;
}
.skel-comment-col {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding-top: 3px;
}

/* Minimal flat skimmer -- one plain shimmering rounded-rect block per
   row instead of mirroring the real row's internals (icon circle + two
   text lines). The detailed version drifted out of sync with real rows
   any time their content changed (a badge added/removed, a line
   wrapping differently) and read as busier than a loading state needs
   to be; a single block communicates "loading" just as clearly with
   far less visual noise. Height (60px) still roughly matches a real
   .club-list-item/.comment-item's rendered height so nothing jumps when
   the real content swaps in. */
.skel-row {
	height: 60px;
	border-radius: 10px;
}

.banner-carousel {
	position: relative;
	border-radius: 14px;
	overflow: hidden;
	border: 1px solid var(--border);
	margin-bottom: 22px;
	aspect-ratio: 2.35 / 1;
	background: var(--surface);
}

.banner-track {
	position: relative;
	width: 100%;
	height: 100%;
}

.banner-slide {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: center;
	display: flex;
	align-items: flex-start;
	opacity: 0;
	transition: opacity .5s ease;
	pointer-events: none;
}

/* Event banners with a link (see sponsor.js's sponsorLinkInput) render as
   a real <a> instead of a <div> -- only these should look clickable. */
.banner-slide-linked {
	cursor: pointer;
}

.banner-slide.active {
	opacity: 1;
	pointer-events: auto;
}

.banner-slide::before {
	content: '';
	position: absolute;
	inset: 0;
	background: linear-gradient(90deg, rgba(8,7,13,.8) 0%, rgba(8,7,13,.45) 32%, rgba(8,7,13,.12) 58%, transparent 82%);
}

/* A video slide's .beu-player (see home.js's BANNER_SLIDES `video`
   field) sits absolutely behind the gradient overlay and copy, filling
   the slide exactly like the background-image it layers on top of.
   Below 1024px width the player never fetches anything (see its own
   data-min-viewport gating) -- the slide's plain background-image
   (used as the player's data-poster too) is all that ever shows there. */
.banner-slide .beu-player {
	position: absolute;
	inset: 0;
	z-index: 0;
	border-radius: 0;
}

.banner-copy {
	position: relative;
	z-index: 1;
	padding: clamp(16px, 4vw, 40px);
	max-width: 60%;
}

.banner-headline {
	font-size: clamp(22px, 4.4vw, 38px);
	font-weight: 800;
	line-height: 1.25;
	color: #fff;
	letter-spacing: -.01em;
}

.banner-headline .accent {
	color: var(--accent-soft, #a79bfa);
}

.banner-tagline {
	margin-top: 10px;
	font-size: clamp(13px, 2.6vw, 16.5px);
	color: rgba(255,255,255,.78);
	line-height: 1.6;
	max-width: 320px;
}

.banner-arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	z-index: 2;
	width: 32px;
	height: 32px;
	border-radius: 50%;
	background: rgba(13,13,15,.55);
	border: 1px solid rgba(255,255,255,.15);
	color: #fff;
	font-size: 18px;
	line-height: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	backdrop-filter: blur(2px);
	transition: background .15s;
}

.banner-arrow:hover {
	background: rgba(124,106,247,.55);
}

/* Same reasoning as .pw-toggle:active above: the global
   button:active scale would otherwise replace this button's own
   translateY(-50%) centering and knock it out of vertical center
   while pressed. */
.banner-arrow:active {
	transform: translateY(-50%) scale(0.9);
}

.banner-arrow-l { left: 12px; }
.banner-arrow-r { right: 12px; }

.banner-dots {
	position: absolute;
	bottom: 12px;
	left: 50%;
	transform: translateX(-50%);
	z-index: 2;
	display: flex;
	gap: 6px;
}

.banner-dot {
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: rgba(255,255,255,.35);
	cursor: pointer;
	border: none;
	padding: 0;
	transition: background .15s, width .15s;
}

.banner-dot.active {
	background: var(--accent);
	width: 16px;
	border-radius: 3px;
}

.uni-section {
	margin-bottom: 24px;
}

.uni-title-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	margin-bottom: 14px;
}

/* No college set -> empty heading: hide the whole row so it doesn't leave a gap. */
.uni-title-row:has(> .uni-title:empty) {
	display: none;
}

.uni-title {
	font-size: clamp(13px, 2.6vw, 15px);
	font-weight: 800;
	letter-spacing: .02em;
	color: var(--text);
}

.uni-icons {
	display: flex;
	gap: 8px;
	flex-shrink: 0;
}

.uni-icon-btn {
	width: 30px;
	height: 30px;
	border-radius: 50%;
	background: #16151d;
	border: 1px solid var(--border);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	transition: border-color .15s, background .15s;
}

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

.quicklink-grid {
	display: grid;
	grid-template-columns: repeat(5, 1fr);
	gap: 10px;
}

@media (max-width: 700px) {
	.quicklink-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 420px) {
	.quicklink-grid { grid-template-columns: repeat(2, 1fr); }
}

.quicklink-card { position: relative; }
.quicklink-unread-dot {
	position: absolute;
	top: 10px;
	right: 10px;
	width: 9px;
	height: 9px;
	border-radius: 50%;
	background: var(--accent, #7c6af7);
	box-shadow: 0 0 0 2px var(--surface, #14141a);
	display: none;
}
.quicklink-card.has-unread .quicklink-unread-dot { display: block; }

.result-item-actions {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	gap: 6px;
}

.result-tab.disabled {
	opacity: .45;
	cursor: not-allowed;
}

.result-tab.disabled:hover { color: var(--muted); }

.syllabus-branch-grid {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 10px;
}

.syllabus-branch-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 9px;
	padding: 16px 8px;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 12px;
	cursor: pointer;
	transition: all .15s;
	-webkit-tap-highlight-color: transparent;
}

.syllabus-branch-card:hover {
	border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
	transform: translateY(-1px);
}

.syllabus-branch-card.active {
	border-color: var(--accent);
	background: color-mix(in srgb, var(--accent) 12%, var(--surf2));
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent);
}

.syllabus-branch-icon {
	width: 44px;
	height: 44px;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	color: #fff;
	font-weight: 800;
	font-size: 11.5px;
	letter-spacing: .01em;
	flex-shrink: 0;
}

.syllabus-branch-label {
	font-size: 12px;
	font-weight: 600;
	color: var(--text);
	line-height: 1.3;
}

@keyframes itemEnterLTR {
	from {
		opacity: 0;
		transform: translateX(-18px);
	}
	to {
		opacity: 1;
		transform: translateX(0);
	}
}
.item-enter {
	animation: itemEnterLTR .32s cubic-bezier(.22, .68, .32, 1) both;
}
@media (prefers-reduced-motion: reduce) {
	.item-enter {
		animation: none;
	}
}

.card {
	margin-top: 14px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 0px;
	padding: clamp(14px, 3vw, 18px) clamp(14px, 4vw, 20px);
}

.card + .card {
	margin-top: 14px;
}

.card-title {
	font-size: 13px;
	font-weight: 700;
	margin-bottom: 14px;
	display: flex;
	align-items: center;
	gap: 8px;
	letter-spacing: .01em;
	text-transform: uppercase;
	color: var(--muted);
}

.card-title small {
	color: var(--muted);
	font-weight: 400;
	font-size: 11px;
	text-transform: none;
	letter-spacing: 0;
}

.ic-done { 
	background: transparent;
}

.ic-done svg { 
	stroke: var(--accent);
}

.ic-partial {
	background: transparent;
}

.ic-partial svg {
	stroke: var(--ok);
}

.ic-left {
	background: transparent;
}

.ic-left svg {
	stroke: var(--a2);
}

.ic-prog {
	background: transparent;
}

.ic-prog svg {
	stroke: var(--accent);
}

input[type="time"],
input[type="date"] {
	color-scheme: dark;
}

input[type="number"] {
	color-scheme: dark;
}

input[type="file"]::file-selector-button,
input[type="file"]::-webkit-file-upload-button {
	margin-right: 10px;
	padding: 7px 14px;
	border-radius: var(--r);
	font-size: 12px;
	font-weight: 600;
	font-family: var(--sans);
	border: 1px solid var(--border);
	background: var(--surf2);
	color: var(--text);
	cursor: pointer;
	transition: all .14s;
}

input[type="file"]::file-selector-button:hover,
input[type="file"]::-webkit-file-upload-button:hover {
	background: var(--border);
}

input[type="time"]::-webkit-calendar-picker-indicator,
input[type="date"]::-webkit-calendar-picker-indicator {
	filter: invert(52%) sepia(60%) saturate(500%) hue-rotate(212deg) brightness(105%);
	cursor: pointer;
	opacity: 1;
}

.auth-screen {
	position: fixed;
	inset: 0;
	z-index: 200;
	/* .auth-box below has a 390px min-width floor (same rationale as
	   .app's min-width) so it stops squishing on very narrow viewports.
	   Because this screen is position:fixed (unlike .app, which is in
	   normal flow), the html/body overflow-x:auto above can't reliably
	   reach content that overflows *it*, so this container scrolls
	   itself horizontally instead once .auth-box no longer fits. */
	overflow-x: auto;
	overflow-y: auto;
	/* Facebook-style split screen: .auth-promo (the video) sits on the
	   left, a hairline .auth-divider between it and the form, and the
	   form column stays centered in whatever space it has on the
	   right. Below the .auth-promo breakpoint this collapses back to
	   the old single centered-box layout. */
	display: flex;
	align-items: stretch;
	justify-content: center;
	/* No background-color here on purpose. .auth-screen is a fixed,
	   full-viewport layer sitting on top of <body> (z-index above), so
	   a solid color here would completely hide auth.php's own
	   page-specific background override (a flat var(--bg), no grid
	   pattern -- see the inline \3c style> in auth.php). Leaving this
	   transparent lets that flat color show through instead of being
	   painted over redundantly.
	   (This rule is scoped to auth.php only -- #authScreen doesn't exist
	   on any other page, so nothing else relies on it being opaque.) */
}

/* Logo now lives top-left of the whole screen, independent of which
   side the form/video land on -- see .auth-screen-logo in
   view-auth.js. Absolutely positioned against .auth-screen (its
   nearest positioned ancestor), so it's lifted out of the row-flex
   flow entirely and doesn't compete for space with .auth-promo/
   .auth-box-wrap. */
.auth-screen-logo {
	position: absolute;
	top: 24px;
	left: 24px;
	z-index: 1;
	margin-bottom: 0;
}

.auth-box-wrap {
	flex: 1 1 480px;
	display: flex;
	align-items: center;
	/* Was justify-content: center, then stretch -- now back to center,
	   but combined with .auth-box's max-width: 480px below the effect is
	   different this time: for widths under the cap the card still
	   fills 100% of whatever space this wrap's padding leaves (left
	   margin from the divider above 1024px / from the left edge below
	   it, right margin from the screen's right edge), but once this
	   wrap has more room than 480px + its own padding, the capped card
	   just centers in the middle of that space instead of hugging the
	   left/divider side, with equal margin on both sides. */
	justify-content: center;
	padding: 20px 80px;
	/* Extra room at the bottom for the now-always-present fixed
	   language row (.auth-promo-langs) pinned to the viewport bottom,
	   so it never overlaps/covers the bottom of the login card on
	   short viewports. */
	padding-bottom: 64px;
	min-width: 390px;
}

.auth-box {
	width: 100%;
	/* Caps how wide the card can stretch to on very wide screens --
	   without it, the removed max-width: 400px left nothing else to stop
	   the card from growing edge-to-edge inside .auth-box-wrap on a
	   large monitor. .auth-box-wrap's own justify-content: stretch above
	   only fills width up to this cap; once the card hits it, it just
	   sits at its left edge (nearest the divider) with the rest of the
	   wrap's space left empty on the right, instead of centering. */
	max-width: 480px;
	/* Floor to match .app's -- below this the form's inputs/buttons start
	   squishing and wrapping badly instead of the screen just scrolling
	   horizontally (see .auth-screen's overflow-x:auto above). Lifted on
	   phone-width screens by the max-width: 480px media query further
	   down, since this floor is exactly what was forcing a horizontal
	   scrollbar on common Android widths (360-412px) below it. */
	min-width: 390px;
	flex-shrink: 0;
	/* Background/border intentionally removed -- the card now sits
	   directly on the screen's own background instead of looking like a
	   raised panel. */
	border-radius: 14px;
	padding: 28px 26px 24px;
	overflow: hidden;
}

/* Left-hand promo panel with beuclub's own promo video, mute toggle
   and scramble-decode slogan (all positioned against this as their
   nearest positioned ancestor -- see the rules below). The language
   row used to live in here too; it's now a sibling fixed to the whole
   viewport bottom instead (see .auth-promo-langs further down) so it
   stays visible even when this panel is hidden below the breakpoint.
   The video is stretched (object-fit: fill) to exactly match the
   panel bounds rather than being letterboxed or cropped. Hidden below
   the breakpoint in the media query further down, exactly like
   Facebook drops its illustration once the viewport can't fit both
   columns. */
.auth-promo {
	position: relative;
	flex: 1 1 55%;
	min-width: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	overflow: hidden;
	/* Was a flat #000 -- with a multi-MB video loading, that showed as a
	   plain black rectangle for however long the download took. Now
	   matches .beu-player's own surface tone (see video-player.css),
	   which the player's poster/shimmer sits on top of, so there's
	   never a moment of flat, dead black. */
	background: var(--surface);
}

/* .auth-promo-video is now the .beu-player wrapper itself (see
   view-auth.js) -- assets/css/video-player.css handles its sizing.
   This just restores the one non-default look this panel wants: fill
   (not cover/contain) stretches the frame to exactly match the
   panel's bounds edge-to-edge, no cropping or letterboxing. The
   tradeoff is the frame can look slightly stretched if the panel's
   aspect ratio doesn't match the source video, which is the
   explicitly requested behavior here. */
.auth-promo-video.beu-player .beu-player-video {
	object-fit: fill;
}

/* Login/signup promo panel wants square corners, not the rounded
   corners .beu-player uses by default elsewhere (e.g. home banner
   slides) -- override just for this instance. */
.auth-promo-video.beu-player {
	border-radius: 0;
}

/* Mute toggle -- top-right corner of the promo panel. Sound starts ON
   by default (see _attemptAuthPromoAutoplayWithSound() in auth.js,
   which tries an unmuted autoplay first and only falls back to muted
   -- reconciling this button's state either way -- if the browser
   rejects that). This button is the manual override either direction.
   Mirrors the .auth-screen-logo pattern: absolutely positioned
   against .auth-promo so it never competes with the video for layout
   space. */
.auth-promo-mute-btn {
	position: absolute;
	top: 16px;
	right: 16px;
	z-index: 2;
	width: 34px;
	height: 34px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	border: 1px solid rgba(255, 255, 255, 0.25);
	background: rgba(13, 13, 15, 0.55);
	backdrop-filter: blur(4px);
	color: #fff;
	cursor: pointer;
	transition: background 0.15s ease, border-color 0.15s ease;
}

.auth-promo-mute-btn:hover {
	background: rgba(13, 13, 15, 0.8);
	border-color: rgba(255, 255, 255, 0.45);
}

/* Icon swap: only one of the two SVGs shows at a time, toggled by the
   [aria-pressed] state that toggleAuthPromoMute() flips in auth.js
   (aria-pressed="true" means audio is ON, i.e. no longer muted). */
.auth-promo-mute-btn .mute-icon-on { display: none; }
.auth-promo-mute-btn[aria-pressed="true"] .mute-icon-off { display: none; }
.auth-promo-mute-btn[aria-pressed="true"] .mute-icon-on { display: flex; }

/* Slogan, bottom-left with margin above the (now page-level) language
   row. One line per <span> -- see view-auth.js -- stacked in a column
   instead of a single wrapped block, so the line breaks are always
   "Connect with" / "the people" / "who inspire you." regardless of
   panel width. Sits on its own stacking layer above the video; a soft
   bottom-up gradient behind it (see .auth-promo::after) keeps it
   legible over bright footage. */
.auth-promo-slogan {
	position: absolute;
	left: 32px;
	right: 32px;
	bottom: 64px;
	z-index: 2;
	display: flex;
	flex-direction: column;
	font-family: var(--sans);
	font-weight: 800;
	font-size: clamp(22px, 2.6vw, 34px);
	line-height: 1.22;
	letter-spacing: -0.01em;
}

.auth-promo-slogan .slogan-line {
	display: block;
}

.auth-promo-slogan .slogan-base {
	color: #fff;
}

.auth-promo-slogan .slogan-accent {
	color: rgb(107, 91, 239);
}

/* Darkening gradient behind the slogan so it stays readable regardless
   of what's playing in the video underneath. */
.auth-promo::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 45%;
	z-index: 1;
	background: linear-gradient(to top, rgba(0, 0, 0, 0.65), rgba(0, 0, 0, 0));
	pointer-events: none;
}

/* Language row -- pinned to the bottom of the WHOLE viewport (not
   .auth-promo -- see view-auth.js, it's a sibling of .auth-promo now,
   not a child), so it stays visible on top of the login form itself
   once the video panel is hidden below the breakpoint instead of
   disappearing along with it. Solid background matching the rest of
   the auth screen (not an overlay-on-video treatment anymore, since
   it's no longer always sitting on top of the video). The dropdown
   panel below (.auth-promo-langs) is `position: fixed` and positions
   itself against its own trigger button in JS, not against this bar,
   so this is just the visible bar/footer row itself. */
.auth-lang-bar {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 3;
	border-top: 1px solid var(--border);
	background: var(--bg);
}

/* The language list is now a real dropdown, not an always-visible flat
   row: [hidden] (set on #authPromoLangs in view-auth.js) collapses it
   to nothing, and toggleAuthLangDropdown() in auth.js flips that
   attribute off when the footer's "English" button is pressed.

   Positioned relative to the VIEWPORT (position: fixed) with its
   left/top set in JS (see _positionAuthLangDropdown() in auth.js)
   from the trigger button's own getBoundingClientRect() each time it
   opens -- not a fixed `right: 24px` offset from the whole bottom
   bar. The bar spans the full screen width and the button inside it
   can end up anywhere along that row once .auth-lang-bar-footer wraps
   on narrow screens, so anchoring the panel to a static point on the
   bar meant it could open nowhere near the button that triggered it.
   Anchoring to the button's own rect keeps the panel visually
   attached to it regardless of where that ends up.

   TWO COLUMNS BY DEFAULT, STACKING TO ONE WHEN THEY DON'T FIT -- this
   used to be a single flex container with `flex-direction: column;
   flex-wrap: wrap;`, relying on a JS-computed pixel `height` (see
   _computeLangDropdownDims() in auth.js) to be *exactly* tall enough
   for ceil(count/2) rows so the browser's own wrapping happened to
   break into 2 columns. That's inherently a guess dressed up as a
   calculation: if that height computation ran before layout/fonts had
   settled, was clamped by _positionAuthLangDropdown()'s
   available-space check, or simply didn't match the real rendered row
   height for any reason, the browser would just as validly wrap into
   1 column (rows never ran out of vertical room) or 3+ (rows ran out
   early) -- same list, same code, different outcome per load. That's
   the "sometimes 1 column, sometimes 2" bug the two-column split
   below replaces.

   The column split itself is structural, not numerical: the language
   buttons are split into two explicit .auth-lang-col children
   directly in the markup (see #authPromoLangs in view-auth.js) --
   column A gets the first half of the list, column B gets the second
   half, always. But two *fixed-width* columns cannot themselves
   shrink to fit a narrow window -- an earlier attempt tried to force
   them to via an inline `max-width` from _positionAuthLangDropdown()
   in auth.js, and because flex children with a fixed width don't
   shrink, that only squeezed the two unshrinkable columns into a box
   too narrow for them, clipping language names off mid-word instead
   of fixing anything.

   So narrow viewports get a structural answer too: when the two
   columns' combined width would exceed the real available space,
   _positionAuthLangDropdown() adds `.is-stacked` (below), which
   switches this container to `flex-direction: column`. Column A then
   renders above column B instead of beside it, so every button keeps
   its own full fixed width and every name stays fully readable --
   the list just gets taller, which the existing vertical scroll
   (overflow-y below) already handles. */
.auth-promo-langs {
	position: fixed;
	/* Above .auth-lang-bar-footer (see below): both are plain children
	   of .auth-lang-bar with z-index:auto, so without this the footer
	   -- later in the DOM -- painted on top of this panel wherever the
	   two ever overlapped (short viewports / the footer wrapping onto
	   two lines pushed the button, and this panel's clamped position,
	   close enough to collide). Only needs to beat the footer's own
	   (auto) stacking, so it stays well under modal-tier z-indexes. */
	z-index: 2;
	display: flex;
	flex-direction: row;
	align-items: flex-start;
	gap: 0 12px;
	/* Safety net only, for very short viewports -- _positionAuthLangDropdown()
	   in auth.js also sets an inline max-height clamped to the real
	   space above the trigger button. overflow-y scrolls the column(s)
	   (rather than hidden, which used to rely on the height always
	   being exactly right) so nothing is ever silently clipped if a
	   screen is too short for the full list -- this is what absorbs
	   the extra height .is-stacked below produces on narrow screens. */
	max-height: min(420px, calc(100vh - 96px));
	overflow-y: auto;
	/* Belt-and-braces only: .is-stacked (below) is what actually keeps
	   names readable on narrow screens by dropping to one column. This
	   just stops a single column from clipping too, in the extreme
	   case of a viewport narrower than even one button
	   (--lang-item-width) plus this panel's own padding/border. */
	overflow-x: auto;
	scrollbar-width: none;      /* Firefox */
	-ms-overflow-style: none;   /* old Edge/IE */
	padding: 8px;
	border-radius: var(--r);
	border: 1px solid var(--border);
	background: var(--surface);
	box-shadow: 0 12px 32px rgba(0, 0, 0, .35);
}

/* Set by _positionAuthLangDropdown() in auth.js whenever the two
   fixed-width columns wouldn't both fit in the real available
   viewport width. Column B drops underneath column A instead of
   beside it -- every button keeps its full, unshrunk width (see
   .auth-lang-col / .auth-promo-lang-item below) so no language name
   is ever clipped, no matter how narrow the window gets. The taller
   single-column result is handled by the existing overflow-y scroll
   above, not by anything new. */
.auth-promo-langs.is-stacked {
	flex-direction: column;
	gap: 4px 0;
}

.auth-promo-langs::-webkit-scrollbar {
	display: none; /* Chrome/Safari/Edge */
}

.auth-promo-langs[hidden] {
	display: none;
}

/* One of the two hard-split halves of the language list -- see the
   comment on .auth-promo-langs above. Each column just stacks its own
   buttons top-to-bottom; the two sit side by side because the parent
   is a plain flex row. */
.auth-lang-col {
	display: flex;
	flex-direction: column;
	/* Width pinned to the exact same value as its buttons (rather than
	   left to shrink-to-fit/auto) -- some engines recompute a flex
	   column's own auto width against the CURRENT available space in
	   its flex-row parent on every reflow (e.g. the resize listener in
	   auth.js repositioning the panel), which can hand back a number
	   larger than any child actually needs even though every button
	   inside is a fixed, unchanging width. Removing the "auto" entirely
	   removes whatever that's computed from. */
	width: var(--lang-item-width, 128px);
	flex: 0 0 auto;
}

.auth-promo-lang-item {
	display: block;
	/* Set from JS (_computeLangItemWidth() in auth.js) to the longest
	   language name in the list plus its own padding, so every button
	   -- in both columns -- lines up instead of each hugging its own
	   text. The px fallback only covers the instant before that JS
	   runs. */
	width: var(--lang-item-width, 128px);
	flex: 0 0 auto;
	background: none;
	border: none;
	border-radius: 6px;
	padding: 5px 8px;
	/* Without this, a <button>'s own default UA margin (present in
	   some browsers/engines even after a reset that only zeroes
	   padding/border) sits outside the `width` above -- margin never
	   counts toward an element's own box width, so it just pushes the
	   button's painted content past its column's fixed-width box.
	   Because overflow is visible by default, that spillover paints
	   straight past the panel's right edge too, WITHOUT enlarging the
	   panel's own getBoundingClientRect() -- which is exactly why
	   even measuring the panel's real rendered width in
	   _positionAuthLangDropdown() (see auth.js) didn't catch this:
	   the box was measured correctly, the leak was happening entirely
	   outside it. !important because this button also picks up any
	   later global `button` margin resets/overrides added to the
	   site -- this one has to win regardless of load order. */
	margin: 0 !important;
	font-family: var(--sans);
	font-size: 12px;
	color: var(--muted);
	cursor: pointer;
	text-align: left;
	white-space: nowrap;
}

.auth-promo-lang-item:hover {
	color: var(--text);
	background: var(--surf2);
}

/* Currently-selected language -- accent color + own tint so it reads
   as "active" rather than just another row. */
.auth-lang-btn.active {
	color: var(--accent, #1d9bf0);
	background: color-mix(in srgb, var(--accent) 12%, transparent);
	font-weight: 600;
}

/* Bottom-of-bar footer inside .auth-lang-bar -- copyright + legal
   links on the left, color-changer + current-language dropdown trigger
   on the right. Sits under .auth-promo-langs, its own top border so it
   reads as a distinct row rather than just more of the language list. */
.auth-lang-bar-footer {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: nowrap;
	gap: clamp(4px, 1.6vw, 20px);
	padding: 10px clamp(10px, 4vw, 24px) 12px;
	border-top: 1px solid var(--border);
	font-size: clamp(9px, 2.6vw, 11px);
	color: var(--muted);
}

.auth-footer-copy {
	white-space: nowrap;
	flex-shrink: 0;
}

.auth-footer-links {
	display: flex;
	gap: clamp(5px, 1.8vw, 14px);
	flex-wrap: nowrap;
	min-width: 0;
}

.auth-footer-links a {
	white-space: nowrap;
}

.auth-footer-links a {
	color: var(--muted);
	text-decoration: none;
}

.auth-footer-links a:hover {
	color: var(--text);
}

/* Dark/light color-changer -- sits just left of the language dropdown
   trigger in the footer row. Only one of the two icons is shown at a
   time; toggleAuthColorTheme() in auth.js flips the .is-light class
   (and the html[data-auth-theme] attribute the actual colors key off
   of) together so the icon always matches the palette on screen. */
.auth-theme-toggle-btn {
	display: flex;
	align-items: center;
	justify-content: center;
	width: clamp(20px, 6vw, 26px);
	height: clamp(20px, 6vw, 26px);
	margin-left: auto;
	flex-shrink: 0;
	background: none;
	border: 1px solid var(--border);
	border-radius: 50%;
	color: var(--muted);
	cursor: pointer;
}

.auth-theme-toggle-btn svg {
	width: clamp(11px, 3vw, 14px);
	height: clamp(11px, 3vw, 14px);
}

.auth-theme-toggle-btn:hover {
	color: var(--text);
}

.auth-theme-toggle-btn .theme-icon-moon { display: block; }
.auth-theme-toggle-btn .theme-icon-sun { display: none; }
.auth-theme-toggle-btn.is-light .theme-icon-moon { display: none; }
.auth-theme-toggle-btn.is-light .theme-icon-sun { display: block; }

.auth-footer-lang-btn {
	display: flex;
	align-items: center;
	gap: 5px;
	flex-shrink: 0;
	white-space: nowrap;
	background: none;
	border: none;
	padding: 2px 0;
	font-family: var(--sans);
	font-size: inherit;
	color: var(--muted);
	cursor: pointer;
}

.auth-footer-lang-btn:hover {
	color: var(--text);
}

/* Chevron starts pointing down (list closed, sitting above the
   button); toggleAuthLangDropdown() adds .is-open when the dropdown is
   expanded, flipping it to point up toward the now-visible list. */
.auth-footer-lang-chevron {
	transition: transform .15s ease;
}

.auth-footer-lang-btn.is-open .auth-footer-lang-chevron {
	transform: rotate(180deg);
}

/* "Switching languages" overlay -- shown for a brief minimum stretch
   (see showAuthLangSwitchOverlay()/hideAuthLangSwitchOverlay() in
   auth.js) any time setAuthLanguage() fires. Fixed over the WHOLE
   viewport (not just the language bar it lives in -- .auth-lang-bar
   itself has no transform, so this fixed child still positions
   against the real viewport) with a blurred backdrop, so the entire
   screen behind the spinner reads as softly out-of-focus rather than
   just covering the dropdown panel. */
.auth-lang-switch-overlay {
	position: fixed;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	background: color-mix(in srgb, var(--bg) 45%, transparent);
	backdrop-filter: blur(10px);
	-webkit-backdrop-filter: blur(10px);
	z-index: 2000;
}

/* Small plain spinner (no logo) -- just a rotating ring in the accent
   color. Reuses the pyq-spin keyframe so it rotates at the same speed
   as every other spinner in the app. */
.beu-brand-spinner {
	display: block;
	width: 28px;
	height: 28px;
	border-radius: 50%;
	border: 3px solid color-mix(in srgb, var(--accent) 22%, transparent);
	border-top-color: var(--accent);
	animation: pyq-spin .8s linear infinite;
}

/* ---------- Basic RTL support (Urdu / Arabic) ----------
   AUTH_I18N.applyToDom() (auth-i18n.js) sets dir="rtl" on #authScreen
   itself when the selected language's JSON entry says dir:"rtl", which
   flips paragraph/label text alignment below. This is intentionally
   modest -- it does not mirror the two-column layout or icon
   positions, just the direction text actually reads in, which is the
   part that matters most for comprehension. Form inputs are pinned
   back to LTR regardless, since email addresses, passwords and OTP
   digits are always typed left-to-right even inside an RTL page. */
.auth-screen[dir="rtl"] .field label,
.auth-screen[dir="rtl"] .auth-hint,
.auth-screen[dir="rtl"] .auth-subtitle,
.auth-screen[dir="rtl"] .auth-title,
.auth-screen[dir="rtl"] .auth-switch,
.auth-screen[dir="rtl"] .form-error {
	text-align: right;
}

.auth-screen[dir="rtl"] input[type="email"],
.auth-screen[dir="rtl"] input[type="password"],
.auth-screen[dir="rtl"] input[inputmode="numeric"] {
	direction: ltr;
	text-align: left;
}

/* Hairline vertical rule between the promo panel and the form, same
   idea as .divider elsewhere in the app just oriented vertically and
   stretched to the screen's full height. */
.auth-divider {
	width: 1px;
	flex-shrink: 0;
	background: var(--border);
}

@media (max-width: 1024px) {
	.auth-promo,
	.auth-divider {
		display: none;
	}
	.auth-box-wrap {
		flex: 1 1 auto;
		/* No divider/promo panel on this side anymore, so the margin
		   collapses down to a simple left/right edge margin instead of
		   the wider "margin from the divider" spacing used above the
		   breakpoint. */
		padding-left: 40px;
		padding-right: 40px;
	}
}

/* Phone-width override: the 390px min-width floor on .auth-box/
   .auth-box-wrap (kept above so the card doesn't squish on tablet/
   desktop-narrow windows) is wider than most Android phones
   (360-412px viewport widths are common), and was exactly what forced
   auth.php into a horizontal scrollbar there. Below this breakpoint the
   floor is dropped in favor of just shrinking to fit the viewport, with
   smaller side padding to match. */
@media (max-width: 480px) {
	.auth-box-wrap {
		min-width: 0;
		padding-left: 24px;
		padding-right: 24px;
	}
	.auth-box {
		min-width: 0;
	}
}

.auth-brand {
	justify-content: center;
	margin-bottom: 20px;
	font-weight: 700;
	font-size: 15px;
	display: flex;
	align-items: center;
	gap: 8px;
}

.auth-title {
	text-align: left;
	font-size: 22px;
	font-weight: 700;
	margin-bottom: 6px;
}

/* Sits directly under .auth-title for the login/signup step only --
   the OTP/photo/forgot/reset steps already carry their own inline
   .auth-hint copy, so setAuthStep() in auth.js hides this one whenever
   a step other than "details" is showing (see there). */
.auth-subtitle {
	text-align: left;
	font-size: 13.5px;
	color: var(--muted);
	line-height: 1.5;
	margin-bottom: 18px;
}

/* Larger, heavier logo mark on the login/signup screen now that it's
   standing alone without the "beuclub" wordmark next to it (see
   view-auth.js) -- scoped to .auth-brand so the header/footer/sponsor-
   modal .brand-mark elsewhere stay their normal size. */
.auth-brand .brand-mark {
	width: clamp(39px, 7.5vw, 48px);
	height: clamp(39px, 7.5vw, 48px);
	border-radius: 14px;
}

.auth-form {
	margin-top: 6px;
}

.auth-submit {
	width: 100%;
	justify-content: center;
}

.auth-hint {
	font-size: 12.5px;
	color: var(--muted);
	line-height: 1.5;
	margin: 0 0 14px;
	text-align: left;
}

/* Small-print consent line sitting right under the login/signup submit
   button (see .auth-form + this in view-auth.js's "details" step). */
.auth-consent-note {
	font-size: 11.5px;
	color: var(--muted);
	line-height: 1.5;
	text-align: center;
	margin: 12px 0 4px;
}

.auth-consent-note a {
	color: var(--muted);
	text-decoration: underline;
}

.auth-consent-note a:hover {
	color: var(--text);
}

/* ---------- Social login (Google) ----------
   Sits above the email/password form on the login+signup ("details")
   step only -- see #authSocialRow's placement in view-auth.js and
   setAuthStep() in auth.js, which hides it on every other step (OTP,
   photo, forgot, reset) the same way #authSubtitle already is. */
.auth-social-row {
	display: flex;
	flex-direction: column;
	gap: 10px;
	margin-bottom: 16px;
}

.social-btn {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	width: 100%;
	height: 44px;
	border-radius: 999px;
	border: 1px solid transparent;
	background: #fff;
	color: #3c4043;
	font-family: var(--sans);
	font-size: 14.5px;
	font-weight: 600;
	cursor: pointer;
	transition: filter .15s ease, transform .1s ease;
}

.social-btn:hover {
	filter: brightness(0.96);
}

.social-btn:active {
	transform: scale(0.98);
}

.social-btn svg {
	width: 20px;
	height: 20px;
	flex-shrink: 0;
}

.social-btn.google {
	border-color: #dadce0;
}

.social-btn.passkey,
.social-btn.qr {
	border-color: #dadce0;
}

.social-btn[disabled] {
	opacity: 0.6;
	cursor: default;
	pointer-events: none;
}

.auth-social-or {
	display: flex;
	align-items: center;
	gap: 10px;
	margin: 4px 0 16px;
	color: var(--muted);
	font-size: 12.5px;
}

.auth-social-or::before,
.auth-social-or::after {
	content: '';
	flex: 1;
	height: 1px;
	background: var(--border);
}

.auth-switch {
	text-align: center;
	margin-top: 16px;
	font-size: 12px;
	color: var(--muted);
}

.auth-switch-btn {
	background: none;
	border: none;
	color: var(--accent);
	font-weight: 600;
	font-size: 12px;
	cursor: pointer;
	margin-left: 4px;
}

.form-error {
	background: rgba(232, 87, 106, .1);
	border: 1px solid rgba(232, 87, 106, .3);
	color: var(--err);
	border-radius: 8px;
	padding: 8px 11px;
	font-size: 12px;
	margin-bottom: 12px;
}

/* ============================================================
   14-step signup wizard (#authSignupWizard in view-auth.js) --
   see assets/js/auth.js's signupWizard* functions.
   ============================================================ */
.signup-progress {
	margin-bottom: 18px;
}

.signup-progress-label {
	font-size: 11.5px;
	font-weight: 600;
	color: var(--muted);
	margin-bottom: 6px;
	letter-spacing: .02em;
}

.signup-progress-track {
	width: 100%;
	height: 5px;
	border-radius: 999px;
	background: var(--border);
	overflow: hidden;
}

.signup-progress-fill {
	height: 100%;
	background: var(--accent);
	border-radius: 999px;
	transition: width .25s ease;
}

.signup-step-title {
	font-size: 19px;
	font-weight: 700;
	color: var(--text);
	margin-bottom: 4px;
}

.signup-hint-sm {
	font-size: 11px;
	margin-top: -6px;
}

.req {
	color: var(--err);
}

.signup-pw-checklist {
	display: flex;
	flex-direction: column;
	gap: 4px;
	margin: 4px 0 14px;
}

.pw-check {
	font-size: 11.5px;
	color: var(--muted);
	padding-left: 18px;
	position: relative;
}

.pw-check::before {
	content: '';
	position: absolute;
	left: 0;
	top: 4px;
	width: 12px;
	height: 12px;
	border-radius: 50%;
	border: 1.5px solid var(--muted);
}

.pw-check.ok {
	color: var(--ok);
}

.pw-check.ok::before {
	border-color: var(--ok);
	background: var(--ok);
}

/* ---- Signup wizard step 4: username field, availability status, suggestions ---- */
.username-input {
	display: flex;
	align-items: center;
	gap: 8px;
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: var(--r);
	padding: 0 12px;
	transition: border-color .18s, box-shadow .18s;
}
.username-input:focus-within {
	border-color: var(--accent);
	box-shadow: 0 0 0 3px rgba(124, 106, 247, .14);
}
.username-input.is-ok { border-color: var(--ok); }
.username-input.is-ok:focus-within { box-shadow: 0 0 0 3px rgba(64, 201, 168, .16); }
.username-input.is-bad { border-color: var(--err); }
.username-input.is-bad:focus-within { box-shadow: 0 0 0 3px rgba(232, 87, 106, .16); }
.username-input input,
.username-input input:focus {
	flex: 1;
	min-width: 0;
	border: 0;
	background: transparent;
	box-shadow: none;
	padding: 9px 0;
}
.username-at {
	color: var(--muted);
	font-size: 14px;
	font-weight: 600;
	user-select: none;
}
.username-state {
	flex: none;
	width: 20px;
	height: 20px;
	border-radius: 50%;
	display: none;
	align-items: center;
	justify-content: center;
	font-size: 12px;
	font-weight: 700;
	line-height: 1;
	color: #fff;
}
.username-state.ok { display: flex; background: var(--ok); }
.username-state.ok::before { content: '\2713'; }
.username-state.bad { display: flex; background: var(--err); }
.username-state.bad::before { content: '!'; }
.username-state.busy {
	display: block;
	border: 2px solid var(--border);
	border-top-color: var(--accent);
	animation: username-spin .7s linear infinite;
}
@keyframes username-spin { to { transform: rotate(360deg); } }
.username-status {
	font-size: 12.5px;
	margin-top: 7px;
	color: var(--muted);
}
.username-status.is-ok { color: var(--ok); font-weight: 600; }
.username-status.is-bad { color: var(--err); }
.username-suggest { margin: 2px 0 14px; }
.username-suggest-title {
	font-size: 12.5px;
	color: var(--muted);
	margin-bottom: 8px;
}
.username-suggest-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.username-suggest-item {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	text-align: left;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: var(--r);
	padding: 9px 12px;
	color: var(--text);
	font-size: 14px;
	cursor: pointer;
	transition: border-color .15s, background .15s;
}
.username-suggest-item:hover,
.username-suggest-item:focus-visible { border-color: var(--accent); }
.username-suggest-ico { display: flex; color: var(--muted); }
.username-suggest-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.username-suggest-plus {
	flex: none;
	width: 20px;
	height: 20px;
	border-radius: 50%;
	background: var(--accent);
	color: #fff;
	font-size: 15px;
	line-height: 20px;
	text-align: center;
}
@media (prefers-reduced-motion: reduce) { .username-state.busy { animation-duration: 1.6s; } }

.option-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 10px;
	margin-bottom: 4px;
}

.option-grid-2col {
	grid-template-columns: 1fr 1fr;
}

.option-card {
	text-align: left;
	background: var(--surface);
	border: 1.5px solid var(--border);
	border-radius: var(--r);
	padding: 12px 14px;
	cursor: pointer;
	color: var(--text);
	font-family: var(--sans);
	transition: border-color .15s ease, background .15s ease;
}

.option-card:hover {
	border-color: var(--accent-soft);
}

.option-card.selected {
	border-color: var(--accent);
	background: rgba(124, 106, 247, .1);
}

.option-card-title {
	font-weight: 700;
	font-size: 14px;
}

.option-card-sub {
	font-size: 11.5px;
	color: var(--muted);
	margin-top: 2px;
}

.chip-row {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-bottom: 4px;
}

.chip {
	background: var(--surface);
	border: 1.5px solid var(--border);
	border-radius: 999px;
	padding: 7px 14px;
	font-size: 12.5px;
	color: var(--text);
	cursor: pointer;
	font-family: var(--sans);
	transition: border-color .15s ease, background .15s ease, color .15s ease;
}

.chip:hover {
	border-color: var(--accent-soft);
}

.chip.active {
	border-color: var(--accent);
	background: var(--accent);
	color: #fff;
}

/* "Tell us about yourself" (signup step 12, #signupBio) -- auto-grows
   via _updateSignupBioCounter() in auth.js instead of scrolling
   internally, so no resize handle or scrollbar is needed here; 160
   characters (the field's maxlength) fits comfortably within a few
   grown lines at this modal's width. */
#signupBio {
	resize: none;
	overflow-y: hidden;
	min-height: 84px;
}

.signup-bio-counter {
	text-align: right;
	font-size: 11px;
	color: var(--muted);
	margin-top: 4px;

}

.people-suggest-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
	max-height: 320px;
	overflow-y: auto;
	margin-bottom: 4px;
}

.people-suggest-loading, .people-suggest-empty {
	text-align: center;
	color: var(--muted);
	font-size: 12.5px;
	padding: 18px 0;
}

.people-suggest-item {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 8px 10px;
	border: 1px solid var(--border);
	border-radius: var(--r);
}

.people-suggest-info {
	flex: 1;
	min-width: 0;
}

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

.people-suggest-handle {
	font-size: 11px;
	color: var(--muted);
}

.people-suggest-follow-btn {
	background: var(--accent);
	color: #fff;
	border: none;
	border-radius: 999px;
	padding: 6px 14px;
	font-size: 12px;
	font-weight: 600;
	cursor: pointer;
	flex-shrink: 0;
}

.people-suggest-follow-btn.following {
	background: var(--surface);
	color: var(--muted);
	border: 1px solid var(--border);
}

.club-checkbox-label {
	display: flex;
	align-items: center;
	gap: 8px;
	font-size: 13px;
	color: var(--text);
	cursor: pointer;
}
.club-checkbox-label input[type="checkbox"] {
	width: 16px;
	height: 16px;
	accent-color: var(--accent);
	cursor: pointer;
}

.cf-turnstile {
	width: 100%;
	max-width: 100%;
	min-width: 0;
	margin-bottom: 12px;
	overflow: hidden;
	position: relative;
	contain: layout;
	box-sizing: border-box;
}
.cf-turnstile iframe {
	display: block;
	width: 100% !important;
	max-width: 100% !important;
	position: relative !important;
}

.turnstile-status {
	text-align: center;
	font-size: 12px;
	color: var(--muted);
	margin: -6px 0 12px;
}
.turnstile-status.is-error {
	color: var(--err);
	cursor: pointer;
	text-decoration: underline;
}

.password-wrap {
	position: relative;
}

.password-wrap input {
	padding-right: 40px;
}

.pw-toggle {
	position: absolute;
	right: 4px;
	top: 50%;
	transform: translateY(-50%);
	width: 30px;
	height: 30px;
	display: flex;
	align-items: center;
	justify-content: center;
	background: none;
	border: none;
	border-radius: 6px;
	color: var(--muted);
	cursor: pointer;
	padding: 0;
}

.pw-toggle:hover {
	color: var(--text);
	background: var(--surf2);
}

/* The global `button:active { transform: scale(0.96) }` rule above
   replaces (not combines with) this button's own centering
   `transform: translateY(-50%)`, so pressing it snapped the icon
   down out of its vertical-center position instead of just
   shrinking in place. Re-declare both transform functions together
   here so the press keeps the translateY centering and only adds
   the scale. */
.pw-toggle:active {
	transform: translateY(-50%) scale(0.9);
}

.pw-toggle .pw-eye-off {
	display: none;
}

.pw-toggle.is-visible .pw-eye {
	display: none;
}

.pw-toggle.is-visible .pw-eye-off {
	display: block;
}

.msgr-avatar {
	flex-shrink: 0;
	border-radius: 50%;
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--surf2);
	border: 1px solid var(--border);
	font-weight: 800;
	color: var(--accent);
	line-height: 1;
}

.msgr-avatar img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.msgr-avatar.initials-avatar {
	color: #fff;
}

.header-avatar-btn {
	width: 34px;
	height: 34px;
	border-radius: 50%;
	border: 1px solid var(--border);
	background: var(--surf2);
	cursor: pointer;
	padding: 0;
	overflow: hidden; /* clips .header-avatar-btn:empty's shimmer sweep to the circle */
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	position: relative;
}
.header-avatar-btn:empty {
	background: var(--surf2) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236868a0' stroke-width='2'%3E%3Ccircle cx='12' cy='8' r='4'/%3E%3Cpath d='M4 20c0-4 4-6 8-6s8 2 8 6'/%3E%3C/svg%3E") center/60% no-repeat;
}
/* Same compositor-only sweep as .date-btn.loading (styles.css) -- shown
   while renderHeaderAvatar() hasn't filled the button in yet, so the
   header's avatar slot reads as loading the same way the date pill does
   instead of just sitting there as a bare placeholder icon. */
.header-avatar-btn:empty::after {
	content: '';
	position: absolute;
	inset: 0;
	transform: translateX(-120%);
	background: linear-gradient(100deg, transparent 30%, var(--surface) 50%, transparent 70%);
	animation: skel-sweep 1.6s ease-in-out infinite;
	will-change: transform;
	pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
	.header-avatar-btn:empty::after { animation: none; content: none; }
}
.header-avatar-btn img,
.header-avatar-btn .msgr-avatar {
	width: 100%;
	height: 100%;
	border: none;
	border-radius: 50%;
}
.profile-photo-row {
	display: flex;
	align-items: center;
	gap: 14px;
	margin-top: -6px;
}
.profile-photo-preview {
	width: 64px;
	height: 64px;
	flex-shrink: 0;
}
.profile-photo-preview .msgr-avatar {
	width: 64px;
	height: 64px;
	font-size: 30px;
}
.profile-photo-acts {
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.modal-overlay {
	position: fixed; inset: 0;
	background: rgba(0,0,0,.55);
	/* Matches .modal-bg's blur (Sponsor modal) so every full-screen
	   overlay -- Profile, image crop, image viewer -- blurs the app
	   behind it the same way instead of just dimming it. */
	backdrop-filter: blur(4px);
	-webkit-backdrop-filter: blur(4px);
	z-index: 1100;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* The avatar picker can be opened from on top of an already-open
   profile modal ("Or pick an avatar instead"). Both are .modal-overlay
   (z-index 1100), so without this the picker -- earlier in the DOM
   than #profileModal -- would render BEHIND it and look like nothing
   happened when tapped. One tier above the base modal z-index puts it
   reliably on top regardless of DOM order. */
.avatar-picker-overlay {
	z-index: 1150;
}
.modal-box {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 14px;
	padding: 24px;
	width: 340px;
	max-width: 95vw;
	display: flex;
	flex-direction: column;
	gap: 14px;
}
.modal-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
}

#cropModal {
	z-index: 1150;
}

.crop-modal-box {
	align-items: center;
}

.crop-stage {
	position: relative;
	width: 260px;
	height: 260px;
	max-width: 100%;
	border-radius: 50%;
	overflow: hidden;
	background: #000;
	margin: 0 auto;
	touch-action: none;
	cursor: grab;
	box-shadow: 0 0 0 2000px rgba(0,0,0,.35);
}

.crop-stage:active {
	cursor: grabbing;
}

/* Rectangular variant used when cropping to the home-banner aspect ratio
   (see imagecrop.js's openBannerCropper()) -- same drag/zoom stage as the
   circular avatar cropper, just square corners and a wide box instead of
   a circle, sized to match .banner-carousel's own 2.35/1 aspect-ratio so
   what the person frames here is exactly what will show on the banner. */
.crop-stage.crop-stage-rect {
	width: 329px;
	height: 140px;
	border-radius: 10px;
}

.crop-stage img {
	position: absolute;
	top: 0;
	left: 0;
	transform-origin: 0 0;
	user-select: none;
	-webkit-user-drag: none;
	pointer-events: none;
	max-width: none;
}

.crop-zoom-row {
	display: flex;
	align-items: center;
	gap: 8px;
	color: var(--muted);
	padding: 0 4px;
}

.crop-zoom-slider {
	flex: 1;
	-webkit-appearance: none;
	appearance: none;
	height: 4px;
	border-radius: 2px;
	background: var(--border);
	outline: none;
}

.crop-zoom-slider::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: var(--accent);
	cursor: pointer;
	border: 2px solid #fff;
}

.crop-zoom-slider::-moz-range-thumb {
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: var(--accent);
	cursor: pointer;
	border: 2px solid #fff;
}

/* Avatar picker (live animated emoji avatars) -- see avatarpicker.js.
   Reachable from both the mandatory signup photo step and the profile
   modal's "Change photo" row via an "Or choose an avatar" link.
   Replaces the old grid of ~10 illustrated .png files with a
   searchable grid of animated emoji (Smileys & Emotion only -- an
   avatar is a face) -- each tile plays a real looping GIF, not a
   static glyph. */
/* see assets/js/avatarpicker.js _avatarPickerGridHtml() */
.avatar-picker-modal-box {
	width: 460px;
	max-height: 82vh;
	overflow-y: auto;
}
.avatar-picker-search-row {
	position: sticky;
	top: 0;
	z-index: 2;
	display: flex;
	align-items: center;
	gap: 8px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 12px;
	padding: 9px 12px;
	color: var(--muted);
	margin-bottom: 10px;
}
.avatar-picker-search-row svg {
	flex-shrink: 0;
	opacity: .8;
}
.avatar-picker-search {
	flex: 1;
	border: none;
	background: none;
	outline: none;
	font-size: 14px;
	font-family: var(--sans);
	color: var(--text);
}
.avatar-picker-search::placeholder {
	color: var(--muted);
}
.avatar-picker-group-label {
	display: flex;
	align-items: center;
	gap: 6px;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: .05em;
	text-transform: uppercase;
	color: var(--accent-soft);
	margin: 16px 0 10px;
	padding-bottom: 8px;
	border-bottom: 1px solid var(--border);
}
.avatar-picker-group-label:first-child {
	margin-top: 0;
}
.avatar-picker-grid {
	display: grid;
	grid-template-columns: repeat(6, 1fr);
	gap: 8px;
}
.avatar-picker-empty {
	text-align: center;
	color: var(--muted);
	font-size: 13px;
	padding: 30px 10px;
}
.avatar-picker-item {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	aspect-ratio: 1;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 12px;
	cursor: pointer;
	padding: 0;
	overflow: hidden;
	transition: border-color .15s, transform .12s, background .15s, box-shadow .15s;
}
.avatar-picker-item img {
	width: 78%;
	height: 78%;
	object-fit: contain;
}
.avatar-picker-item:hover,
.avatar-picker-item:focus-visible {
	border-color: var(--accent);
	background: color-mix(in srgb, var(--accent) 10%, var(--surf2));
	transform: translateY(-2px) scale(1.06);
	box-shadow: 0 6px 18px rgba(0,0,0,.25), 0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
}
.avatar-picker-item:active {
	transform: translateY(0) scale(.95);
}
@media (max-width: 480px) {
	.avatar-picker-modal-box { width: 100%; }
	.avatar-picker-grid { grid-template-columns: repeat(5, 1fr); gap: 6px; }
}

/* Live animated-emoji avatar rendering everywhere an avatar shows up
   (header, profile modal, club chat/DM messages, member lists,
   comments, People tab) -- see avatarMarkup()/emojiAvatarImgHtml() in
   helpers.js and avatarHtml() in profile.js. Sits inside the same
   circular .msgr-avatar/.club-member-avatar etc. wrapper a photo
   avatar would, just with the animated GIF filling it instead of a
   plain <img> photo. */
.emoji-avatar,
.emoji-avatar-wrap {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	line-height: 1;
	background: var(--surf2);
	overflow: hidden;
}
.emoji-avatar-img {
	width: 100%;
	height: 100%;
	object-fit: contain;
}
.auth-avatar-alt {
	text-align: center;
	margin-top: 2px;
}
.auth-avatar-alt-btn {
	background: none;
	border: none;
	color: var(--accent-soft);
	font-size: 13px;
	cursor: pointer;
	padding: 4px;
	font-family: var(--sans);
}
.auth-avatar-alt-btn:hover {
	color: var(--accent);
	text-decoration: underline;
}

.auth-terms-field {
	margin-bottom: 12px;
}
.auth-terms-check {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	font-size: 12.5px;
	color: var(--muted);
	line-height: 1.5;
	cursor: pointer;
}
.auth-terms-check input[type="checkbox"] {
	appearance: none;
	-webkit-appearance: none;
	width: 16px;
	height: 16px;
	margin-top: 2px;
	padding: 0;
	box-sizing: border-box;
	border-radius: 4px;
	border: 1.5px solid var(--border);
	background: var(--surf2);
	flex-shrink: 0;
	cursor: pointer;
	position: relative;
	transition: background .15s ease, border-color .15s ease, transform .1s ease;
}
.auth-terms-check input[type="checkbox"]:hover {
	border-color: var(--accent-soft, var(--accent));
}
.auth-terms-check input[type="checkbox"]:checked {
	background: var(--accent);
	border-color: var(--accent);
}
.auth-terms-check input[type="checkbox"]:active {
	transform: scale(.88);
}
.auth-terms-link {
	background: none;
	border: none;
	color: var(--accent);
	font-weight: 600;
	font-size: 12.5px;
	cursor: pointer;
	padding: 0;
	text-decoration: underline;
	font-family: var(--sans);
}
.auth-terms-field.field-error .auth-terms-check {
	color: var(--err);
}

/* Animated tick shared by the auth Terms checkbox above and the Sponsor
   beuclub checkbox below (see .sponsor-terms-check input[type="checkbox"]
   further down) -- both are appearance:none custom boxes, so nothing
   draws a check mark on its own. Drawn as a small rotated "L" of two
   borders rather than an image/SVG. It scales + fades in from the
   center on check and reverses on uncheck, instead of the box just
   instantly flipping color with no mark at all. */
.auth-terms-check input[type="checkbox"]::after,
.sponsor-terms-check input[type="checkbox"]::after {
	content: '';
	position: absolute;
	left: 4px;
	top: 1px;
	width: 5px;
	height: 9px;
	border: solid #fff;
	border-width: 0 2px 2px 0;
	transform: rotate(45deg) scale(0);
	opacity: 0;
	transition: transform .18s cubic-bezier(.34, 1.56, .64, 1), opacity .12s ease;
}
.auth-terms-check input[type="checkbox"]:checked::after,
.sponsor-terms-check input[type="checkbox"]:checked::after {
	transform: rotate(45deg) scale(1);
	opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
	.auth-terms-check input[type="checkbox"],
	.sponsor-terms-check input[type="checkbox"],
	.auth-terms-check input[type="checkbox"]::after,
	.sponsor-terms-check input[type="checkbox"]::after {
		transition: none;
	}
}
.auth-terms-modal-box {
	width: 480px;
	max-width: 92vw;
	/* Same 390px floor as .auth-box/.auth-screen -- without it, at
	   narrow widths 92vw shrinks below what the text/buttons can lay
	   out in (see the squished screenshot this was reported from), long
	   before the rest of the page's own 390px floor kicks in, since this
	   modal is its own position:fixed overlay (#authTermsModal below)
	   rather than living inside .auth-screen. min-width beats max-width
	   here when they conflict, which is exactly the point. */
	min-width: 390px;
	flex-shrink: 0;
	max-height: 82vh;
	display: flex;
	flex-direction: column;
}
#authTermsModal {
	/* Lets the overlay itself scroll horizontally once auth-terms-modal-box's
	   390px floor above no longer fits, instead of clipping it or letting it
	   overflow the viewport with no way to reach the rest of it. */
	overflow-x: auto;
}
.auth-terms-modal-body {
	overflow-y: auto;
	padding-right: 4px;
	margin-bottom: 10px;
}
.auth-terms-modal-body .card-title:first-child {
	margin-top: 0;
}
.auth-terms-header-actions {
	display: flex;
	align-items: center;
	gap: 6px;
	flex-shrink: 0;
}
#authTermsListenBtn.is-speaking {
	color: var(--accent);
	border-color: color-mix(in srgb, var(--accent) 45%, transparent);
	background: color-mix(in srgb, var(--accent) 14%, transparent);
}
/* Colored + widened so it's obviously "there's more below" rather than
   blending into the default thin gray scrollbar (see html::-webkit-scrollbar
   above) that's easy to miss on a dark modal. */
.auth-terms-modal-body::-webkit-scrollbar {
	width: 10px;
}
.auth-terms-modal-body::-webkit-scrollbar-track {
	background: color-mix(in srgb, var(--accent) 10%, transparent);
	border-radius: 6px;
}
.auth-terms-modal-body::-webkit-scrollbar-thumb {
	background: var(--accent);
	border-radius: 6px;
	border: 2px solid var(--surface);
	background-clip: padding-box;
}
.auth-terms-modal-body::-webkit-scrollbar-thumb:hover {
	background: var(--accent-soft);
	background-clip: padding-box;
}
.auth-terms-modal-body {
	scrollbar-color: var(--accent) color-mix(in srgb, var(--accent) 10%, transparent);
}
.auth-terms-scroll-hint {
	font-size: 12px;
	font-weight: 600;
	color: var(--accent);
	background: color-mix(in srgb, var(--accent) 14%, transparent);
	border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
	border-radius: 8px;
	text-align: center;
	padding: 7px 10px;
	margin-bottom: 8px;
	animation: authTermsHintPulse 1.6s ease-in-out infinite;
}
@keyframes authTermsHintPulse {
	0%, 100% { opacity: 1; }
	50% { opacity: 0.55; }
}

.profile-logout-btn {
	background: var(--err);
	border-color: var(--err);
	color: #fff;
}
.profile-logout-btn:hover {
	background: #c23a4d;
	border-color: #c23a4d;
}

.chat-reply-preview {
	font-size: 11px;
	color: var(--muted);
	background: var(--surf2);
	border-left: 3px solid var(--accent);
	border-radius: 4px;
	padding: 4px 8px;
	margin-bottom: 4px;
	display: flex;
	gap: 6px;
	flex-wrap: wrap;
}
.chat-reply-name { color: var(--accent); font-weight: 600; }
.chat-edited { font-size: 10px; color: var(--muted); font-style: italic; }
.chat-deleted { color: var(--muted); font-style: italic; }
.chat-msg-menu-wrap {
	position: absolute;
	top: 4px;
	right: -6px;
	opacity: 0;
	transition: opacity .15s;
}
.chat-msg { position: relative; }
.chat-msg:hover .chat-msg-menu-wrap,
.chat-msg-menu-wrap.open { opacity: 1; }
.chat-msg-me .chat-msg-menu-wrap { right: auto; left: -6px; }
.chat-msg-menu-btn {
	width: 22px;
	height: 22px;
	border-radius: 50%;
	border: none;
	background: rgba(0,0,0,.35);
	color: var(--text);
	font-size: 13px;
	line-height: 1;
	cursor: pointer;
}
.chat-msg-menu-btn:hover { background: rgba(0,0,0,.55); }
.chat-msg-menu {
	position: absolute;
	top: 24px;
	right: 0;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 8px;
	padding: 4px;
	min-width: 110px;
	z-index: 8;
	box-shadow: 0 8px 20px rgba(0,0,0,.4);
}
.chat-msg-me .chat-msg-menu { right: auto; left: 0; }
.chat-msg-menu button {
	display: block;
	width: 100%;
	text-align: left;
	font-size: 12px;
	background: none;
	border: none;
	color: var(--text);
	padding: 6px 8px;
	border-radius: 5px;
	cursor: pointer;
}
.chat-msg-menu button:hover { background: var(--border); }

.compose-context {
	font-size: 12px;
	padding: 5px 10px;
	border-radius: 6px;
	display: flex;
	align-items: center;
	gap: 6px;
}
.compose-context.reply { background: rgba(124,106,247,.12); border-left: 3px solid var(--accent); }
.compose-context.edit  { background: rgba(255,200,0,.08);   border-left: 3px solid #f5c518; }
.compose-context button {
	background: none; border: none; cursor: pointer;
	color: var(--muted); font-size: 14px; margin-left: auto;
}

.app-view {
	display: none;
}

.app-view.active {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
}

/* The sweep used to be driven by animating `background-position` on
   .skel itself (a 300%-wide gradient sliding from 200% to -200%). That
   forces the browser to repaint the element every frame instead of just
   compositing it, which on a lot of devices (especially inside a Tauri
   webview, with no fully hardware-accelerated 2D canvas) shows up as a
   stuttery, uneven shimmer rather than a smooth sweep — worse the more
   skeleton rows are on screen at once, since each one repaints
   independently every frame. Moving the highlight onto its own ::after
   layer and animating `transform: translateX()` instead lets the
   compositor handle it (GPU, no repaint), which is both smoother and
   cheaper with many rows on screen at once. */
@keyframes skel-sweep {
	0%   { transform: translateX(-120%); }
	100% { transform: translateX(120%); }
}
.skel {
	position: relative;
	overflow: hidden;
	background: var(--surf2);
	border-radius: var(--r);
	pointer-events: none;
}
.skel::after {
	content: '';
	position: absolute;
	inset: 0;
	transform: translateX(-120%);
	background: linear-gradient(100deg, transparent 30%, var(--surface) 50%, transparent 70%);
	animation: skel-sweep 1.6s ease-in-out infinite;
	will-change: transform;
}
.skel-line {
	height: 11px;
	border-radius: 5px;
}
.skel-circle {
	border-radius: 50%;
	flex-shrink: 0;
}
@media (prefers-reduced-motion: reduce) {
	.skel::after { animation: none; content: none; }
}

.skel-banner {
	position: absolute;
	inset: 0;
}

/* Mirrors .quicklink-card's own box model (padding, flex gap) instead
   of a flat colored block, so the skeleton occupies exactly the same
   position/dimensions as the real card it's standing in for -- see
   SKEL_QUICKLINK_CARD in view-home.js for the markup this styles. */
.skel-quicklink {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--r);
	padding: 16px 10px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 10px;
}
.skel-quicklink-icon {
	width: 40px;
	height: 40px;
	border-radius: 10px;
	flex-shrink: 0;
}
.skel-quicklink-sub {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	width: 100%;
}

.skel-comment {
	display: flex;
	gap: 10px;
}
.skel-comment .skel-circle {
	width: 34px;
	height: 34px;
}
.skel-comment-col {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding-top: 3px;
}

/* Minimal flat skimmer -- one plain shimmering rounded-rect block per
   row instead of mirroring the real row's internals (icon circle + two
   text lines). The detailed version drifted out of sync with real rows
   any time their content changed (a badge added/removed, a line
   wrapping differently) and read as busier than a loading state needs
   to be; a single block communicates "loading" just as clearly with
   far less visual noise. Height (60px) still roughly matches a real
   .club-list-item/.comment-item's rendered height so nothing jumps when
   the real content swaps in. */
.skel-row {
	height: 60px;
	border-radius: 10px;
}

.banner-carousel {
	position: relative;
	border-radius: 14px;
	overflow: hidden;
	border: 1px solid var(--border);
	margin-bottom: 22px;
	aspect-ratio: 2.35 / 1;
	background: var(--surface);
}

.banner-track {
	position: relative;
	width: 100%;
	height: 100%;
}

.banner-slide {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: center;
	display: flex;
	align-items: flex-start;
	opacity: 0;
	transition: opacity .5s ease;
	pointer-events: none;
}

/* Event banners with a link (see sponsor.js's sponsorLinkInput) render as
   a real <a> instead of a <div> -- only these should look clickable. */
.banner-slide-linked {
	cursor: pointer;
}

.banner-slide.active {
	opacity: 1;
	pointer-events: auto;
}

.banner-slide::before {
	content: '';
	position: absolute;
	inset: 0;
	background: linear-gradient(90deg, rgba(8,7,13,.8) 0%, rgba(8,7,13,.45) 32%, rgba(8,7,13,.12) 58%, transparent 82%);
}

/* A video slide's .beu-player (see home.js's BANNER_SLIDES `video`
   field) sits absolutely behind the gradient overlay and copy, filling
   the slide exactly like the background-image it layers on top of.
   Below 1024px width the player never fetches anything (see its own
   data-min-viewport gating) -- the slide's plain background-image
   (used as the player's data-poster too) is all that ever shows there. */
.banner-slide .beu-player {
	position: absolute;
	inset: 0;
	z-index: 0;
	border-radius: 0;
}

.banner-copy {
	position: relative;
	z-index: 1;
	padding: clamp(16px, 4vw, 40px);
	max-width: 60%;
}

.banner-headline {
	font-size: clamp(22px, 4.4vw, 38px);
	font-weight: 800;
	line-height: 1.25;
	color: #fff;
	letter-spacing: -.01em;
}

.banner-headline .accent {
	color: var(--accent-soft, #a79bfa);
}

.banner-tagline {
	margin-top: 10px;
	font-size: clamp(13px, 2.6vw, 16.5px);
	color: rgba(255,255,255,.78);
	line-height: 1.6;
	max-width: 320px;
}

.banner-arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	z-index: 2;
	width: 32px;
	height: 32px;
	border-radius: 50%;
	background: rgba(13,13,15,.55);
	border: 1px solid rgba(255,255,255,.15);
	color: #fff;
	font-size: 18px;
	line-height: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	backdrop-filter: blur(2px);
	transition: background .15s;
}

.banner-arrow:hover {
	background: rgba(124,106,247,.55);
}

/* Same reasoning as .pw-toggle:active above: the global
   button:active scale would otherwise replace this button's own
   translateY(-50%) centering and knock it out of vertical center
   while pressed. */
.banner-arrow:active {
	transform: translateY(-50%) scale(0.9);
}

.banner-arrow-l { left: 12px; }
.banner-arrow-r { right: 12px; }

.banner-dots {
	position: absolute;
	bottom: 12px;
	left: 50%;
	transform: translateX(-50%);
	z-index: 2;
	display: flex;
	gap: 6px;
}

.banner-dot {
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: rgba(255,255,255,.35);
	cursor: pointer;
	border: none;
	padding: 0;
	transition: background .15s, width .15s;
}

.banner-dot.active {
	background: var(--accent);
	width: 16px;
	border-radius: 3px;
}

.uni-section {
	margin-bottom: 24px;
}

.uni-title-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	margin-bottom: 14px;
}

.uni-title {
	font-size: clamp(13px, 2.6vw, 15px);
	font-weight: 800;
	letter-spacing: .02em;
	color: var(--text);
}

.uni-icons {
	display: flex;
	gap: 8px;
	flex-shrink: 0;
}

.uni-icon-btn {
	width: 30px;
	height: 30px;
	border-radius: 50%;
	background: #16151d;
	border: 1px solid var(--border);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	transition: border-color .15s, background .15s;
}

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

.quicklink-grid {
	display: grid;
	grid-template-columns: repeat(5, 1fr);
	gap: 10px;
}

@media (max-width: 700px) {
	.quicklink-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 420px) {
	.quicklink-grid { grid-template-columns: repeat(2, 1fr); }
}

.quicklink-card {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--r);
	padding: 16px 10px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 10px;
	text-align: center;
	cursor: pointer;
	transition: border-color .15s, transform .12s;
}

.quicklink-card:hover {
	border-color: var(--accent);
	transform: translateY(-1px);
}

.quicklink-icon {
	width: 40px;
	height: 40px;
	border-radius: 10px;
	background: rgba(124, 106, 247, .16);
	background: color-mix(in srgb, var(--accent) 16%, transparent);
	color: var(--accent);
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
}

/* Some COLLEGE_QUICKLINK_ICONS entries (home.js) hard-code their SVG's
   own width/height at native art-board size -- e.g. "transport" is
   320x288 and "room" is 380x400 -- far larger than this 40x40 box.
   Without this rule those icons rendered at full size and visually
   overflowed past the card's edge; because that overflow was still a
   DOM descendant of the card, clicks landing on the "empty" space
   outside the visible card border were actually landing on the
   oversized icon and triggering the card's data-onclick handler. This
   forces every icon to scale down (or up) to fill its box exactly, so
   nothing renders -- or is clickable -- outside the card's own bounds. */
.quicklink-icon svg {
	max-width: 100%;
	max-height: 100%;
}

/* Nearby services (Vendors / Transports / Rooms / Sell-Buy): full-colour art on a dark navy tile, in both themes. */
.quicklink-icon.ql-art {
	background: #252544;
	color: inherit;
}
.quicklink-icon.ql-art svg { width: 78%; height: 78%; }

/* Home quick-link icons: PNG artwork that already carries its own rounded dark tile, so no box background of ours. */
.quicklink-icon.ql-img {
	width: 48px;
	height: 48px;
	background: none;
	border-radius: 0;
	overflow: visible;
}
.quicklink-icon.ql-img img { width: 100%; height: 100%; object-fit: contain; display: block; pointer-events: none; user-select: none; }

.quicklink-label {
	font-size: 12px;
	font-weight: 700;
	color: var(--text);
}

.quicklink-sub {
	font-size: 10.5px;
	color: var(--muted);
	line-height: 1.4;
}

.comments-section {
	margin-bottom: 24px;
}

.comment-composer {
	display: flex;
	align-items: center;
	gap: 10px;
	margin-bottom: 22px;
}

.comment-avatar {
	width: 34px;
	height: 34px;
	border-radius: 50%;
	background: var(--accent);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 12px;
	font-weight: 800;
	flex-shrink: 0;
	overflow: hidden;
}

.comment-avatar img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.comment-input {
	flex: 1;
	min-width: 0;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 20px;
	padding: 9px 16px;
	color: var(--text);
	font-family: var(--sans);
	font-size: 13px;
}

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

.comment-list {
	display: flex;
	flex-direction: column;
	gap: 14px;
}

.comment-item {
	display: flex;
	gap: 10px;
}

.comment-body-col {
	flex: 1;
	min-width: 0;
}

.comment-meta {
	display: flex;
	align-items: baseline;
	gap: 8px;
	flex-wrap: wrap;
}

.comment-name {
	font-size: 12.5px;
	font-weight: 700;
	color: var(--text);
}

.comment-time {
	font-size: 11px;
	color: var(--muted);
}

.comment-text {
	font-size: 12.5px;
	color: var(--text);
	line-height: 1.6;
	margin-top: 2px;
	word-break: break-word;
}

.comment-like-btn {
	display: flex;
	align-items: center;
	gap: 4px;
	background: none;
	border: 1px solid transparent;
	border-radius: 6px;
	color: var(--muted);
	font-size: 11px;
	font-family: var(--mono);
	cursor: pointer;
	flex-shrink: 0;
	align-self: flex-start;
	padding: 2px 6px;
	outline: none;
	transition: color .15s, background .15s, border-color .15s;
}

.comment-like-btn:focus-visible {
	outline: 2px solid var(--accent-soft);
	outline-offset: 1px;
}

.comment-like-btn.liked,
.comment-like-btn.has-likes {
	color: var(--accent-soft);
	background: color-mix(in srgb, var(--accent-soft) 14%, transparent);
	border-color: color-mix(in srgb, var(--accent-soft) 38%, transparent);
}

.comment-like-btn.just-liked svg {
	animation: commentHeartPop .4s ease;
	transform-origin: center;
}

@keyframes commentHeartPop {
	0%   { transform: scale(1); }
	35%  { transform: scale(1.35); }
	60%  { transform: scale(0.9); }
	100% { transform: scale(1); }
}

/* club-msg-actions/club-msg-action-btn are shared with club.js's chat
   bubbles (see that file's near-identical actionsHtml block), which are
   absolutely-positioned and only reveal on hover of the whole bubble.
   A comment row is an ordinary flex row instead, so the wrapper needs
   its own layout here -- same buttons, same icons, just laid out
   inline next to the like button instead of floating over a bubble. */
.comment-item .comment-actions.club-msg-actions {
	flex-direction: row;
	align-self: flex-start;
	opacity: 1;
	flex-shrink: 0;
}

.comment-empty {
	color: var(--muted);
	font-size: 12.5px;
	text-align: center;
	padding: 10px 0;
}

.view-all-comments-btn {
	display: block;
	margin: 16px auto 0;
	background: none;
	border: none;
	color: var(--accent);
	font-weight: 700;
	font-size: 12.5px;
	cursor: pointer;
}

.club-card {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
}

.club-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
	overflow-y: auto;
	flex: 1;
	min-height: 0;
}

.club-list-item {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	width: 100%;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 12px 14px;
	color: var(--text);
	cursor: pointer;
	text-align: left;
	transition: border-color .15s, background .15s;
}

.club-list-item:hover {
	border-color: var(--accent);
	background: var(--surf2);
}

.club-list-item-icon {
	width: 36px;
	height: 36px;
	flex-shrink: 0;
	border-radius: 9px;
	background: var(--accent);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 13px;
	font-weight: 800;
	font-family: var(--mono);
	overflow: hidden;
}

.club-list-item-icon img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.club-list-item-name {
	flex: 1;
	min-width: 0;
	font-size: 13.5px;
	font-weight: 700;
	line-height: 1.35;
	white-space: normal;
	overflow-wrap: break-word;
	padding-top: 7px;
}

.club-list-item svg {
	flex-shrink: 0;
	color: var(--muted);
	margin-top: 9px;
}

/* Toggle pill in the CLUBS/FRIENDS card title that flips the list
   between its normal view and the archived-only one (see
   switchClubArchiveView()/switchFriendArchiveView() in club.js). Sits
   alongside the section label the same way "+ New Club" already does,
   just styled as plain text rather than a filled button so it doesn't
   compete with it for attention. */

.people-section {
	margin-top: 16px;
}

.people-section.people-section-flush {
	margin-top: 0;
}

.people-section-label {
	font-size: 11.5px;
	font-weight: 700;
	letter-spacing: .04em;
	color: var(--muted);
	text-transform: uppercase;
	margin-bottom: 8px;
}

.people-row-handle {
	font-weight: 400;
	color: var(--muted);
	font-size: 11.5px;
	margin-top: 2px;
}

.people-row-actions {
	display: flex;
	align-items: center;
	gap: 6px;
	flex-shrink: 0;
	padding-top: 2px;
}

.empty {
	text-align: center;
	color: var(--muted);
	font-size: 12.5px;
	padding: 40px 20px;
}

/* Bounded to the viewport between the header and bottom nav (both stay
   put and stay clickable) instead of just being a flex child of the
   normal page flow. Chat used to be laid out as flex:1 inside .app-view -> .app -> body, but
   none of those ancestors actually have a capped height (body is only
   min-height:100vh), so .club-messages below never became a genuinely
   bounded/overflowing box -- the whole PAGE grew to fit every message and
   scrolled instead, which left the compose row wherever it happened to
   fall in that very long flow (hence needing to scroll all the way down
   just to see it) and made wrap.scrollTop = wrap.scrollHeight a no-op
   (the wrap's scrollHeight never exceeded its clientHeight, since it was
   free to just grow taller instead of overflowing). Fixing it to the
   viewport between the header and bottom nav gives .club-messages a real
   height to overflow within, so the compose row stays visibly pinned at
   the bottom of the chat and the existing scrollTop-to-bottom calls in
   club.js/renderClubMessages() and renderDmMessages() actually work. */
.club-chat-pane {
	position: fixed;
	top: var(--header-h, 64px);
	bottom: var(--bottom-nav-h, calc(76px + env(safe-area-inset-bottom)));
	left: 50%;
	transform: translateX(-50%);
	width: min(75%, 1100px);
	max-width: 95vw;
	z-index: 100;
	display: flex;
	flex-direction: column;
	min-height: 0;
	background: var(--bg);
	padding: 0 14px 14px;
}

@media (max-width: 1100px) {
	.club-chat-pane { width: 90%; max-width: 90vw; }
}

@media (max-width: 900px) {
	.club-chat-pane { width: 95%; max-width: 95vw; }
}

@media (max-width: 600px) {
	.club-chat-pane { width: 100%; max-width: 100vw; padding: 0 10px 10px; }
}

.club-chat-header {
	flex-shrink: 0;
	margin-top: 14px;
}

/* Sub-group ("topic") tab strip inside an open club's chat -- Lobby +
   any created groups (see club_groups / renderClubGroupTabs() in
   club.js). Sits right under the chat header, above the message list;
   hidden entirely when there's only the Lobby and nobody who could add
   a group (see renderClubGroupTabs()'s showRow check). */

.club-back-btn {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 8px;
	color: var(--text);
	width: 32px;
	height: 32px;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
}

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

.club-chat-title {
	font-size: 14.5px;
	font-weight: 800;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	flex: 1;
	min-width: 0;
}

.btn.ghost.sm.danger,
.btn.ghost.danger {
	color: var(--danger, #e5484d);
}
.btn.ghost.sm.danger:hover,
.btn.ghost.danger:hover {
	border-color: var(--danger, #e5484d);
	background: var(--surf2);
}

/* Disabled variant of the list row's Join pill -- "Invite only"
   (join_mode='invite_link') or "Requested" (join_mode='request', already
   applied) -- see _clubJoinBtnHtml() in club.js. A <span>, not a
   <button>, since neither is actually clickable; styled to sit at the
   same size/weight as the real .btn.primary.sm Join button next to it
   in the other branches, just muted instead of accent-colored. */

/* Join-requests inbox button in the club chat header -- see
   openClubJoinRequestsModal() in club.js. Only shown to that club's
   moderator on a join_mode='request' club. */

.club-messages {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	display: flex;
	flex-direction: column;
	gap: 12px;
	padding: 4px 2px 10px;
}

/* Date divider ("Today" / "Yesterday" / weekday / full date) between
   messages sent on different calendar days -- shared by Club chat and
   Friends (DM) chat, both of which use .club-messages as their list
   container. See _messagesHtmlWithDateDividers()/_dateDividerLabel()
   in club.js. */
.chat-date-divider {
	display: flex;
	justify-content: center;
	margin: 4px 0 2px;
	/* Pinned at the top-centre of the message list while its day's
	   messages scroll underneath (bounded by .chat-day-group). */
	position: sticky;
	top: 6px;
	z-index: 3;
	pointer-events: none;
}
.chat-day-group {
	display: flex;
	flex-direction: column;
	gap: inherit;
	flex-shrink: 0;
}

.chat-date-divider span {
	font-size: 10.5px;
	font-weight: 800;
	letter-spacing: 0.02em;
	color: var(--text-muted, var(--accent-soft));
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 999px;
	padding: 3px 12px;
}

.club-msg {
	display: flex;
	align-items: flex-end;
	gap: 8px;
	max-width: 82%;
}

.club-msg.mine {
	align-self: flex-end;
	flex-direction: row-reverse;
}

.club-msg-avatar {
	width: 26px;
	height: 26px;
	flex-shrink: 0;
	border-radius: 50%;
	background: var(--accent);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 10px;
	font-weight: 800;
	font-family: var(--mono);
	overflow: hidden;
}

.club-msg-avatar img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.club-msg-bubble {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 12px;
	padding: 8px 11px;
	min-width: 0;
	position: relative;
}

/* Extra headroom so the corner-badge role tag (see .club-msg-mod-tag
   below) never sits on top of the sender name/text -- content is
   pushed down below the badge instead of overlapping it horizontally,
   which stays correct no matter how narrow the bubble is. */
.club-msg-bubble.has-role-tag {
	padding-top: 22px;
}

.club-msg.mine .club-msg-bubble {
	background: color-mix(in srgb, var(--accent) 22%, var(--surface));
	border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
}

.club-msg-name {
	font-size: 10.5px;
	font-weight: 800;
	color: var(--accent-soft);
	margin-bottom: 2px;
}

.club-msg-text {
	font-size: 13px;
	line-height: 1.5;
	white-space: pre-wrap;
	word-break: break-word;
}

.club-msg-image {
	display: block;
	max-width: 220px;
	max-height: 220px;
	width: 100%;
	height: auto;
	border-radius: 8px;
	cursor: pointer;
	object-fit: cover;
}

.club-msg-actions {
	display: flex;
	flex-direction: column;
	gap: 4px;
	flex-shrink: 0;
	opacity: 0;
	transition: opacity .15s;
}
.club-msg:hover .club-msg-actions,
.club-msg:focus-within .club-msg-actions {
	opacity: 1;
}
@media (hover: none) {
	/* Touch devices have no hover -- keep the icons reachable rather than
	   hidden behind a gesture that doesn't exist here. */
	.club-msg-actions { opacity: .55; }
}

.club-msg-action-btn {
	width: 22px;
	height: 22px;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 6px;
	color: var(--text-dim, #9a9aa5);
	cursor: pointer;
}
.club-msg-action-btn:hover {
	border-color: var(--accent);
	color: var(--text);
}

.club-msg-edited {
	font-size: 10.5px;
	color: var(--text-dim, #9a9aa5);
	font-weight: 600;
}

.club-msg-deleted {
	font-size: 13px;
	font-style: italic;
	color: var(--text-dim, #9a9aa5);
}

/* DM placeholder the other person sees after a sender deletes a message: block icon + label */
.dm-deleted-note {
	display: inline-flex;
	align-items: center;
	gap: 6px;
}
.dm-deleted-note .dm-deleted-icon {
	flex: 0 0 auto;
	opacity: .85;
}

/* Clickable link inside a chat bubble (see linkifyText() in mentions.js) */
.club-msg-link {
	color: var(--accent-soft);
	text-decoration: underline;
	word-break: break-all;
}
.club-msg.mine .club-msg-link {
	color: inherit;
	opacity: .9;
}

/* Optional caption under an image message (club-send-image.php/
   dm-send-image.php's `caption` field) -- same text styling as a
   regular text bubble, just with a little breathing room from the
   image above it. */
.club-msg-caption {
	font-size: 13px;
	line-height: 1.5;
	white-space: pre-wrap;
	word-break: break-word;
	margin-top: 6px;
}

/* "Moderator"/"Developer" tag for a club sender -- see the modTag
   block in _buildClubMsgHtml() and the tag var in
   _renderClubMembersList() (club.js). Rendered as a corner badge (not
   inline next to the name) in both places it appears: the chat
   bubble (.club-msg-bubble) and the members list row
   (.club-member-row) -- both of those are position:relative so this
   pins to their upper-right corner. */

/* Emoji reaction pills row under a bubble's body, and the "+" button
   that opens the picker -- see _reactionsBarHtml()/openReactionPicker()
   in club.js. */
.club-msg-reactions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 4px;
	margin-top: 6px;
}

.club-msg-reaction-pill {
	display: inline-flex;
	align-items: center;
	gap: 3px;
	padding: 1px 7px;
	font-size: 12px;
	line-height: 1.7;
	border-radius: 999px;
	border: 1px solid var(--border);
	background: var(--surf2);
	color: var(--text);
	cursor: pointer;
}
.club-msg-reaction-pill.mine {
	border-color: var(--accent);
	background: color-mix(in srgb, var(--accent) 18%, var(--surf2));
}
.club-msg-reaction-count {
	font-size: 10.5px;
	font-weight: 700;
	color: var(--text-dim, #9a9aa5);
}
.club-msg-reaction-pill.mine .club-msg-reaction-count {
	color: var(--accent-soft);
}

.club-msg-reaction-add {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 20px;
	height: 20px;
	border-radius: 50%;
	border: 1px solid var(--border);
	background: transparent;
	color: var(--text-dim, #9a9aa5);
	cursor: pointer;
	opacity: 0;
	transition: opacity .15s;
}
.club-msg:hover .club-msg-reaction-add,
.club-msg:focus-within .club-msg-reaction-add {
	opacity: 1;
}
@media (hover: none) {
	.club-msg-reaction-add { opacity: .55; }
}
.club-msg-reaction-add:hover {
	border-color: var(--accent);
	color: var(--text);
}

/* Floating emoji picker popup -- position (top/left) is set inline by
   openReactionPicker() in club.js since it's anchored to whichever "+"
   button was tapped, inside a scrolling chat container. */
.club-reaction-picker {
	position: fixed;
	z-index: 400;
	display: flex;
	gap: 2px;
	padding: 6px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 999px;
	box-shadow: 0 6px 20px rgba(0,0,0,.25);
}
.club-reaction-picker-btn {
	width: 30px;
	height: 30px;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 18px;
	background: transparent;
	border: none;
	border-radius: 50%;
	cursor: pointer;
}
.club-reaction-picker-btn:hover {
	background: var(--surf2);
}

.club-msg-edit-row {
	display: flex;
	align-items: flex-end;
	gap: 6px;
}

/* System-authored announcements (join/removal -- see
   post_club_system_message() in common.php, rendered by the
   msg_type==='system' branch of _buildClubMsgHtml()). No bubble, no
   avatar, just a small centered line, same visual family as a date
   divider. */
.club-msg-system {
	align-self: center;
	max-width: 90%;
	text-align: center;
	font-size: 11.5px;
	color: var(--text-dim, #9a9aa5);
	padding: 2px 10px;
	margin: 2px 0;
}

/* Audio / video call log in a friend chat (msg_type==='call', see _dmCallLogHtml() in clubs-and-dms.js).
   A small centred pill, same family as the system lines above. */
.dm-call-log {
	align-self: center;
	max-width: 92%;
	margin: 6px 0;
	display: flex;
	justify-content: center;
}
.dm-call-log-pill {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 6px 12px;
	border-radius: 999px;
	background: var(--surf2);
	border: 1px solid var(--border);
	font-size: 12.5px;
	color: var(--text-dim, #9a9aa5);
}
.dm-call-log-pill b { color: var(--text, inherit); font-weight: 600; }
.dm-call-log-icon { display: inline-flex; color: var(--accent); }
.dm-call-log-time { font-size: 11px; opacity: .8; }
.dm-call-log.missed .dm-call-log-icon,
.dm-call-log.missed .dm-call-log-pill b { color: var(--err, #ff5c6c); }

.club-msg.pinned .club-msg-bubble {
	border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
}

/* Pinned-messages strip above the message list -- see
   renderClubPinnedBar() in club.js. Hidden entirely when nothing's
   pinned. */

/* Brief highlight when jumping to a message from the pinned bar -- see
   scrollToClubMsg() in club.js. */
.flash-highlight .club-msg-bubble {
	animation: flashHighlight 1.2s ease-out;
}
@keyframes flashHighlight {
	0% { box-shadow: 0 0 0 2px var(--accent); }
	100% { box-shadow: 0 0 0 0 transparent; }
}

/* Members modal -- see openClubMembersModal()/_renderClubMembersList()
   in club.js. Reuses the confirm-bg/confirm-box modal shell everywhere
   else here already uses (see .confirm-box). */
/* Same spacing .confirm-msg used for the old plain-text "Members"
   heading, now that it's a .modal-header row (title + X button) instead
   -- see openClubMembersModal() in club.js. */
.club-members-list {
	overflow-y: auto;
	max-height: 60vh;
	display: flex;
	flex-direction: column;
	gap: 2px;
	margin: 8px 0;
}
/* Reserves room on the right so the corner-badge role tag (see
   .club-msg-mod-tag) never overlaps a long display name/handle --
   only applied when this row actually carries a tag. A moderator/
   developer row never also shows the Remove button (server-side
   can_remove excludes them, see club-members-list.php), so there's
   no conflict between the two. */
/* Wraps Make Moderator + Remove when a row shows both (a developer
   viewing a plain member) -- see _renderClubMembersList() in club.js. */

/* Link preview card -- see assets/js/link-preview.js. Sits right under
   a bubble's text/caption, same "attached to the content, not a
   separate floating element" placement a WhatsApp/iMessage-style
   preview uses. */
.link-preview-card {
	display: block;
	margin-top: 6px;
	border: 1px solid var(--border);
	border-radius: 8px;
	overflow: hidden;
	background: var(--surf2);
	max-width: 260px;
}
.link-preview-inner {
	display: block;
	color: inherit;
	text-decoration: none;
}
.link-preview-image {
	display: block;
	width: 100%;
	max-height: 140px;
	object-fit: cover;
}
.link-preview-text {
	padding: 7px 10px;
}
.link-preview-site {
	font-size: 10px;
	text-transform: uppercase;
	letter-spacing: .02em;
	font-weight: 700;
	color: var(--text-dim, #9a9aa5);
}
.link-preview-title {
	font-size: 12.5px;
	font-weight: 700;
	margin-top: 2px;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.link-preview-desc {
	font-size: 11.5px;
	color: var(--text-dim, #9a9aa5);
	margin-top: 2px;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.link-preview-skeleton {
	height: 44px;
	background: linear-gradient(90deg, var(--surf2) 25%, var(--surface) 50%, var(--surf2) 75%);
	background-size: 200% 100%;
	animation: linkPreviewShimmer 1.4s ease-in-out infinite;
}
@keyframes linkPreviewShimmer {
	0% { background-position: 200% 0; }
	100% { background-position: -200% 0; }
}

.club-msg-edit-input {
	flex: 1;
	min-width: 120px;
	background: var(--surf2);
	border: 1px solid var(--accent);
	border-radius: 7px;
	color: var(--text);
	font-family: var(--sans);
	font-size: 13px;
	padding: 5px 8px;
	/* Now a <textarea> (was a single-line <input>) so an in-place edit
	   can keep a message's newlines/formatting -- see startEditMsg()'s
	   _autoGrowComposeInput() call in club.js. */
	resize: none;
	min-height: 30px;
	max-height: 160px;
	line-height: 1.35;
	overflow-y: auto;
}

/* True full-viewport takeover -- covers the header and bottom nav too
   (z-index above both, see header's 120 and .bottom-nav's 150), with an
   opaque background and no side/top/bottom gaps at any width. This used
   to be "bounded like the rest of the app shell" (fixed between the
   header and bottom nav, centered at a capped width) on purpose, so
   those stayed visible/usable underneath -- but that meant Home's own
   content was directly visible (and, since the body underneath was
   never scroll-locked, could still visibly scroll/shift) through the
   gaps above, below, and on both sides of the panel for however long it
   took the iframe to actually finish loading. openExternalWebView()/
   _hideExternalWebView() (extwebview.js) toggle a body class that locks
   background scrolling for the same reason. */
/* Set on <body> by openExternalWebView()/_hideExternalWebView()
   (extwebview.js) for as long as the overlay above is open --
   position:fixed (not just overflow:hidden) so an in-progress touch
   drag/rubber-band scroll on iOS Safari can't keep the background
   moving underneath even mid-gesture. topOnLock (an inline custom
   property set alongside the class -- see extwebview.js) offsets body
   by the scroll position it was at, so releasing the lock doesn't jump
   the page back to the top. */
body.ext-web-open {
	position: fixed;
	top: calc(-1 * var(--ext-web-scroll-y, 0px));
	left: 0;
	right: 0;
	overflow: hidden;
}

/* BUGFIX: this rule used to be bounded between the header and bottom nav
   (top: var(--header-h); bottom: var(--bottom-nav-h)) -- the "old"
   behavior the comment above describes moving away FROM, but the actual
   position/z-index below never got updated to match, so it silently
   stayed bounded. That's exactly what let Home's header/content peek
   through above, below, and (at wide viewports, since it was also
   capped/centered like the rest of the app shell) on both sides of this
   panel. Same z-index tier as .modal-overlay (1100) -- also a true
   fullscreen takeover -- so it reliably sits above the header (120) and
   bottom nav (150) with nothing bleeding through. */
.ext-web-overlay {
	position: fixed;
	inset: 0;
	z-index: 1100;
	display: flex;
	flex-direction: column;
	background: #0d0d0f;
	padding: 0;
}

.ext-web-overlay .result-pdf-topbar {
	margin-top: 0;
	/* Now covers the header too (see .ext-web-overlay above), so this
	   topbar IS the top edge of the screen -- give it the safe-area
	   inset padding the real header would otherwise have provided. */
	padding: calc(10px + env(safe-area-inset-top)) 14px 10px;
}

.ext-web-overlay .result-pdf-wrap {
	padding: 0 14px 14px;
}

.image-viewer-overlay {
	position: fixed;
	inset: 0;
	background: rgba(0, 0, 0, .88);
	z-index: 1200;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 40px 20px;
}

.image-viewer-img {
	max-width: 92vw;
	max-height: 88vh;
	width: auto;
	height: auto;
	border-radius: 10px;
	object-fit: contain;
	box-shadow: 0 20px 60px rgba(0, 0, 0, .5);
}

.image-viewer-close {
	position: fixed;
	top: 18px;
	right: 18px;
	width: 40px;
	height: 40px;
	display: flex;
	align-items: center;
	justify-content: center;
	border: none;
	border-radius: 50%;
	background: rgba(255, 255, 255, .1);
	color: #fff;
	cursor: pointer;
	transition: background .15s;
}

.image-viewer-close:hover {
	background: rgba(255, 255, 255, .2);
}

.club-msg-time {
	font-size: 9.5px;
	color: var(--muted);
	margin-top: 4px;
}

.club-image-preview {
	font-size: 11.5px;
	color: var(--muted);
	padding: 4px 2px;
	flex-shrink: 0;
}

.club-compose-row {
	display: flex;
	/* flex-end (not center) so the attach/send buttons stay pinned to
	   the bottom of a multi-line textarea instead of re-centering on
	   its full height as it grows -- see .club-msg-input below. */
	align-items: flex-end;
	gap: 8px;
	flex-shrink: 0;
	margin-bottom: 14px;
	/* .club-chat-pane is now a bounded overlay (fixed between the header
	   and bottom nav, see its own comment above) with .club-messages as
	   the only child that actually scrolls -- this row is just a normal,
	   non-shrinking flex item sitting after it, so it's always on screen
	   without needing position:sticky. Kept position:relative + a solid
	   background so message bubbles can't visibly peek out from behind
	   its edges while .club-messages scrolls underneath. */
	position: relative;
	z-index: 2;
	background: var(--bg);
	padding-top: 6px;
}

.club-attach-btn {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 8px;
	color: var(--muted);
	width: 36px;
	height: 36px;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	transition: color .15s, border-color .15s;
}

.club-attach-btn:hover:not(:disabled) {
	color: var(--accent);
	border-color: var(--accent);
}

.club-attach-btn:disabled {
	opacity: .5;
	cursor: default;
}

.club-msg-input {
	flex: 1;
	min-width: 0;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 8px;
	padding: 9px 12px;
	color: var(--text);
	font-family: var(--sans);
	font-size: 13px;
	/* Now a <textarea> (was a single-line <input>) so Shift+Enter can
	   start a new line -- see _wireEnterToSend()/_autoGrowComposeInput()
	   in club.js. resize:none since height is driven by that JS, not a
	   user drag-handle; max-height caps how tall it can grow before it
	   starts scrolling internally instead of pushing the rest of the
	   compose row off-screen. */
	resize: none;
	min-height: 36px;
	max-height: 160px;
	line-height: 1.35;
	overflow-y: auto;
}

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

/* Rendered message-body styles for the formatting the compose toolbar
   above inserts -- see formatChatMarkdown()/formatQuotes() in
   mentions.js, which is what actually turns the typed syntax into these
   tags once a message renders. */
.club-msg-quote {
	position: relative;
	margin: 4px 0;
	padding: 7px 30px 7px 12px;
	background: rgba(0, 0, 0, .25);
	border-left: 3px solid #e8892b;
	border-radius: 6px;
	color: var(--text);
}
/* Quote mark in the top-right corner of the quote block */
.club-msg-quote::after {
	content: "\201D";
	position: absolute;
	top: 2px;
	right: 9px;
	font: 700 22px/1 Georgia, serif;
	color: #e8892b;
	pointer-events: none;
}
/* Monospace: real monospace face, bold blue, no chip */
.club-msg-code {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
	font-weight: 600;
	color: #4d8fd6;
	background: none;
	padding: 0;
	font-size: .95em;
}
/* Spoiler: hidden behind a static/noise texture until tapped (toggled by
   the inline data-onclick on the span, see formatChatMarkdown()). */
.club-msg-spoiler {
	cursor: pointer;
	border-radius: 3px;
	color: transparent;
	text-shadow: none;
	user-select: none;
	-webkit-user-select: none;
	background-color: rgba(0, 0, 0, .3);
	background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='40'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 .75 0 0 0 0 .8 0 0 0 0 .85 0 0 0 2.4 -.95'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
	background-size: 80px 40px;
}
.club-msg-spoiler.revealed {
	color: inherit;
	user-select: text;
	-webkit-user-select: text;
	background-image: none;
	background-color: rgba(0, 0, 0, .2);
}

.bottom-nav {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 150;
	display: flex;
	justify-content: space-evenly;
	background: rgba(13,13,15,.92);
	backdrop-filter: blur(10px);
	border-top: 1px solid var(--border);
	padding: 8px 10px calc(8px + env(safe-area-inset-bottom));
}

.bottom-nav-btn {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 3px;
	background: none;
	border: none;
	color: var(--muted);
	cursor: pointer;
	font-family: var(--sans);
	font-size: 10px;
	font-weight: 700;
	letter-spacing: .03em;
	padding: 4px 10px;
	transition: color .15s;
	text-decoration: none;
	position: relative;
}

.nav-unseen-badge {
	position: absolute;
	top: -1px;
	right: 4px;
	min-width: 15px;
	height: 15px;
	padding: 0 4px;
	border-radius: 8px;
	background: var(--err);
	color: #fff;
	font-size: 9.5px;
	font-weight: 800;
	line-height: 15px;
	text-align: center;
}

.toast-container {
	position: fixed;
	top: calc(12px + env(safe-area-inset-top));
	left: 50%;
	transform: translateX(-50%);
	z-index: 9999;
	display: flex;
	flex-direction: column;
	gap: 8px;
	width: min(380px, calc(100vw - 24px));
	pointer-events: none;
}

.app-toast {
	position: relative;
	overflow: hidden;
	pointer-events: auto;
	display: flex;
	align-items: flex-start;
	gap: 10px;
	background: rgba(24,24,28,.97);
	border: 1px solid var(--border);
	border-radius: 12px;
	padding: 11px 12px;
	box-shadow: 0 8px 24px rgba(0,0,0,.35);
	cursor: pointer;
	opacity: 0;
	transform: translateY(-10px);
	transition: opacity .2s ease, transform .2s ease;
}

.app-toast.app-toast-in {
	opacity: 1;
	transform: translateY(0);
}

.app-toast.app-toast-out {
	opacity: 0;
	transform: translateY(-10px);
}

.app-toast-icon {
	color: var(--accent);
	flex-shrink: 0;
	margin-top: 1px;
}

.app-toast-text {
	flex: 1;
	min-width: 0;
}

.app-toast-title {
	font-size: 13px;
	font-weight: 700;
	color: var(--text);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.app-toast-body {
	font-size: 12px;
	color: var(--muted);
	margin-top: 1px;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.app-toast-close {
	background: none;
	border: none;
	color: var(--muted);
	cursor: pointer;
	font-size: 11px;
	padding: 2px 4px;
	flex-shrink: 0;
}
.app-toast-close:hover {
	color: var(--text);
}

.app-toast-mute {
	background: none;
	border: 1px solid var(--border);
	border-radius: 6px;
	color: var(--muted);
	cursor: pointer;
	font-size: 10px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .03em;
	padding: 3px 7px;
	flex-shrink: 0;
	align-self: center;
}
.app-toast-mute:hover {
	color: var(--text);
	border-color: var(--text);
}

.app-toast-countdown {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 3px;
	background: transparent;
	overflow: hidden;
}
.app-toast-countdown-bar {
	width: 100%;
	height: 100%;
	background: var(--accent);
	opacity: .55;
	transform: scaleX(1);
	transform-origin: left center;
	transition: transform linear;
}

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

/* Tab switch: the label turns bold (same accent colour) and the icon grows, then settles back to its normal size.
   Unselected labels are a lighter weight so the change is visible; a fixed min-width stops the tabs shifting
   sideways when a label gets bolder. */
.bottom-nav-btn {
	min-width: 64px;
	font-weight: 500;
	transition: color .15s, font-weight .2s ease;
}
.bottom-nav-btn.active { font-weight: 800; }
.bottom-nav-btn svg { transform-origin: center; }
.bottom-nav-btn.active svg { animation: navIconPop .38s cubic-bezier(.3, 1.6, .5, 1); }
@keyframes navIconPop {
	0%   { transform: scale(1); }
	45%  { transform: scale(1.35); }
	100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
	.bottom-nav-btn.active svg { animation: none; }
}

.bottom-nav-btn:hover:not(.active) {
	color: var(--text);
}

.sponsor-fab {
	position: fixed;
	right: clamp(14px, 4vw, 24px);
	bottom: calc(var(--bottom-nav-h, calc(76px + env(safe-area-inset-bottom))) + 14px);
	z-index: 140;
	width: 48px;
	height: 48px;
	border-radius: 50%;
	border: 1px solid var(--border);
	background: var(--surf2);
	color: var(--a2);
	cursor: pointer;
	padding: 0;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	box-shadow: 0 8px 22px -6px rgba(0,0,0,.55);
	transition: all .15s;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.sponsor-fab:hover {
	background: var(--border);
	color: var(--text);
	transform: translateY(-2px);
}

.sponsor-terms-note {
	font-size: 12px;
	color: var(--muted);
	line-height: 1.5;
	text-align: center;
	margin: 10px 0 4px;
}

.sponsor-terms-check {
	display: flex;
	align-items: flex-start;
	gap: 8px;
	font-size: 12.5px;
	font-weight: 400;
	color: var(--muted);
	line-height: 1.45;
	margin: 4px 0 14px;
	user-select: none;
	text-transform: none;
	letter-spacing: normal;
}

.sponsor-terms-check input[type="checkbox"] {
	appearance: none;
	-webkit-appearance: none;
	margin-top: 2px;
	width: 17px;
	height: 17px;
	padding: 0;
	box-sizing: border-box;
	border-radius: 5px;
	border: 1.5px solid var(--border);
	background: var(--surf2);
	flex-shrink: 0;
	cursor: pointer;
	position: relative;
	transition: background .15s ease, border-color .15s ease, transform .1s ease;
}

.sponsor-terms-check input[type="checkbox"]:hover {
	border-color: var(--accent-soft, var(--accent));
}

.sponsor-terms-check input[type="checkbox"]:checked {
	background: var(--accent);
	border-color: var(--accent);
}

.sponsor-terms-check input[type="checkbox"]:active {
	transform: scale(.88);
}

.sponsor-pick-cards {
	display: flex;
	flex-direction: column;
	gap: 10px;
	margin-bottom: 14px;
}

.sponsor-card {
	text-align: left;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 12px;
	padding: 14px 16px;
	cursor: pointer;
	transition: border-color .15s, background .15s;
	font-family: var(--sans);
	color: var(--text);
}

.sponsor-card:hover {
	border-color: var(--accent);
	background: var(--border);
}

.sponsor-card-title {
	font-size: 14px;
	font-weight: 700;
	margin-bottom: 4px;
}

.sponsor-card-blurb {
	font-size: 12px;
	color: var(--muted);
	line-height: 1.4;
	margin-bottom: 8px;
}

.sponsor-card-price {
	font-size: 13px;
	font-weight: 700;
	color: var(--text);
	font-family: var(--mono);
}

.sponsor-card-skeleton {
	cursor: default;
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.sponsor-card-skeleton:hover {
	border-color: var(--border);
	background: var(--surf2);
}

.sponsor-skel-line {
	position: relative;
	overflow: hidden;
	height: 12px;
	border-radius: 4px;
	background: var(--border);
}

/* Same transform-based sweep as .skel (see styles.css's SKELETON /
   SKIMMER block) instead of animating background-position -- keeps this
   shimmer compositor-accelerated and in sync, visually, with every other
   skimmer in the app rather than each using its own paint-heavy version. */
.sponsor-skel-line::after {
	content: '';
	position: absolute;
	inset: 0;
	transform: translateX(-120%);
	background: linear-gradient(90deg, transparent 25%, rgba(255, 255, 255, 0.12) 50%, transparent 75%);
	animation: sponsorSkelShimmer 1.3s ease-in-out infinite;
	will-change: transform;
}

.sponsor-skel-title {
	width: 42%;
	height: 13px;
}

.sponsor-skel-blurb {
	width: 78%;
}

@keyframes sponsorSkelShimmer {
	0%   { transform: translateX(-120%); }
	100% { transform: translateX(120%); }
}

@media (prefers-reduced-motion: reduce) {
	.sponsor-skel-line::after { animation: none; content: none; }
}

.sponsor-strike {
	text-decoration: line-through;
	color: var(--muted);
	font-weight: 500;
	margin-right: 4px;
}

.sponsor-discount-banner {
	background: rgba(232, 147, 74, .12);
	border: 1px solid var(--a2);
	color: var(--a2);
	border-radius: 10px;
	padding: 8px 12px;
	font-size: 12px;
	font-weight: 700;
	text-align: center;
	margin-bottom: 4px;
}

.sponsor-chip-row {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
	margin-top: -6px;
	margin-bottom: 14px;
}

.sponsor-img-preview {
	width: 100%;
	height: 160px;
	border-radius: 10px;
	background-color: var(--surf2);
	background-size: contain;
	background-repeat: no-repeat;
	background-position: center;
	border: 1px solid var(--border);
	margin-top: 8px;
}

.sponsor-price-preview {
	font-size: 13px;
	font-weight: 700;
	color: var(--text);
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 10px 12px;
	margin-top: 4px;
}

.sponsor-qr-wrap {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 8px;
	margin-bottom: 14px;
}

.sponsor-qr-box {
	background: #fff;
	padding: 12px;
	border-radius: 12px;
	width: 224px;
	height: 224px;
	display: flex;
	align-items: center;
	justify-content: center;
}

.sponsor-pay-amount {
	font-size: 24px;
	font-weight: 800;
	font-family: var(--mono);
	color: var(--text);
}

.sponsor-pay-upi {
	font-size: 12px;
	color: var(--muted);
	font-family: var(--mono);
}

#sponsorTextInput {
	resize: none;
	font-size: 13px;
	line-height: 1.5;
	white-space: pre-wrap;
	word-break: break-word;
}

#mSponsor .modal {
	scrollbar-width: thin;
	scrollbar-color: rgba(150,150,170,.45) transparent;
}

#mSponsor .modal::-webkit-scrollbar {
	width: 8px;
}

#mSponsor .modal::-webkit-scrollbar-button {
	display: none;
	width: 0;
	height: 0;
}

/* margin keeps the track (and its ends) clear of the rounded corners */
#mSponsor .modal::-webkit-scrollbar-track {
	background: transparent;
	margin: 14px 0;
}

#mSponsor .modal::-webkit-scrollbar-thumb {
	background: rgba(150,150,170,.4);
	border-radius: 10px;
	border: 2px solid transparent;
	background-clip: padding-box;
}

#mSponsor .modal::-webkit-scrollbar-thumb:hover {
	background: rgba(150,150,170,.7);
	border: 2px solid transparent;
	background-clip: padding-box;
}

.sponsor-mine-list {
	margin-top: 4px;
	max-height: 220px;
	overflow-y: auto;
	padding-right: 4px;
	scrollbar-width: thin;
	scrollbar-color: var(--border) var(--surface);
}

.sponsor-mine-list::-webkit-scrollbar {
	width: 6px;
}

.sponsor-mine-list::-webkit-scrollbar-track {
	background: var(--surface);
}

.sponsor-mine-list::-webkit-scrollbar-thumb {
	background: var(--border);
	border-radius: 10px;
}

.sponsor-mine-list::-webkit-scrollbar-thumb:hover {
	background: var(--muted);
}

.sponsor-mine-title {
	font-size: 11px;
	font-weight: 700;
	color: var(--muted);
	text-transform: uppercase;
	letter-spacing: .06em;
	margin-bottom: 8px;
}

.sponsor-mine-row {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: 8px 0;
	border-top: 1px solid var(--border);
	font-size: 12px;
}

.sponsor-mine-row-top {
	display: flex;
	align-items: center;
	gap: 8px;
}

.sponsor-mine-meta {
	font-size: 11px;
	color: var(--muted);
}

.sponsor-mine-type {
	flex: 1;
	font-weight: 600;
}

.sponsor-mine-amount {
	font-family: var(--mono);
	color: var(--muted);
}

.sponsor-mine-status {
	padding: 3px 9px;
	border-radius: 20px;
	font-size: 11px;
	font-weight: 700;
	background: var(--surf2);
	border: 1px solid var(--border);
}

.sponsor-mine-status.status-active { color: var(--ok); border-color: var(--ok); }
.sponsor-mine-status.status-pending_verification,
.sponsor-mine-status.status-pending_payment { color: var(--warn); border-color: var(--warn); }
.sponsor-mine-status.status-rejected { color: var(--err); border-color: var(--err); }
.sponsor-mine-status.status-expired { color: var(--muted); }

.sponsor-admin-reminder {
	font-size: 12.5px;
	color: var(--warn, #e8934a);
	background: color-mix(in srgb, var(--warn, #e8934a) 12%, transparent);
	border: 1px solid color-mix(in srgb, var(--warn, #e8934a) 30%, transparent);
	border-radius: 8px;
	padding: 8px 10px;
	line-height: 1.45;
	margin-bottom: 12px;
}

.sponsor-admin-list {
	display: flex;
	flex-direction: column;
	gap: 10px;
	max-height: 60vh;
	overflow-y: auto;
}

/* ---------- Results: Auto-find popup (see resultautofind.js) ---------- */
.autofind-branch-row {
	display: grid;
	grid-template-columns: 1.4fr 1fr 1fr auto;
	gap: 6px;
	margin-bottom: 8px;
}

.autofind-branch-row input {
	min-width: 0;
}

@media (max-width: 480px) {
	.autofind-branch-row { grid-template-columns: 1fr 1fr; }
	.autofind-branch-row button { grid-column: 1 / -1; justify-self: end; }
}

.autofind-output {
	width: 100%;
	margin-top: 10px;
	margin-bottom: 8px;
	min-height: 110px;
	font-family: var(--sans);
	font-size: 12px;
	line-height: 1.5;
	white-space: pre;
	overflow-x: auto;
	resize: vertical;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 8px;
	color: var(--text);
	padding: 10px;
}

.autofind-sep {
	border: none;
	border-top: 1px solid var(--border);
	margin: 18px 0 12px;
}

.autofind-saved-label {
	display: block;
	font-size: 12px;
	font-weight: 700;
	color: var(--muted);
	text-transform: uppercase;
	letter-spacing: .02em;
	margin-bottom: 8px;
}

.autofind-saved-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
	margin-bottom: 10px;
	max-height: 30vh;
	overflow-y: auto;
}

.autofind-saved-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 8px;
	padding: 8px 10px;
}

.autofind-saved-name {
	font-size: 13px;
	min-width: 0;
	overflow-wrap: anywhere;
}

.autofind-saved-actions {
	display: flex;
	gap: 6px;
	flex-shrink: 0;
}

.sponsor-admin-row {
	display: flex;
	gap: 10px;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 10px;
}

.sponsor-admin-thumb {
	display: block;
	width: 56px;
	height: 56px;
	border-radius: 8px;
	background-size: cover;
	background-position: center;
	flex-shrink: 0;
}

.sponsor-admin-info {
	flex: 1;
	min-width: 0;
}

.sponsor-admin-line1 {
	font-size: 13px;
	font-weight: 700;
	margin-bottom: 2px;
}

.sponsor-admin-line2 {
	font-size: 11px;
	color: var(--muted);
	line-height: 1.4;
	word-break: break-word;
}

.sponsor-admin-actions {
	display: flex;
	flex-direction: column;
	gap: 6px;
	flex-shrink: 0;
	justify-content: center;
}

.sponsor-tag {
	position: absolute;
	top: 12px;
	right: 12px;
	background: rgba(232, 147, 74, .92);
	color: #1a1206;
	font-size: 10px;
	font-weight: 800;
	letter-spacing: .08em;
	padding: 3px 9px;
	border-radius: 20px;
	z-index: 2;
}

.syllabus-subject-block {
	margin-bottom: 8px;
}

.syllabus-branch-label {
	font-size: 11px;
	font-weight: 800;
	text-transform: uppercase;
	letter-spacing: .04em;
	color: var(--muted);
	margin: 0 0 10px;
}

.syllabus-branches {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
	gap: 8px 10px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 6px;
	margin-bottom: 18px;
}

.syllabus-elective-groups {
	margin-bottom: 4px;
}

.syllabus-elective-group {
	margin-bottom: 14px;
}

.syllabus-elective-group-label {
	font-size: 10.5px;
	font-weight: 800;
	text-transform: uppercase;
	letter-spacing: .04em;
	color: var(--accent);
	margin: 0 0 6px 2px;
}

.syllabus-branch-btn {
	display: block;
	width: 100%;
	text-align: center;
	padding: 9px 12px;
	border-radius: 7px;
	border: none;
	background: none;
	color: var(--muted);
	font-size: 13px;
	font-weight: 700;
	font-family: var(--sans);
	cursor: pointer;
	transition: all .14s;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.syllabus-branch-btn:hover:not(.active) { background: var(--surf2); color: var(--text); }

.syllabus-branch-btn.active {
	background: var(--a2);
	color: #1a1206;
}

.syllabus-hr {
	border: none;
	border-top: 1px solid var(--border);
	margin: 4px 0 20px;
}

.syllabus-group-title {
	font-size: 12.5px;
	font-weight: 800;
	color: var(--warn);
	margin: 16px 0 8px;
}

.syllabus-group-title:first-of-type { margin-top: 0; }

.syllabus-topic {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 12px 14px;
	margin-bottom: 10px;
}

.syllabus-topic.is-done { border-color: color-mix(in srgb, var(--ok) 45%, var(--border)); }

.syllabus-topic-text {
	font-size: 13px;
	line-height: 1.55;
	color: var(--text);
	margin-bottom: 10px;
}

/* BUGFIX: used to also set text-decoration:line-through here -- doesn't
   match how every other progress-tracked list marks something done
   (.pyq-paper-item.is-done above just fades its title/sub via opacity,
   no strikethrough), and the done-toggle itself already turns solid
   green (.pyq-paper-done-btn.done), so the strikethrough was a second,
   inconsistent "done" signal layered on top. Fade only, same as PYQ. */
.syllabus-topic.is-done .syllabus-topic-text { opacity: .48; }

.syllabus-topic-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
}

/* Push the round done-toggle to the far right of the actions row,
   clear of the Ask AI / YouTube pills, instead of sitting immediately
   after them wherever they happen to wrap to -- mirrors
   .pyq-paper-item's own layout (justify-content:space-between), where
   the done-toggle is already flush against the item's right edge. */
.syllabus-topic-actions .pyq-paper-done-btn {
	margin-left: auto;
}

.syllabus-action-btn {
	padding: 6px 12px;
	border-radius: 999px;
	font-size: 11.5px;
	font-weight: 700;
	font-family: var(--sans);
	cursor: pointer;
	background: transparent;
	transition: all .14s;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.syllabus-action-btn.ai { border: 1px solid var(--a3); color: var(--a3); }
.syllabus-action-btn.ai:hover { background: color-mix(in srgb, var(--a3) 15%, transparent); }

.syllabus-action-btn.yt { border: 1px solid var(--err); color: var(--err); }
.syllabus-action-btn.yt:hover { background: color-mix(in srgb, var(--err) 15%, transparent); }

/* Unit topic rows (syllabus.js): flat list -- done circle on the left, topic text, then square YouTube / Ask AI icon tiles. */
.syllabus-topic.syl-row {
	display: flex;
	align-items: center;
	gap: 12px;
	background: transparent;
	border: 0;
	border-radius: 0;
	padding: 3px 0;
	margin: 0;
}
.syl-row .syllabus-topic-text { flex: 1; min-width: 0; margin: 0; font-size: 14.5px; line-height: 1.4; }
.syl-row .syllabus-topic-actions { flex: none; flex-wrap: nowrap; gap: 6px; }
.syl-row .pyq-paper-done-btn { width: 24px; height: 24px; border-width: 2px; border-color: color-mix(in srgb, var(--accent) 55%, var(--muted)); margin-left: 0; }
.syl-ico-btn {
	width: 34px;
	height: 34px;
	flex: none;
	border-radius: 10px;
	border: 1px solid var(--border);
	background: var(--surface);
	color: var(--text);
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	transition: background .14s, transform .14s;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}
.syl-ico-btn:hover { background: color-mix(in srgb, var(--text) 8%, var(--surface)); }
.syl-ico-btn:active { transform: scale(.94); }

.syllabus-credits-toggle {
	display: flex;
	align-items: center;
	justify-content: space-between;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 10px 14px;
	margin-bottom: 10px;
	cursor: pointer;
	font-size: 13px;
	font-weight: 700;
	color: var(--text);
	transition: background .15s;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.syllabus-credits-toggle:hover { background: var(--surf2); }

.syllabus-credits-chevron {
	transition: transform .18s;
	color: var(--muted);
	flex-shrink: 0;
}

.syllabus-credits-toggle.open .syllabus-credits-chevron { transform: rotate(180deg); }

.syllabus-credits-wrap { margin-bottom: 4px; }

.syllabus-credits-table {
	width: 100%;
	border-collapse: collapse;
	font-size: 12px;
}

.syllabus-credits-table th, .syllabus-credits-table td {
	padding: 7px 10px;
	border-bottom: 1px solid var(--border);
	text-align: left;
}

.syllabus-credits-table th {
	color: var(--muted);
	font-weight: 800;
	text-transform: uppercase;
	font-size: 10px;
	letter-spacing: .05em;
	white-space: nowrap;
}

.syllabus-credits-table td { color: var(--text); }

.syllabus-credits-table th:not(:first-child),
.syllabus-credits-table td:not(:first-child) {
	text-align: center;
	white-space: nowrap;
}

.syllabus-credits-code {
	color: var(--muted);
	font-family: var(--mono);
	font-size: 10.5px;
}

.syllabus-credits-table tr.total-row td {
	font-weight: 800;
	color: var(--accent);
	border-top: 1px solid var(--border);
	border-bottom: none;
}

.syllabus-book-list {
	margin: 0 0 10px;
	padding-left: 20px;
	list-style: disc;
}

.syllabus-book-list li {
	font-size: 13px;
	line-height: 1.6;
	color: var(--text);
	margin-bottom: 6px;
}

.pyq-toolbar {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
	margin: 14px 0 18px;
	position: sticky;
	top: calc(var(--header-h, 60px) + 8px);
	z-index: 60;
}

.pyq-search-wrap {
	position: relative;
	width: 100%;
	min-width: 160px;
	display: flex;
}

.pyq-search {
	flex: 1;
	width: 100%;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 999px;
	padding: 11px 34px 11px 16px;
	color: var(--text);
	font-size: 13.5px;
	font-family: var(--sans);
	box-shadow: 0 8px 24px -8px rgba(0, 0, 0, 0.55);
}

.pyq-search:focus {
	outline: none;
	border-color: var(--accent);
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent);
}

.pyq-search-clear {
	position: absolute;
	top: 50%;
	right: 6px;
	transform: translateY(-50%) scale(.8);
	width: 26px;
	height: 26px;
	border-radius: 50%;
	border: none;
	background: transparent;
	color: var(--muted);
	font-size: 22px;
	line-height: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	padding: 0;
	opacity: 0;
	pointer-events: none;
	transition: opacity .14s ease, transform .14s ease, color .14s;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.pyq-search-clear.visible {
	opacity: 1;
	pointer-events: auto;
	transform: translateY(-50%) scale(1);
}

.pyq-search-clear:hover {
	color: var(--accent);
}

.pyq-hidden { display: none !important; }

.pyq-q-head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 12px;
	margin-bottom: 14px;
	padding-bottom: 12px;
	border-bottom: 1px solid color-mix(in srgb, var(--warn) 30%, var(--border));
}

.pyq-q-num { font-size: 13.5px; font-weight: 800; color: var(--warn); line-height: 1.5; }

.pyq-q-marks {
	flex-shrink: 0;
	font-size: 10.5px;
	font-weight: 700;
	color: var(--warn);
	border: 1px solid color-mix(in srgb, var(--warn) 45%, var(--border));
	border-radius: 999px;
	padding: 3px 9px;
	text-transform: uppercase;
	letter-spacing: .03em;
	white-space: nowrap;
}

.pyq-q-image {
	width: 100%;
	border-radius: 6px;
	border: 1px solid var(--border);
	background: #fff;
	display: block;
	margin: 8px 0 10px;
}

.pyq-q-text {
	font-size: 12.5px;
	line-height: 1.7;
	color: var(--text);
	margin-bottom: 10px;
}

.pyq-q-loading, .pyq-q-error {
	font-size: 13px;
	color: var(--muted);
	padding: 14px 2px;
}

.pyq-q-loading {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 14px;
	min-height: 40vh;
	text-align: center;
}

.syllabus-branch-grid > .pyq-q-loading,
.syllabus-branch-grid > .pyq-q-error {
	grid-column: 1 / -1;
}

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

.pyq-spinner {
	width: 34px;
	height: 34px;
	border-radius: 50%;
	border: 3px solid color-mix(in srgb, var(--accent) 25%, transparent);
	border-top-color: var(--accent);
	animation: pyq-spin .7s linear infinite;
}

@keyframes pyq-spin {
	to { transform: rotate(360deg); }
}

/* Generic small inline spinner for plain "Loading..." text rows (profile
   panels, mentions inbox, sponsor list, etc). Reuses the pyq-spin keyframe
   so all loading circles in the app rotate at the same speed. */
.beu-spinner-inline {
	display: inline-block;
	width: 14px;
	height: 14px;
	margin-right: 7px;
	vertical-align: -2px;
	border-radius: 50%;
	border: 2px solid color-mix(in srgb, var(--accent) 25%, transparent);
	border-top-color: var(--accent);
	animation: pyq-spin .7s linear infinite;
	flex: none;
}

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

.pyq-scroll-top {
	position: fixed;
	right: clamp(14px, 4vw, 24px);
	bottom: calc(var(--bottom-nav-h, calc(76px + env(safe-area-inset-bottom))) + 14px);
	z-index: 140;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	border: 1px solid var(--border);
	background: var(--surf2);
	color: var(--accent-soft);
	cursor: pointer;
	padding: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	box-shadow: 0 8px 22px -6px rgba(0, 0, 0, .55);
	opacity: 0;
	pointer-events: none;
	transform: translateY(8px);
	transition: opacity .18s ease, transform .18s ease, background .14s, color .14s;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.pyq-scroll-top.visible {
	opacity: 1;
	pointer-events: auto;
	transform: translateY(0);
}

.pyq-scroll-top:hover {
	background: var(--accent);
	color: #fff;
}

@property --about-wobble {
	syntax: '<angle>';
	inherits: false;
	initial-value: 0deg;
}

.about-team-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
	gap: 18px 14px;
}

.about-team-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 4px;
	padding: 16px 8px 14px;
	border: 1px solid var(--border);
	background: var(--surf2);
	opacity: 0;
	transform: translateY(26px) scale(.88) rotateX(35deg);
	transform-style: preserve-3d;
	perspective: 600px;
	will-change: transform, opacity;
}

.about-avatar {
	width: 100px;
	height: 190px;
	margin-bottom: 6px;
}

.about-avatar-svg {
	width: 100%;
	height: 100%;
	fill: none;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.about-draw-path {
	stroke: var(--accent-soft);
	stroke-width: 2.6;
	stroke-dasharray: var(--plen, 0);
	stroke-dashoffset: 0;
}

.about-limb {
	stroke-width: 8.5;
}

.about-avatar-dot,
.about-avatar-accent {
	fill: var(--accent-soft);
	opacity: 0;
	transform-box: fill-box;
	transform-origin: center;
}

.about-team-card:nth-child(2) .about-draw-path,
.about-team-card:nth-child(2) .about-avatar-dot,
.about-team-card:nth-child(2) .about-avatar-accent { stroke: var(--a2); fill: var(--a2); }
.about-team-card:nth-child(3) .about-draw-path,
.about-team-card:nth-child(3) .about-avatar-dot,
.about-team-card:nth-child(3) .about-avatar-accent { stroke: var(--a3); fill: var(--a3); }
.about-team-card:nth-child(4) .about-draw-path,
.about-team-card:nth-child(4) .about-avatar-dot,
.about-team-card:nth-child(4) .about-avatar-accent { stroke: var(--accent); fill: var(--accent); }

.draw-in .about-team-card {
	animation: about-card-in .7s cubic-bezier(.22, 1, .36, 1) forwards;
}
.draw-in .about-team-card .about-draw-path {
	animation: about-draw 1.1s cubic-bezier(.65, 0, .35, 1) forwards;
}
.draw-in .about-team-card .about-avatar-dot,
.draw-in .about-team-card .about-avatar-accent {
	animation: about-dot-in .6s cubic-bezier(.34, 1.56, .64, 1) forwards;
}
.draw-in .about-team-card .about-avatar {
	animation: about-glow-pulse .7s ease forwards, about-idle-sway 3.8s ease-in-out infinite;
}

.draw-in .about-team-card:nth-child(1) { animation-delay: 0s; }
.draw-in .about-team-card:nth-child(1) .about-draw-path { animation-delay: .35s; }
.draw-in .about-team-card:nth-child(1) .about-avatar-dot,
.draw-in .about-team-card:nth-child(1) .about-avatar-accent { animation-delay: 1.5s; }
.draw-in .about-team-card:nth-child(1) .about-avatar { animation-delay: 1.65s, 2s; }

.draw-in .about-team-card:nth-child(2) { animation-delay: .18s; }
.draw-in .about-team-card:nth-child(2) .about-draw-path { animation-delay: .53s; }
.draw-in .about-team-card:nth-child(2) .about-avatar-dot,
.draw-in .about-team-card:nth-child(2) .about-avatar-accent { animation-delay: 1.68s; }
.draw-in .about-team-card:nth-child(2) .about-avatar { animation-delay: 1.83s, 2.18s; }

.draw-in .about-team-card:nth-child(3) { animation-delay: .36s; }
.draw-in .about-team-card:nth-child(3) .about-draw-path { animation-delay: .71s; }
.draw-in .about-team-card:nth-child(3) .about-avatar-dot,
.draw-in .about-team-card:nth-child(3) .about-avatar-accent { animation-delay: 1.86s; }
.draw-in .about-team-card:nth-child(3) .about-avatar { animation-delay: 2.01s, 2.36s; }

.draw-in .about-team-card:nth-child(4) { animation-delay: .54s; }
.draw-in .about-team-card:nth-child(4) .about-draw-path { animation-delay: .89s; }
.draw-in .about-team-card:nth-child(4) .about-avatar-dot,
.draw-in .about-team-card:nth-child(4) .about-avatar-accent { animation-delay: 2.04s; }
.draw-in .about-team-card:nth-child(4) .about-avatar { animation-delay: 2.19s, 2.54s; }

@keyframes about-card-in {
	0% { opacity: 0; transform: translateY(26px) scale(.88) rotateX(35deg); }
	60% { opacity: 1; transform: translateY(-4px) scale(1.02) rotateX(0deg); }
	100% { opacity: 1; transform: translateY(0) scale(1) rotateX(0deg); }
}

@keyframes about-draw {
	0% { stroke-dashoffset: var(--plen, 0); }
	80% { stroke-dashoffset: -6; }
	100% { stroke-dashoffset: 0; }
}

@keyframes about-dot-in {
	0% { opacity: 0; transform: scale(0); }
	60% { opacity: 1; transform: scale(1.35); }
	100% { opacity: 1; transform: scale(1); }
}

@keyframes about-glow-pulse {
	0% { filter: drop-shadow(0 0 0 transparent); }
	50% { filter: drop-shadow(0 0 10px var(--accent-soft)); }
	100% { filter: drop-shadow(0 0 0 transparent); }
}

@keyframes about-idle-sway {
	0%, 100% { transform: rotate(var(--about-wobble)) translateY(0) scale(1); }
	50% { --about-wobble: 4deg; transform: rotate(var(--about-wobble)) translateY(-3px) scale(1.035); }
}

.policy-meta {
	font-size: 11px;
	font-weight: 600;
	color: var(--accent-soft);
	text-transform: uppercase;
	letter-spacing: .02em;
	margin-bottom: 8px;
}

.policy-text {
	font-size: 12.5px;
	line-height: 1.65;
	color: var(--text);
}

.policy-list {
	margin: 8px 0 0;
	padding-left: 18px;
	display: flex;
	flex-direction: column;
	gap: 6px;
}

.policy-list li {
	font-size: 12.5px;
	line-height: 1.6;
	color: var(--text);
}

/* Shared "legal document" layout -- modeled on X/Twitter's own legal
   pages: a plain, editorial flow of text broken up by hairline
   dividers and a jump-to table of contents, instead of the boxed
   .card treatment these views used to have. Used by the Cookies,
   Privacy and Terms views alike (see their view-*.js files for the
   markup this styles) -- kept under one shared "legal-" prefix rather
   than duplicated per view. */

.legal-hero {
	margin-top: 16px;
}

.legal-hero-list {
	margin: 10px 0 0;
	padding-left: 18px;
	display: flex;
	flex-direction: column;
	gap: 6px;
}

.legal-hero-list li {
	font-size: 12.5px;
	line-height: 1.6;
	color: var(--text);
}

.legal-hero-note {
	margin-top: 10px;
	color: var(--muted);
}

/* "Before you scroll, read this" callout -- X's Privacy Policy opens
   with a plain-English TL;DR box before the full legal text. Same
   idea here: a distinct tinted panel (not just another paragraph) so
   it reads as "the short version" at a glance, ending in a bolded
   one-line takeaway. */
.legal-tldr {
	margin-top: 16px;
	padding: 14px 16px;
	background: color-mix(in srgb, var(--accent) 8%, var(--surf2));
	border: 1px solid var(--border);
	border-left: 3px solid var(--accent);
}

.legal-tldr-title {
	font-size: 13px;
	font-weight: 800;
	color: var(--text);
	margin-bottom: 8px;
}

.legal-tldr-takeaway {
	margin-top: 10px;
	font-weight: 700;
	color: var(--text);
}

/* Jump-links row -- a plain wrapped list of pill-style anchors into the
   sections below, the way X's articles open with jump links right
   under the intro. Real <a href="#id"> anchors (see the note in
   view-cookies.js on why that's safe here) rather than data-onclick,
   so browser-native same-page scrolling and history both just work. */
.legal-toc {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin-top: 18px;
	padding: 14px;
	background: var(--surf2);
	border: 1px solid var(--border);
}

.legal-toc-link {
	font-size: 12px;
	font-weight: 600;
	color: var(--text);
	text-decoration: none;
	padding: 6px 12px;
	border: 1px solid var(--border);
	border-radius: 999px;
	background: var(--surface);
	white-space: nowrap;
}

.legal-toc-link:hover {
	color: var(--accent);
	border-color: var(--accent);
}

/* Sections flow directly on the page background (no .card border/bg)
   with a hairline rule between them doing the separating -- .legal-
   divider below. scroll-margin-top keeps a jumped-to section clear of
   the sticky site header (see `header { position: sticky }` above). */
.legal-section {
	margin-top: 22px;
	scroll-margin-top: 84px;
}

.legal-section-title {
	font-size: 15px;
	font-weight: 800;
	color: var(--text);
	margin-bottom: 10px;
}

.legal-divider {
	margin-top: 22px;
	border-top: 1px solid var(--border);
}

/* Category/sub-topic breakdown inside a section -- bold inline label +
   text, the way X's articles call out "Authentication and security",
   "Personal Accounts.", etc. as running sub-heads rather than a
   bullet list. */
.legal-category {
	margin-top: 12px;
	padding-left: 14px;
	border-left: 2px solid var(--border);
}

.legal-category-title {
	font-size: 12.5px;
	font-weight: 700;
	color: var(--accent);
	margin-bottom: 3px;
}

.legal-action-btn {
	margin-top: 14px;
}

.legal-footer-row {
	margin-top: 28px;
	padding-top: 16px;
	border-top: 1px solid var(--border);
	text-align: center;
}

.about-team-name {
	font-size: 12.5px;
	font-weight: 700;
	color: var(--text);
	margin-top: 2px;
}

.about-team-role {
	font-size: 11px;
	color: var(--muted);
}

.about-hero-immersive {
	position: relative;
	overflow: hidden;
	margin-top: 28px;
	border: 1px solid var(--border);
	background: radial-gradient(ellipse 120% 90% at 50% -10%, rgba(24, 35, 66, .82) 0%, rgba(10, 14, 28, .82) 48%, rgba(5, 6, 12, .82) 100%);
}

.about-hero-nightsky {
	position: absolute;
	inset: -10%;
	z-index: 0;
	pointer-events: none;
	background:
		radial-gradient(circle at 20% 25%, rgba(124, 106, 247, .22), transparent 55%),
		radial-gradient(circle at 82% 60%, rgba(64, 201, 168, .16), transparent 50%),
		radial-gradient(circle at 55% 10%, rgba(232, 147, 74, .12), transparent 45%);
	background-size: 140% 140%;
	filter: blur(40px);
	animation: about-nebula-drift 26s ease-in-out infinite;
}

@keyframes about-nebula-drift {
	0%, 100% { background-position: 0% 0%, 100% 100%, 50% 0%; }
	50%	  { background-position: 12% 8%, 88% 92%, 60% 12%; }
}

@media (prefers-reduced-motion: reduce) {
	.about-hero-nightsky { animation: none; }
}

#aboutStarsCanvas {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	z-index: 0;
	pointer-events: none;
}

#aboutBgCanvas {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	z-index: 1;
	pointer-events: none;
}

.about-hero-bgfade {
	position: absolute;
	inset: 0;
	z-index: 2;
	pointer-events: none;
	background:
		radial-gradient(ellipse 65% 60% at 25% 20%, rgba(5, 6, 12, .05), rgba(5, 6, 12, .55) 70%),
		linear-gradient(180deg, rgba(5, 6, 12, .04) 0%, rgba(5, 6, 12, .65) 100%);
}

.about-hero-inner {
	position: relative;
	z-index: 3;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	text-align: center;
	gap: 10px;
	padding: clamp(26px, 6vw, 44px);
	perspective: 1100px;
	height: calc(100vh - var(--header-h, 64px) - var(--bottom-nav-h, 76px) - 32px);
	min-height: 340px;
	box-sizing: border-box;
	bottom: 50px;
}

.about-hero-copy {
	position: relative;
	min-width: 0;
	max-width: 560px;
	transform-style: preserve-3d;
	will-change: transform;
}

.about-hero-visual {
	position: relative;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	margin-top: 26px;
	transform-style: preserve-3d;
	will-change: transform;
}

.about-hero-gif {
	display: block;
	width: 50vw;
	max-width: 480px;
	aspect-ratio: 400 / 306;
	object-fit: contain;
}

@media (max-width: 600px) {
	.about-hero-gif { width: 70vw; max-width: 260px; }
}

.about-hero-lede {
	position: relative;
	max-width: 420px;
	font-size: 13.5px;
	line-height: 1.72;
	color: var(--muted);
	margin-bottom: 20px;
	margin-left: auto;
	margin-right: auto;
}

.about-hero-social {
	position: relative;
	display: flex;
	justify-content: center;
	gap: 10px;
	margin-top: 16px;
}

.about-hero-social a {
	width: 32px;
	height: 32px;
	border-radius: 50%;
	border: 1.5px solid var(--border);
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--muted);
	text-decoration: none;
	background: var(--surface);
	transition: background .15s ease, color .15s ease, border-color .15s ease, transform .15s ease;
}

.about-hero-social a:hover {
	background: var(--surf2);
	border-color: var(--accent);
	color: var(--accent-soft);
	transform: translateY(-2px);
}

.about-hero-social svg {
	width: 14px;
	height: 14px;
	fill: currentColor;
}

.about-timeline {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 22px;
	padding-left: 6px;
}

.about-timeline::before {
	content: '';
	position: absolute;
	top: 6px;
	bottom: 6px;
	left: 14px;
	width: 2px;
	background: var(--border);
	opacity: .5;
}

.about-timeline-progress {
	position: absolute;
	top: 6px;
	left: 14px;
	width: 2px;
	height: 0;
	background: linear-gradient(
		180deg,
		var(--accent) 0%,
		var(--a2) 34%,
		var(--a3) 67%,
		var(--accent) 100%
	);
	transition: height .6s cubic-bezier(.22, 1, .36, 1);
	will-change: height;
}

.about-timeline-item {
	position: relative;
	display: flex;
	gap: 16px;
	padding-left: 34px;
	opacity: 0;
	transform: translateY(22px);
	transition: opacity .6s cubic-bezier(.22, 1, .36, 1), transform .6s cubic-bezier(.22, 1, .36, 1);
	will-change: transform, opacity;
}

.about-timeline-item.in-view {
	opacity: 1;
	transform: translateY(0);
}

.about-timeline-marker {
	position: absolute;
	left: 6px;
	top: 4px;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: var(--surf2);
	border: 2px solid var(--accent);
	box-shadow: 0 0 0 4px var(--surface);
	transform: scale(.8);
	transition: transform .45s cubic-bezier(.22, 1, .36, 1), box-shadow .45s ease;
}

.about-timeline-item.in-view .about-timeline-marker {
	transform: scale(1);
	box-shadow: 0 0 0 4px var(--surface), 0 0 0 7px color-mix(in srgb, var(--accent) 22%, transparent);
}

.about-timeline-item:nth-child(2) .about-timeline-marker { border-color: var(--a2); }
.about-timeline-item:nth-child(3) .about-timeline-marker { border-color: var(--a3); }
.about-timeline-item:nth-child(4) .about-timeline-marker { border-color: var(--accent); }
.about-timeline-item:nth-child(5) .about-timeline-marker { border-color: var(--a2); }
.about-timeline-item:nth-child(6) .about-timeline-marker { border-color: var(--a3); }

.about-timeline-body { flex: 1; min-width: 0; }

.about-timeline-era-row {
	display: flex;
	align-items: center;
	gap: 7px;
	margin-bottom: 3px;
}

.about-timeline-badge {
	flex: none;
	display: inline-flex;
	width: 20px;
	height: 20px;
	color: var(--accent-soft);
}

.about-badge-svg { width: 100%; height: 100%; overflow: visible; }

.about-badge-shape {
	fill: color-mix(in srgb, currentColor 14%, transparent);
	stroke: currentColor;
	stroke-width: 1.6;
}

.about-badge-shape-inner {
	fill: none;
	stroke: currentColor;
	stroke-width: .75;
	opacity: .55;
}

.about-badge-num {
	fill: currentColor;
	font-family: var(--mono);
	font-size: 13px;
	font-weight: 500;
}

.about-timeline-item:nth-child(2) .about-timeline-badge { color: var(--a2); }
.about-timeline-item:nth-child(3) .about-timeline-badge { color: var(--a3); }
.about-timeline-item:nth-child(4) .about-timeline-badge { color: var(--accent); }
.about-timeline-item:nth-child(5) .about-timeline-badge { color: var(--a2); }
.about-timeline-item:nth-child(6) .about-timeline-badge { color: var(--a3); }

.about-timeline-era {
	font-family: var(--mono);
	font-size: 10px;
	font-weight: 500;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--accent-soft);
}

.about-timeline-era.flicker-in {
	animation: about-era-flicker 1.1s linear both;
}

@keyframes about-era-flicker {
	0%   { opacity: 0;   text-shadow: none; }
	4%   { opacity: 1;   text-shadow: 0 0 8px currentColor; }
	8%   { opacity: .2;  text-shadow: none; }
	12%  { opacity: 1;   text-shadow: 0 0 10px currentColor; }
	16%  { opacity: .3;  text-shadow: none; }
	19%  { opacity: 1;   text-shadow: 0 0 6px currentColor; }
	24%  { opacity: .15; text-shadow: none; }
	28%  { opacity: 1;   text-shadow: 0 0 9px currentColor; }
	36%  { opacity: .4;  text-shadow: none; }
	40%  { opacity: 1;   text-shadow: 0 0 4px currentColor; }
	60%  { opacity: 1;   text-shadow: 0 0 4px currentColor; }
	64%  { opacity: .5;  text-shadow: none; }
	68%  { opacity: 1;   text-shadow: 0 0 3px currentColor; }
	100% { opacity: 1;   text-shadow: none; }
}

.about-timeline-item:nth-child(2) .about-timeline-era { color: var(--a2); }
.about-timeline-item:nth-child(3) .about-timeline-era { color: var(--a3); }
.about-timeline-item:nth-child(4) .about-timeline-era { color: var(--accent-soft); }
.about-timeline-item:nth-child(5) .about-timeline-era { color: var(--a2); }
.about-timeline-item:nth-child(6) .about-timeline-era { color: var(--a3); }

.about-timeline-title {
	font-size: 13.5px;
	font-weight: 700;
	color: var(--text);
	margin-bottom: 5px;
}

.about-timeline-text {
	font-size: 12px;
	line-height: 1.6;
	color: var(--muted);
}

@media (prefers-reduced-motion: reduce) {
	.about-timeline-item {
		transition: none;
		opacity: 1;
		transform: none;
	}
	.about-timeline-progress { transition: none; }
	.about-timeline-marker { transition: none; }
	.about-timeline-era.flicker-in { animation: none; }
}

@media (max-width: 420px) {
	.about-timeline-item { gap: 12px; }
}

.leg-icon {
	width: 16px;
	height: 16px;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	background: transparent;
}

.leg-icon svg {
	width: 12px;
	height: 12px;
}

.upcoming-title {
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .08em;
	color: var(--muted);
	margin: 14px 0 10px;
	display: flex;
	align-items: center;
	justify-content: space-between;
}

.add-event-btn {
	font-size: 11px;
	color: var(--muted);
	background: none;
	border: none;
	cursor: pointer;
	font-weight: 600;
	transition: color .14s;
	letter-spacing: .02em;
}

.add-event-btn:hover {
	color: var(--text);
}

.no-events {
	text-align: center;
	padding: 24px 0 14px;
	color: var(--muted);
	font-size: 12px;
	line-height: 1.6;
}

.no-events-icon {
	width: 52px;
	height: 52px;
	display: block;
	margin: 0 auto 10px;
	stroke: var(--muted);
	opacity: 0.5;
}

.sess-chip {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 10px 4px;
	font-size: 12px;
	font-family: var(--mono);
	border-bottom: 1px solid var(--border);
}

.sess-chip:last-child {
	border-bottom: none;
}

.sess-chip svg {
	width: 14px;
	height: 14px;
	stroke: var(--accent);
}

.modal-bg {
	position: fixed;
	inset: 0;
	z-index: 160;
	background: rgba(13,13,15,.82);
	display: none;
	align-items: center;
	justify-content: center;
	backdrop-filter: blur(4px);
	padding: 16px;
}

.modal-bg.open {
	display: flex;
}

.modal {
	position: relative;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 12px;
	padding: 24px 20px;
	width: 100%;
	max-width: 500px;
	animation: up .22s ease;
	max-height: 90vh;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
}

.modal-title-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	margin-bottom: 18px;
}

.modal-title-row .modal-title {
	margin-bottom: 0;
}

.modal-title-actions {
	display: flex;
	align-items: center;
	gap: 8px;
	flex-shrink: 0;
}

@keyframes up {
	from {
		opacity: 0;
		transform: translateY(16px);
	}
	to {
		opacity: 1;
		transform: none;
	}
}

.modal-title {
	font-size: 16px;
	font-weight: 700;
	margin-bottom: 18px;
}

.row2 {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 10px;
}

@media (max-width: 560px) {
	.week-grid {
		gap: 3px;
	}
	.bar {
		width: 14px;
	}
	.row2 {
		grid-template-columns: 1fr;
	}
}

.confirm-bg {
	position: fixed;
	inset: 0;
	z-index: 1200;
	background: rgba(13,13,15,.7);
	display: flex;
	align-items: center;
	justify-content: center;
	backdrop-filter: blur(4px);
	animation: fadeIn .15s ease;
}

@keyframes fadeIn {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}

.confirm-box {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 14px;
	padding: 24px 22px 18px;
	width: min(320px, 90vw);
	box-shadow: 0 12px 48px rgba(0,0,0,.5);
	animation: up .18s ease;
}

.confirm-msg {
	font-size: 14px;
	font-weight: 600;
	color: var(--text);
	margin-bottom: 18px;
	line-height: 1.5;
}

.confirm-btns {
	display: flex;
	gap: 8px;
	justify-content: flex-end;
}

.report-reason-list {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin-bottom: 14px;
}
.report-reason-btn {
	width: 100%;
	text-align: left;
}

.report-other-input {
	width: 100%;
	min-height: 76px;
	resize: vertical;
	margin-bottom: 14px;
	padding: 10px;
	border-radius: 8px;
	border: 1px solid var(--border);
	background: var(--bg);
	color: var(--text);
	font: inherit;
	font-size: 13px;
}
.report-other-input:focus {
	outline: none;
	border-color: var(--accent);
}

.ro-blocked {
	pointer-events: none;
	user-select: none;
}

.ro-blocked input,
.ro-blocked select,
.ro-blocked button,
.ro-blocked textarea {
	opacity: 0.4;
	cursor: not-allowed;
}

.pyq-crumb {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 4px;
	font-size: 12px;
	color: var(--muted);
	margin-bottom: 14px;
}

.pyq-crumb button {
	background: none;
	border: none;
	color: var(--muted);
	font: inherit;
	font-weight: 700;
	cursor: pointer;
	padding: 2px 5px;
	border-radius: 5px;
	-webkit-tap-highlight-color: transparent;
}

.pyq-crumb button:hover { color: var(--text); background: var(--surf2); }

.pyq-crumb-sep { opacity: .5; }

.pyq-crumb-current { color: var(--accent-soft); font-weight: 700; padding: 0 5px; }

.pyq-subject-count {
	display: inline-block;
	margin-left: 6px;
	font-family: var(--mono);
	font-size: 10.5px;
	color: var(--muted);
	opacity: .85;
}

.pyq-paper-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.pyq-paper-item {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 12px 14px;
	cursor: pointer;
	transition: border-color .14s;
	-webkit-tap-highlight-color: transparent;
}

.pyq-paper-item:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }

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

.pyq-paper-item-title { font-size: 13.5px; font-weight: 600; color: var(--text); transition: opacity .16s; }

.pyq-paper-item-sub { font-size: 11.5px; color: var(--muted); font-family: var(--mono); transition: opacity .16s; }

/* Marked-done fade -- applied by progress.js (_applyProgressBtnState) via
   the same .is-done row class the Syllabus topics use (see
   .syllabus-topic.is-done above), so every progress-tracked list fades
   its label the same way once it's ticked off. */
.pyq-paper-item.is-done { border-color: color-mix(in srgb, var(--ok) 45%, var(--border)); }
.pyq-paper-item.is-done .pyq-paper-item-title,
.pyq-paper-item.is-done .pyq-paper-item-sub {
	opacity: .48;
}

.pyq-paper-item-right {
	display: flex;
	align-items: center;
	gap: 8px;
	flex: none;
}

/* Round done-toggle -- see progress.js's pyqPaperDoneBtnHtml() /
   toggleProgress(). Used next to a PYQ paper's year chip
   (.pyq-paper-item) and, matching that same look, next to a Syllabus
   topic's Ask AI / YouTube pills (.syllabus-topic-actions). */
.pyq-paper-done-btn {
	width: 26px;
	height: 26px;
	flex: none;
	border-radius: 50%;
	border: 1.5px solid var(--border);
	background: transparent;
	color: transparent;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	transition: all .14s;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}
.pyq-paper-done-btn:hover { border-color: color-mix(in srgb, var(--ok) 55%, var(--border)); }
.pyq-paper-done-btn.done {
	background: var(--ok);
	border-color: var(--ok);
	color: #06231c;
}

.pyq-year-chip {
	font-family: var(--mono);
	font-size: 12px;
	font-weight: 700;
	color: var(--accent-soft);
	background: color-mix(in srgb, var(--accent) 14%, transparent);
	border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
	border-radius: 6px;
	padding: 3px 9px;
	white-space: nowrap;
}

.pyq-paper-header {
	border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
	background: linear-gradient(165deg, color-mix(in srgb, var(--accent) 12%, transparent), color-mix(in srgb, var(--accent) 3%, transparent) 70%);
	border-radius: 12px;
	padding: 16px 16px 14px;
	margin-bottom: 18px;
}

.pyq-paper-university {
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: .06em;
	color: var(--muted);
	font-weight: 800;
	text-align: center;
}

.pyq-paper-exam-title { font-size: 13px; color: var(--accent-soft); margin-top: 5px; font-weight: 700; text-align: center; }

.pyq-paper-subject { font-size: 19px; font-weight: 800; color: var(--text); margin-top: 4px; letter-spacing: -.01em; text-align: center; }

.pyq-paper-meta {
	display: flex;
	align-items: center;
	justify-content: space-between;
	width: 100%;
	margin-top: 12px;
	gap: 8px;
	flex-wrap: nowrap;
}

.pyq-paper-meta span {
	flex: 1 1 0;
	min-width: 0;
	line-height: 1.8;
	font-size: 11px;
	font-weight: 700;
	font-family: var(--mono);
	color: var(--accent-soft);
	border-radius: 999px;
	padding: 3px 10px;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	box-sizing: border-box;
}

.pyq-paper-meta span:first-child {
	text-align: left;
}

.pyq-paper-meta span:nth-child(2) {
	text-align: center;
}

.pyq-paper-meta span:last-child {
	text-align: right;
}

.pyq-q-block-label { color: var(--muted); font-weight: 600; }

.pyq-qblock {
	padding: 16px 16px 14px;
	margin-bottom: 16px;
}

.pyq-subq-list {
	display: flex;
	flex-direction: column;
	gap: 16px;
}

.pyq-subq {
	display: flex;
	gap: 10px;
	padding-top: 14px;
	border-top: 1px dashed var(--border);
}

.pyq-subq:first-child { padding-top: 0; border-top: none; }

.pyq-subq-letter {
	flex-shrink: 0;
	font-weight: 800;
	font-size: 12.5px;
	color: var(--warn);
	padding-top: 1px;
}

.pyq-subq-content { flex: 1; min-width: 0; }

.pyq-options-list {
	list-style: lower-alpha;
	padding-left: 22px;
	margin: 0 0 10px;
	font-size: 12.5px;
	color: var(--muted);
}

.pyq-options-list li { margin-bottom: 4px; }
/* ============ ALERT BANNER (scheduled-maintenance / downtime notice, ============
   see assets/js/alertbanner.js) ============ */
:root {
	--alert-banner-h: 0px;
}

.alert-banner {
	display: none;
	position: sticky;
	top: 0;
	z-index: 130;
	align-items: center;
	gap: 8px;
	width: 100%;
	box-sizing: border-box;
	padding: 9px 16px;
	font-size: 12.5px;
	font-weight: 600;
	line-height: 1.4;
	color: #1a1406;
}

.alert-banner.visible {
	display: flex;
}

.alert-banner-warn {
	background: var(--warn);
}

.alert-banner-danger {
	background: var(--err);
	color: #2a0a0e;
}

.alert-banner-icon {
	flex-shrink: 0;
}

.alert-banner-text {
	flex: 1;
	min-width: 0;
}

.alert-banner-msg {
	font-weight: 500;
	opacity: .88;
}

.alert-banner-close {
	flex-shrink: 0;
	width: 22px;
	height: 22px;
	display: flex;
	align-items: center;
	justify-content: center;
	border: none;
	border-radius: 6px;
	background: rgba(0,0,0,.14);
	color: inherit;
	cursor: pointer;
	font-size: 11px;
	line-height: 1;
}

.alert-banner-close:hover {
	background: rgba(0,0,0,.24);
}

/* Keep the app's own sticky header docked right below the banner
   instead of the two overlapping. */
body.alert-banner-open header {
	top: var(--alert-banner-h);
}

/* ============ USER PROFILE VIEW (viewing someone else's profile) ============
   see assets/js/profile.js openUserProfile() / shell.js #userProfileModal */
.user-profile-modal-box {
	background: var(--bg);
}

.user-profile-modal-header {
	margin-bottom: 6px;
}

.user-profile-modal-header-left {
	display: flex;
	align-items: center;
	gap: 10px;
}

.user-profile-modal-icon {
	width: 30px;
	height: 30px;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 9px;
	background: color-mix(in srgb, var(--accent) 16%, transparent);
	border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
	color: var(--accent-soft);
}

.user-profile-body {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	padding: 10px 4px 4px;
}

.user-profile-loading,
.user-profile-error {
	padding: 30px 10px;
	text-align: center;
	color: var(--muted);
	font-size: 13px;
}

/* Avatar + the ambient glow/orbit/sparkle decoration around it -- purely
   CSS (radial-gradient glow behind the photo, a faint rotated ellipse
   "orbit" ring, three small star sparkles at fixed positions), no image
   assets. Kept subtle/static rather than actually animating the orbit --
   a person's profile picture is the thing to look at, not a spinner. */
.user-profile-avatar-wrap {
	position: relative;
	width: 148px;
	height: 148px;
	display: flex;
	align-items: center;
	justify-content: center;
	margin-bottom: 10px;
}

.user-profile-avatar-glow {
	position: absolute;
	inset: -10px;
	border-radius: 50%;
	background: radial-gradient(circle, color-mix(in srgb, var(--accent) 38%, transparent) 0%, transparent 72%);
	filter: blur(2px);
	pointer-events: none;
}

.user-profile-avatar-orbit {
	position: absolute;
	inset: 6px;
	border-radius: 50%;
	border: 1px solid color-mix(in srgb, var(--accent-soft) 45%, transparent);
	transform: rotate(-18deg) scaleY(0.82);
	pointer-events: none;
}

.user-profile-sparkle {
	position: absolute;
	color: var(--accent-soft);
	opacity: .85;
	pointer-events: none;
}
.user-profile-sparkle-1 { top: 6px; right: 6px; }
.user-profile-sparkle-2 { bottom: 14px; left: 0; color: var(--text); opacity: .6; }
.user-profile-sparkle-3 { bottom: 22px; right: -2px; }

.user-profile-avatar {
	position: relative;
	width: 130px;
	height: 130px;
	border-radius: 50%;
	flex-shrink: 0;
	box-shadow: 0 0 0 3px var(--bg), 0 0 0 4px color-mix(in srgb, var(--accent) 55%, transparent);
	overflow: hidden;
}

/* Online-friend treatment: the avatar's own outline ring (the second
   box-shadow layer above) swaps from --accent to --ok, and a solid green
   dot sits on the avatar's own edge, bottom-right (badge style, like a
   messenger "online" indicator) -- same --ok color the offline
   last-seen row below uses for "Online now" before this moved onto the
   avatar. Only added when isOnlineFriend is true (see profile.js) --
   anyone not a friend, or a friend who's currently offline, gets the
   plain accent-colored ring with no dot, same as before. */
.user-profile-avatar-wrap.online .user-profile-avatar {
	box-shadow: 0 0 0 3px var(--bg), 0 0 0 4px color-mix(in srgb, var(--ok) 65%, transparent);
}

/* Sized/positioned to sit right on the outer edge of the avatar's own
   ring (not just loosely in the wrap's corner) so it reads as a badge
   overlapping the avatar -- a plain solid --bg border (no extra colored
   halo) is what keeps it crisp and clearly separate from the ring
   behind it instead of the two greens blurring into each other. */
.user-profile-online-dot {
	position: absolute;
	right: 6px;
	bottom: 10px;
	width: 28px;
	height: 28px;
	border-radius: 50%;
	background: var(--ok);
	border: 4px solid var(--bg);
	z-index: 2;
	pointer-events: none;
}

.user-profile-avatar .msgr-avatar {
	width: 100%;
	height: 100%;
	font-size: 44px;
}

.user-profile-name {
	font-size: 21px;
	font-weight: 800;
	text-align: center;
	margin-top: 4px;
}

.user-profile-handle {
	font-family: var(--mono);
	font-size: 13px;
	color: var(--accent-soft);
	margin-bottom: 6px;
}

/* "Batch 2025-2029" pill -- own row, own icon, distinct from the college
   row below it rather than lumped into a generic chip list. */
.user-profile-batch-pill {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	background: color-mix(in srgb, var(--accent) 14%, transparent);
	border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
	color: var(--accent-soft);
	border-radius: 20px;
	padding: 5px 14px;
	font-size: 12px;
	font-weight: 600;
	margin: 6px 0 14px;
}

.user-profile-college-row {
	display: flex;
	align-items: center;
	gap: 12px;
	width: 100%;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 14px;
	padding: 12px 14px;
	margin-bottom: 10px;
}

.user-profile-college-icon {
	width: 38px;
	height: 38px;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	background: color-mix(in srgb, var(--accent) 18%, transparent);
	color: var(--accent-soft);
}

.user-profile-college-name {
	font-size: 12.5px;
	font-weight: 700;
	letter-spacing: .01em;
	color: var(--text);
	line-height: 1.4;
}

/* Friends-only last-seen row (replaces the old decorative "Friends"
   pill -- see _renderUserProfileBody()'s comment). Grey dot + muted text
   ("Last seen …") -- the "currently online" case no longer renders this
   row at all; it shows as a green ring + dot on the avatar itself
   instead (see .user-profile-avatar-wrap.online / .user-profile-online-dot
   above), so there's no .online variant of this row anymore. */
.user-profile-lastseen-row {
	display: flex;
	align-items: center;
	gap: 8px;
	width: 100%;
	justify-content: center;
	color: var(--muted);
	font-size: 12.5px;
	font-weight: 600;
	margin-top: 4px;
}

.user-profile-lastseen-dot {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--muted);
	flex-shrink: 0;
}

.user-profile-status {
	font-size: 12px;
	color: var(--ok);
	font-weight: 700;
	margin: 4px 0 10px;
}

.user-profile-status-blocked {
	color: var(--err);
}

.user-profile-btn-row {
	display: flex;
	gap: 8px;
	width: 100%;
	margin-top: 10px;
}

.user-profile-btn-row .btn {
	flex: 1;
}

/* ============ MENTIONS (header bell + inbox) ============ */
.header-bell-btn {
	position: relative;
	width: 34px;
	height: 34px;
	border-radius: 50%;
	border: 1px solid var(--border);
	background: var(--surf2);
	color: var(--muted);
	cursor: pointer;
	padding: 0;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	/* No overflow:hidden here anymore -- it used to clip the corner of
	   .header-mentions-badge below (positioned slightly outside this
	   button's own box, at top:-1px/right:-1px, so it could sit flush
	   against the circular edge instead of floating visibly inside it).
	   That clipped off a sliver of the dot on two sides, leaving it
	   looking like a faint/cut-off crescent rather than a solid circle
	   -- which is why the "new notification" dot was hard to see. The
	   shimmer sweep below still needs clipping to the button's circular
	   shape, so that's now scoped to .header-bell-icon-wrap instead. */
}

.header-bell-icon-wrap {
	position: relative;
	width: 100%;
	height: 100%;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden; /* clips .header-bell-btn.loading .header-bell-icon-wrap::after's shimmer sweep to a circle, without clipping the badge below */
}

/* Loading state: the bell can't honestly show "nothing new" (no badge)
   until the first refreshUnseenCounts() poll actually resolves (club.js)
   -- until then, whether anything's unread is simply unknown, same as
   the header avatar/date pill before their own data arrives. Same
   compositor-only sweep as .date-btn.loading::after. club.js removes
   this class the moment the first poll's result is rendered, whether
   or not it turns out there's anything to show. */
.header-bell-btn.loading .header-bell-icon-wrap::after {
	content: '';
	position: absolute;
	inset: 0;
	transform: translateX(-120%);
	background: linear-gradient(100deg, transparent 30%, var(--surface) 50%, transparent 70%);
	animation: skel-sweep 1.6s ease-in-out infinite;
	will-change: transform;
	pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
	.header-bell-btn.loading .header-bell-icon-wrap::after { animation: none; content: none; }
}

.header-bell-btn:hover {
	color: var(--text);
	border-color: var(--accent);
}

.header-mentions-badge {
	position: absolute;
	top: -1px;
	right: -1px;
	/* A plain dot now (see _renderMentionsBadge() in club.js -- content
	   is always empty), not a number pill anymore, so this is a small
	   fixed circle rather than a min-width bubble sized for digits. */
	width: 9px;
	height: 9px;
	border-radius: 50%;
	background: var(--err);
	border: 1.5px solid var(--bg);
	box-sizing: border-box;
}

.mentions-inbox-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
	max-height: 60vh;
	overflow-y: auto;
}

.mentions-inbox-item {
	display: flex;
	gap: 10px;
	align-items: flex-start;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 10px 12px;
	cursor: pointer;
	text-align: left;
}

.mentions-inbox-item:hover {
	border-color: var(--accent);
}

.mentions-inbox-item.unseen {
	border-color: color-mix(in srgb, var(--warn) 45%, var(--border));
	background: color-mix(in srgb, var(--warn) 6%, var(--surface));
}

.mentions-inbox-item-body {
	flex: 1;
	min-width: 0;
}

.mentions-inbox-item-where {
	font-size: 11.5px;
	color: var(--muted);
	margin-bottom: 2px;
}

.mentions-inbox-item-snippet {
	font-size: 13px;
	overflow-wrap: break-word;
}

.mentions-inbox-item-time {
	font-size: 10.5px;
	color: var(--muted);
	margin-top: 3px;
}

.mentions-inbox-request-actions {
	display: flex;
	gap: 8px;
	margin-top: 6px;
}

/* Warning/block system notice (source_type='system') -- see
   report-resolve.php's escalation and mentions.js's special-case
   render. Not clickable to anywhere, so no hover/cursor affordance
   beyond the shared .mentions-inbox-item ones, and a neutral bell/warn
   icon in place of a person's avatar. */
.mentions-inbox-item-icon-system {
	width: 34px;
	height: 34px;
	flex: none;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 16px;
	border-radius: 50%;
	background: color-mix(in srgb, var(--warn) 18%, transparent);
}

/* Highlight @handle tokens inline inside rendered chat/comment text. */
.mention-token {
	color: var(--accent-soft);
	font-weight: 700;
}

/* ============ @MENTION AUTOCOMPLETE DROPDOWN (assets/js/mentions.js) ============ */
.mention-dropdown {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 100%;
	margin-bottom: 6px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 10px;
	box-shadow: 0 8px 24px rgba(0,0,0,.35);
	overflow: hidden;
	z-index: 40;
	max-height: 220px;
	overflow-y: auto;
}

.mention-dropdown-row {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 8px 10px;
	cursor: pointer;
}

.mention-dropdown-row.active,
.mention-dropdown-row:hover {
	background: var(--surf2);
}

.mention-dropdown-icon {
	width: 26px;
	height: 26px;
	border-radius: 7px;
	background: var(--accent);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 10.5px;
	font-weight: 800;
	font-family: var(--mono);
	overflow: hidden;
	flex-shrink: 0;
}

.mention-dropdown-icon img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.mention-dropdown-name {
	font-size: 12.5px;
	font-weight: 600;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.mention-dropdown-handle {
	margin-left: 6px;
	font-family: var(--mono);
	font-size: 11px;
	color: var(--muted);
	font-weight: 500;
}

/* ============================================================
   Board / PDF reader (assets/js/pdfreader.js) styling.
   MOVED HERE from a runtime document.createElement('style') block
   that pdfreader.js used to inject into <head> the moment its own
   (deferred) script executed. Since dashboard.html's Board markup
   is already present in the static HTML the browser parses well
   before any deferred script runs, the browser could paint it
   using only this file's rules first -- unstyled/partially styled
   -- and only pick up the rest a moment later once pdfreader.js
   finally ran and appended its \3c style> tag, showing up as a brief
   flash of "unfinished" CSS on Board that then snapped into place.
   Living in this file instead means the browser has every rule
   available before it paints anything, same as the rest of the app.
   ============================================================ */

/* ---------- chrome-style browser tabs (tab-merge curve) ---------- */
.pdf-tabstrip {
	display: flex;
	align-items: stretch;
	gap: 0;
	margin-bottom: 0;
	padding: 6px 6px 0;
	background: #1c1c22;
	border-radius: 12px 12px 0 0;
	overflow: visible;
	position: relative;
	z-index: 1;
}

.pdf-tabstrip-tabs {
	display: flex;
	align-items: stretch;
	gap: 2px;
	flex: 1;
	min-width: 0;
	overflow-x: auto;
	overflow-y: hidden;
	scrollbar-width: thin;
	scroll-behavior: smooth;
	padding-bottom: 0;
}

/* The "+" now lives INSIDE the tab list itself (rendered as the last
   element by renderTabStrip), not pinned to the far right past the
   fullscreen/dark-mode/split icons. That makes it naturally sit
   right after the last real tab when there are any, and fall back
   to being the leftmost (in fact only) thing in the strip when
   there are none — exactly by virtue of normal flex flow, no
   separate "is there a tab?" positioning logic needed. */
.pdf-tabstrip-add {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 34px;
	min-width: 34px;
	flex-shrink: 0;
	border: none;
	background: transparent;
	color: #fff;
	font-size: 19px;
	font-weight: 400;
	line-height: 1;
	cursor: pointer;
	border-radius: 8px 8px 0 0;
	transition: background .12s, color .12s;
	-webkit-tap-highlight-color: transparent;
}
.pdf-tabstrip-add:hover {
	background: rgba(255,255,255,0.08);
}

/* While split view is on, switching/closing tabs from the top strip is
   ambiguous (which pane would it affect?) — so the whole strip is
   frozen: dimmed and unclickable. Use each pane's own document-picker
   dropdown to change documents instead. Re-enabled the moment split
   view is turned back off. */
.pdf-tabstrip-tabs.pdf-frozen {
	pointer-events: none;
	opacity: .5;
	filter: saturate(.6);
}
/* Freezing the strip during split view is about switching tabs
   being ambiguous (which pane would it go to?) — opening a NEW
   file isn't ambiguous, so the add button stays clickable and at
   full opacity even while the rest of the strip is dimmed/frozen.
   Now that "+" lives inside .pdf-tabstrip-tabs (see renderTabStrip),
   it would otherwise have inherited the freeze along with every
   actual tab. */
.pdf-tabstrip-tabs.pdf-frozen .pdf-tabstrip-add {
	pointer-events: auto;
	opacity: 1;
	filter: none;
}

/* horizontal scrollbar shows automatically once tabs overflow */
.pdf-tabstrip-tabs::-webkit-scrollbar {
	height: 4px;
}
.pdf-tabstrip-tabs::-webkit-scrollbar-track {
	background: transparent;
}
.pdf-tabstrip-tabs::-webkit-scrollbar-thumb {
	background: var(--border);
	border-radius: 4px;
}
.pdf-tabstrip-tabs::-webkit-scrollbar-thumb:hover {
	background: var(--muted);
}

/* each doc "tab" — inactive tabs are a subtle dark pill floating
   on the strip; the active tab shares the strip/toolbar color so
   it appears to be a seamless extension of the toolbar below */
.pdf-doctab {
	--notch: 10px;
	display: flex;
	align-items: center;
	gap: 7px;
	max-width: 200px;
	min-width: 120px;
	padding: 7px 8px 7px 12px;
	margin-top: 0;
	border-radius: 8px 8px 0 0;
	background: #0d0d0f;
	color: rgba(220,220,240,0.55);
	cursor: pointer;
	font-size: 12.5px;
	font-weight: 500;
	white-space: nowrap;
	flex-shrink: 0;
	transition: background .12s, color .12s;
	-webkit-tap-highlight-color: transparent;
	position: relative;
	margin-bottom: 0;
}

/* concave notch corners on the active tab only */
.pdf-doctab::before,
.pdf-doctab::after {
	content: '';
	position: absolute;
	bottom: 0;
	width: var(--notch);
	height: var(--notch);
	background: transparent;
	opacity: 0;
	transition: opacity .12s;
	pointer-events: none;
	z-index: 2;
}
.pdf-doctab::before {
	left: calc(var(--notch) * -1);
	background: radial-gradient(circle at top left, transparent var(--notch), #2a2a32 calc(var(--notch) + 1px));
}
.pdf-doctab::after {
	right: calc(var(--notch) * -1);
	background: radial-gradient(circle at top right, transparent var(--notch), #2a2a32 calc(var(--notch) + 1px));
}

.pdf-doctab:hover {
	background: #16161a;
	color: rgba(220,220,240,0.85);
}

/* active tab = same color as toolbar, so bottom edge disappears
   and the tab appears to grow directly out of the toolbar */
.pdf-doctab.active {
	background: #2a2a32;
	color: #fff;
	/* extend 1px below so no hairline gap to toolbar */
	margin-bottom: -1px;
	padding-bottom: 8px;
}

.pdf-doctab.active::before,
.pdf-doctab.active::after {
	opacity: 1;
}

.pdf-doctab-favicon {
	width: 14px;
	height: 14px;
	flex-shrink: 0;
	border-radius: 3px;
	display: flex;
	align-items: center;
	justify-content: center;
	color: rgba(200,200,220,0.65);
}

.pdf-doctab-name {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	flex: 1;
	min-width: 0;
}

.pdf-doctab-close {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 18px;
	height: 18px;
	border-radius: 50%;
	flex-shrink: 0;
	color: inherit;
	opacity: 0;
	transition: opacity .12s, background .12s;
}

.pdf-doctab:hover .pdf-doctab-close,
.pdf-doctab.active .pdf-doctab-close {
	opacity: .75;
}

.pdf-doctab-close:hover {
	opacity: 1 !important;
	background: rgba(255,255,255,.16);
}

.pdf-tabstrip-side {
	display: flex;
	align-items: center;
	gap: 2px;
	flex-shrink: 0;
	align-self: flex-start;
	margin-top: 3px;
	padding-left: 4px;
}

.pdf-tabstrip-add,
.pdf-splitview-btn {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 28px;
	height: 28px;
	border-radius: 8px;
	border: none;
	background: transparent;
	color: rgba(200,200,220,0.65);
	cursor: pointer;
	font-size: 17px;
	line-height: 1;
	flex-shrink: 0;
	transition: all .14s;
}

.pdf-tabstrip-add:hover,
.pdf-splitview-btn:hover {
	color: var(--text);
	background: rgba(255,255,255,.12);
}

.pdf-splitview-btn.active {
	color: var(--accent);
	background: rgba(124,106,247,.18);
}

/* ---------- immersive stage ---------- */
.pdf-stage {
	position: relative;
	display: flex;
	flex-direction: column;
	border-radius: 0 0 12px 12px;
	overflow: hidden;
	flex: 1 1 auto;
	min-height: 0;
	height: auto;
	background: var(--bg);
}

/* Loading skimmer shown over .pdf-stage while a document is being
   fetched/parsed (see initEmbedApi()'s pdfOpenFromUrl() call below).
   Previously the pane just stayed blank/hidden during that fetch,
   which read as a broken/blank render rather than "still loading" --
   using the same shimmer sweep as every other list skeleton in the
   app (see styles.css's .skel) makes it obviously a loading state
   instead. Absolutely positioned INSIDE .pdf-stage, which is already
   position:relative + overflow:hidden, so the sweep can never spill
   past the pane's own bounds regardless of viewport size. */
.pdf-loading-skel {
	position: absolute;
	inset: 0;
	z-index: 5;
	background: var(--surf2);
}
.pdf-loading-skel::after {
	content: '';
	position: absolute;
	inset: 0;
	transform: translateX(-120%);
	background: linear-gradient(100deg, transparent 30%, var(--surface) 50%, transparent 70%);
	animation: pdfSkelSweep 1.6s ease-in-out infinite;
	will-change: transform;
}
@keyframes pdfSkelSweep {
	0%   { transform: translateX(-120%); }
	100% { transform: translateX(120%); }
}
@media (prefers-reduced-motion: reduce) {
	.pdf-loading-skel::after { animation: none; content: none; }
}

/* ---------- empty-state: two drop zones + saved-files history ----------
   Height behaves dynamically: with NO history yet, the drop zones
   expand to fill the whole body (first-run, nothing else to show).
   The moment there IS history, the zones shrink down to a compact
   row so the history list — the thing you're more likely to want
   next time — gets the room instead. Toggled via .pdf-has-history
   on the card (see pdfRenderSavedList). */
.pdf-empty-card {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	min-height: 0;
}
.pdf-dropzones {
	display: flex;
	gap: 14px;
	flex: 1 1 auto;
	min-height: 220px;
	transition: flex-basis .25s ease, min-height .25s ease;
}
.pdf-empty-card.pdf-has-history .pdf-dropzones {
	flex: 0 0 auto;
	min-height: 130px;
}
.pdf-dropzone {
	flex: 1 1 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 10px;
	text-align: center;
	padding: 24px 20px;
	border: 1.5px dashed var(--border);
	border-radius: 14px;
	color: var(--muted);
	cursor: pointer;
	background: var(--surf2);
	transition: border-color .15s ease, background .15s ease, color .15s ease, transform .12s ease;
}
.pdf-dropzone:hover {
	border-color: var(--accent, #7c6af7);
	color: var(--text);
	background: rgba(124,106,247,.08);
}
.pdf-dropzone:active {
	transform: scale(.985);
}
/* Active drag-over state — set/cleared by wireDropzones() */
.pdf-dropzone.pdf-dragover {
	border-color: var(--accent, #7c6af7);
	border-style: solid;
	background: rgba(124,106,247,.14);
	color: var(--text);
}
.pdf-dropzone-title {
	font-size: 14px;
	font-weight: 700;
	color: var(--text);
}
.pdf-dropzone-sub {
	font-size: 11.5px;
	line-height: 1.5;
	max-width: 260px;
}
.pdf-empty-card.pdf-has-history .pdf-dropzone-sub {
	display: none;
}
.pdf-empty-card.pdf-has-history .pdf-dropzone {
	padding: 14px 16px;
	flex-direction: row;
	gap: 12px;
}
#pdfSavedFilesSection {
	margin-top: 14px;
	flex: 0 0 auto;
}
/* Seekbar for the saved-files list — it can grow well past what
   fits (a dozen+ previously-opened files is common), so it scrolls
   within its own bounded area instead of pushing the rest of the
   page down indefinitely. */
.pdf-saved-list {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin-bottom: 10px;
	max-height: 260px;
	overflow-y: auto;
	scrollbar-width: thin;
	padding-right: 2px;
}
.pdf-saved-list::-webkit-scrollbar {
	width: 6px;
}
.pdf-saved-list::-webkit-scrollbar-thumb {
	background: rgba(255,255,255,0.15);
	border-radius: 3px;
}
.pdf-saved-list::-webkit-scrollbar-thumb:hover {
	background: rgba(255,255,255,0.25);
}

/* "Clear history" text-button above the saved list — hover state used
   to be set via onmouseover/onmouseout (inline handlers); moved to CSS
   so the behavior no longer depends on running JS per element. */
.pdf-clear-history-btn {
	color: var(--muted);
	text-decoration-color: transparent;
}
.pdf-clear-history-btn:hover {
	color: var(--text);
	text-decoration-color: currentColor;
}

/* ---------- per-pane toolbar — lives INSIDE each .pdf-pane, so its
   width is always exactly that pane's width. In split view every pane
   gets its own full toolbar, and dragging the resizer reflows both
   toolbars live since each is just that pane's header row. ---------- */
.pdf-toolbar {
	display: flex;
	align-items: center;
	gap: 2px;
	flex-shrink: 0;
	padding: 5px 6px 5px 10px;
	background: #2a2a32;
	border-bottom: 1px solid rgba(255,255,255,0.07);
	position: relative;
	z-index: 30;
	border-radius: 0;
	box-sizing: border-box;
	width: 100%;
	min-width: 0;
	/* Previously overflow:hidden — with enough buttons (especially
	   in split view, where each pane only gets half the width),
	   the row could exceed the available space and whatever fell
	   off the right edge — often the Save button, since it's one
	   of the last items — was simply clipped off-screen and
	   invisible, with no way to reach it. Scrolling keeps every
	   button reachable; the thin scrollbar only appears when it's
	   actually needed. */
	overflow-x: auto;
	overflow-y: hidden;
	scrollbar-width: thin;
}
.pdf-toolbar::-webkit-scrollbar {
	height: 4px;
}
.pdf-toolbar::-webkit-scrollbar-thumb {
	background: rgba(255,255,255,0.15);
	border-radius: 2px;
}

.pdf-tbtn {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	flex-shrink: 0;
	border: none;
	background: transparent;
	color: rgba(200,200,220,0.65);
	border-radius: 7px;
	cursor: pointer !important;
	transition: all .14s, width .12s, height .12s;
	-webkit-tap-highlight-color: transparent;
}

/* Save (download a copy) is the one button that should never
   require scrolling the toolbar to reach, no matter how many other
   buttons are crowded into a narrow pane (e.g. split view) — it
   sticks to the right edge of the toolbar's own scroll area, with
   a soft fade behind it so it doesn't look like it's just floating
   over whatever's scrolled underneath. */
.pdf-tbtn[id^="pdfSaveBtn_"] {
	position: sticky;
	right: 0;
	z-index: 1;
	background: #2a2a32;
	box-shadow: -10px 0 8px -4px #2a2a32;
}

.pdf-tbtn svg, .pdf-tbtn-accent svg {
	stroke: rgba(200,200,220,0.65);
	transition: width .12s, height .12s;
}

.pdf-tbtn:hover {
	color: #fff;
	background: rgba(255,255,255,.12);
}

.pdf-tbtn:active {
	transform: scale(.9);
}

.pdf-tbtn:hover svg {
	stroke: #fff;
}

.pdf-tbtn-accent {
	color: rgba(200,200,220,0.65);
}
.pdf-tbtn-accent:hover {
	color: #fff;
	background: rgba(255,255,255,.12);
}

.pdf-tbtn.active {
	color: #fff;
	background: var(--accent);
}

.pdf-tbtn.active svg {
	stroke: #fff;
}

.pdf-tbtn:disabled {
	opacity: .45;
	cursor: default;
}

/* Loading state: a buffering ring spins AROUND the icon (the icon
   itself stays upright and just changes color) instead of the old
   approach of spinning the icon glyph itself, which looked odd for
   icons like the floppy-disk save glyph that aren't meant to rotate. */
.pdf-tbtn.is-saving {
	position: relative;
	color: var(--accent, #7c6af7);
	opacity: 1 !important;
}
.pdf-tbtn.is-saving svg {
	stroke: var(--accent, #7c6af7);
	transition: stroke .15s ease;
}
.pdf-tbtn.is-saving::before {
	content: '';
	position: absolute;
	inset: 1px;
	border-radius: 50%;
	border: 2px solid transparent;
	border-top-color: var(--accent, #7c6af7);
	animation: pdfBufferSpin .7s linear infinite;
}

.pdf-tbtn.is-saved {
	color: #40c9a8;
}
.pdf-tbtn.is-saved svg {
	stroke: #40c9a8;
	transition: stroke .15s ease;
}

@keyframes pdfBufferSpin {
	to { transform: rotate(360deg); }
}

.pdf-tsep {
	width: 1px;
	height: 18px;
	background: rgba(255,255,255,0.1);
	margin: 0 6px;
	flex-shrink: 0;
	transition: margin .12s;
}

.pdf-tbtn-spacer {
	flex: 1;
}

/* ---------- responsive toolbar tiers ----------
   Each pane is its own size-container (see .pdf-pane), so every
   pane's toolbar shrinks based on THAT pane's own width — dragging
   the split resizer narrower shrinks just that pane's tools, in
   steps, rather than letting them overflow or requiring a scrollbar.
   Buttons get smaller and gaps tighten first; once that's not
   enough, the least essential bits of chrome (the "of N" page
   count) tuck away. The document-picker dropdown is NEVER fully
   hidden — in split view it's the only way to change which doc a
   pane shows, so it just keeps shrinking down to a narrow but still
   tappable width instead of disappearing. */
@container pdf-pane (max-width: 480px) {
	.pdf-toolbar { gap: 0; padding: 5px 4px 5px 8px; }
	.pdf-tbtn { width: 27px; height: 27px; }
	.pdf-tbtn svg { width: 15px; height: 15px; }
	.pdf-tsep { margin: 0 4px; }
	.pdf-pane-select { flex: 0 1 90px; min-width: 40px; }
}
@container pdf-pane (max-width: 380px) {
	.pdf-tbtn { width: 24px; height: 24px; border-radius: 6px; }
	.pdf-tbtn svg { width: 14px; height: 14px; }
	.pdf-tsep { margin: 0 3px; }
	.pdf-page-sep { display: none; }
	.pdf-page-input-wrap { padding: 2px 4px; }
	.pdf-pane-select { flex: 0 1 56px; min-width: 30px; font-size: 10px; padding: 4px 3px; margin-left: 2px; }
}
@container pdf-pane (max-width: 300px) {
	.pdf-toolbar { gap: 0; padding: 4px 3px 4px 6px; }
	.pdf-tbtn { width: 22px; height: 22px; }
	.pdf-tbtn svg { width: 13px; height: 13px; }
	.pdf-tsep { margin: 0 2px; width: 1px; height: 14px; }
	.pdf-page-input { width: 20px; font-size: 11px; }
	.pdf-pane-select { flex: 0 1 34px; min-width: 24px; font-size: 9px; padding: 4px 2px; margin-left: 1px; }
}

.pdf-panes {
	display: flex;
	align-items: stretch;
	flex: 1 1 auto;
	min-height: 0;
	position: relative;
}

.pdf-pane {
	position: relative;
	flex: 1 1 0;
	min-width: 0;
	overflow: hidden;
	background: var(--surf2);
	display: flex;
	flex-direction: column;
	/* lets the toolbar inside react to THIS pane's own width via
	   @container queries — so each pane's toolbar shrinks on its
	   own as the split resizer changes that pane's width, independent
	   of the other pane or the viewport as a whole */
	container-type: inline-size;
	container-name: pdf-pane;
}

/* draggable vertical divider between the two split-view panes */
.pdf-pane-resizer {
	width: 7px;
	flex: 0 0 7px;
	background: var(--border);
	cursor: col-resize;
	position: relative;
	z-index: 22;
	touch-action: none;
}
.pdf-pane-resizer::after {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	width: 3px;
	height: 36px;
	transform: translate(-50%, -50%);
	border-radius: 3px;
	background: var(--muted);
	opacity: .6;
}
.pdf-pane-resizer:hover,
.pdf-pane-resizer.is-dragging {
	background: var(--accent);
}
.pdf-pane-resizer:hover::after,
.pdf-pane-resizer.is-dragging::after {
	opacity: 0;
}

.pdf-pane-viewport {
	width: 100%;
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	overflow-x: auto;
	display: flex;
	flex-direction: column;
	/* "align-items: center" alone has a well-known flexbox/grid
	   limitation: once a page is zoomed wider than the viewport,
	   the centered page overflows equally on both sides, but only
	   the END-side (right) overflow is actually reachable by
	   scrolling in most browsers -- the START-side (left) portion
	   that centering pushes past the container's own left edge
	   never gets included in the scrollable area at all, so no
	   amount of dragging/scrolling left ever reveals it. That's
	   exactly the reported "part of the zoomed page is outside and
	   dragging left-right doesn't show it" bug. "safe center" is
	   the CSS Box Alignment spec's purpose-built fix for this
	   exact case: it behaves like plain "center" until the content
	   would overflow off an unreachable edge, then falls back to
	   start-alignment so the ENTIRE page — including what would
	   have been the inaccessible left portion — stays within the
	   scrollable area and reachable by dragging. Browsers that
	   don't recognize "safe center" (an invalid value) simply
	   ignore this second declaration and keep the plain "center"
	   above, so this is a no-risk progressive enhancement rather
	   than a hard requirement.
	*/
	align-items: center;
	align-items: safe center;
	scrollbar-width: thin;
	scrollbar-color: rgba(255,255,255,0.18) #2a2a32;
	/* Reserve the vertical scrollbar's track width UP FRONT, even
	   on the very first layout pass before any page has actually
	   rendered tall enough to overflow. Without this,
	   pdfZoomStretch() measures clientWidth while no scrollbar
	   exists yet, fits the page to that (too-wide) width, and only
	   THEN does the page's own height push the viewport into
	   overflow -- at which point the vertical scrollbar finally
	   appears and eats into the width that was already fitted,
	   leaving the page permanently a few pixels too wide for the
	   pane and a dead, un-fillable gap on the right of it (right
	   after where the scrollbar now sits) with no later reflow
	   ever correcting it. Reserving the gutter unconditionally
	   makes clientWidth identical before/after the scrollbar
	   shows up, so the fit is correct from the first measurement
	   and that gap can't open up in the first place. */
	scrollbar-gutter: stable;
	/* Without this, dragging past the first/last page's edge on a
	   touchscreen "chains" the scroll gesture into the OUTER
	   page's own scroll/rubber-band bounce once this viewport
	   runs out of room to scroll further -- a jarring handoff
	   mid-gesture that reads as the scroll suddenly "slipping".
	   Containing it here keeps the whole gesture, bounce
	   included, inside this viewport. */
	overscroll-behavior: contain;
	/* No top padding -- the first page should butt directly up
	   against the toolbar above it, not leave a bare strip of
	   the viewport's own #111 background showing through. */
	padding: 0 0 90px;
	gap: 14px;
	background: #111;
}
@supports not (scrollbar-gutter: stable) {
	.pdf-pane-viewport { overflow-y: scroll; }
}
/* Match the scrollbar's track to the toolbar's own color so it
   reads as part of the same surface rather than a separate pale
   strip running down the right edge -- and keep it slim so the
   page content fills the pane right up to the scrollbar itself
   instead of leaving dead space beside a wide default OS bar. */
.pdf-pane-viewport::-webkit-scrollbar {
	width: 10px;
	height: 10px;
}
.pdf-pane-viewport::-webkit-scrollbar-track {
	background: #2a2a32;
}
.pdf-pane-viewport::-webkit-scrollbar-thumb {
	background: rgba(255,255,255,0.18);
	border-radius: 6px;
	border: 2px solid #2a2a32;
	background-clip: padding-box;
}
.pdf-pane-viewport::-webkit-scrollbar-thumb:hover {
	background: rgba(255,255,255,0.3);
	background-clip: padding-box;
}
.pdf-pane-viewport::-webkit-scrollbar-corner {
	background: #2a2a32;
}
/* While actively click-dragging to pan (no tool selected — see
   wirePanDrag), show the "gripping" cursor over the whole viewport,
   not just the canvas, so the cursor stays correct even if the drag
   momentarily passes over a gap between pages. */
.pdf-pane-viewport.pdf-panning {
	cursor: grabbing !important;
}
.pdf-pane-viewport.pdf-panning .pdf-draw-canvas {
	cursor: grabbing !important;
}

/* page board: a transparent shell sized EXACTLY to the real
   rendered page (no artificial A4 padding, no white background of
   its own) — the page's own pdf.js-rendered canvas is the only
   visual here, so there's never a double white layer or letterboxing
   around pages that aren't A4-shaped. */
.pdf-page-board {
	position: relative;
	flex-shrink: 0;
	background: transparent;
	box-shadow: 0 6px 24px rgba(0,0,0,.45);
	line-height: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
}

.pdf-canvas-wrap {
	position: relative;
	flex-shrink: 0;
	line-height: 0;
	animation: pdfPageMountIn .25s ease-out;
	/* White page surface underneath the render/draw canvases --
	   matches the now-opaque-white page.render() background (see
	   mountPage()) instead of leaving this wrap transparent, so
	   there's no dark flash behind the page for the instant before
	   that render call resolves. */
	background: #fff;
}
/* ---------- Add Text / Add Picture interactive overlay ---------- */
.pdf-ibox-text:empty:before {
	content: attr(data-placeholder);
	opacity: .45;
	pointer-events: none;
}
.pdf-ibox-handle:active {
	cursor: grabbing;
}
.pdf-ibox-resizer:hover {
	transform: scale(1.15);
}
@keyframes pdfPageMountIn {
	from { opacity: 0; transform: translateY(6px); }
	to   { opacity: 1; transform: translateY(0); }
}

.pdf-canvas-wrap canvas {
	display: block;
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
}

.pdf-draw-canvas {
	touch-action: none;
	cursor: crosshair;
}

.pdf-page-render-error {
	display: flex;
	align-items: center;
	justify-content: center;
	text-align: center;
	width: 100%;
	height: 100%;
	min-height: 120px;
	padding: 16px;
	background: #f4f4f6;
	color: #6b6b76;
	font-family: var(--sans);
	font-size: 13px;
}

.pdf-page-num-tag {
	position: absolute;
	bottom: 6px;
	right: 8px;
	font-family: var(--mono);
	font-size: 10px;
	color: rgba(0,0,0,.32);
	background: rgba(255,255,255,.55);
	padding: 1px 6px;
	border-radius: 5px;
	pointer-events: none;
	z-index: 5;
}

/* document selector, shown inline in a pane's toolbar only when that
   pane has more than one open document to choose between (split view) */
.pdf-pane-select {
	flex: 0 1 120px;
	min-width: 56px;
	background: rgba(255,255,255,0.06);
	border: 1px solid rgba(255,255,255,0.12);
	border-radius: 6px;
	color: var(--text);
	font-size: 11px;
	padding: 4px 6px;
	margin-left: 4px;
	max-width: 140px;
	cursor: pointer;
}

/* ---------- floating glass controls ---------- */
.pdf-float {
	position: absolute;
	display: flex;
	align-items: center;
	gap: 5px;
	background: #0d0d0f;
	border: 1px solid rgba(255,255,255,0.1);
	border-radius: 12px;
	padding: 6px;
	z-index: 40;
	box-shadow: 0 4px 18px rgba(0,0,0,.5);
	animation: pdfFloatIn .22s cubic-bezier(.22,.9,.32,1.1);
}

@keyframes pdfFloatIn {
	from { opacity: 0; transform: translateY(8px) scale(.96); }
	to   { opacity: 1; transform: translateY(0) scale(1); }
}

.pdf-float-bl {
	left: 14px;
	bottom: 14px;
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 5px;
	max-height: min(70vh, calc(100% - 90px));
	overflow-y: auto;
	scrollbar-width: none;
}
.pdf-float-bl::-webkit-scrollbar { display: none; }

.pdf-float-br {
	/* Same box model as the canvas toolkit (.pdf-float-bl) — a
	   direct 2-column grid, not a flex column wrapping a separately-
	   sized swatch grid + custom-color circle + slider. That
	   mismatch in sizing models was the actual "overlap" — the
	   custom button (24px circle) sat at a different width than
	   the swatch grid above it inside a flex column, crowding
	   together instead of lining up in clean cells. Now every
	   swatch AND the custom-color button are plain 32×32 grid
	   cells, exactly like every tool icon in the canvas toolkit.
	   24px sat the panel right where the pane's own scrollbar
	   lives — the pane's box (the positioning ancestor) spans its
	   full width including the scrollbar's strip, so a small
	   right-offset put the panel right under/behind it instead of
	   clearing it (exactly the overlap in the reference screenshot).
	   24px gives enough clearance for a typical scrollbar width
	   across browsers/OSes plus a little breathing room. */
	right: 24px;
	bottom: 14px;
	display: grid;
	grid-template-columns: 1fr 1fr;
	/* Fixed width, matching the 2×48px swatch grid exactly (48+48+5
	   gap = 101 content + 6px padding each side = 113 total). Without
	   this, the panel used to shrink-to-fit its content — fine while
	   only swatches were showing, but the moment the RGB/HSV board
	   opened, its R/G/B input row (much wider than two swatches) blew
	   the whole grid open to fit, so the panel visibly jumped wider
	   between the two states (see reference images: the RGB board
	   should stay the same width as the swatch/tool grid, not balloon
	   out past it). Locking the width here means the board's own
	   contents now have to fit THIS width instead of dictating it. */
	width: 113px;
	max-height: min(70vh, calc(100% - 90px));
	overflow-y: auto;
	overflow-x: hidden;
	scrollbar-width: none;
}
/* While the custom color board is open, every sibling (swatches,
   sliders) is collapsed to zero height via .pdf-cb-hidden — but
   they're still grid items, so the panel's row-gap (inherited
   from .pdf-float) kept stacking up between each now-empty row,
   showing as a visible empty band above AND below the board.
   Zeroing the gap while the board is open removes that band;
   the board manages its own internal spacing itself. */
.pdf-float-br.pdf-board-open {
	gap: 0;
}
.pdf-float-br::-webkit-scrollbar { display: none; }

.pdf-fbtn-icon {
	width: 48px;/*32px*/
	height: 32px;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	border: 1px solid transparent;
	background: transparent;
	color: rgba(200,200,220,0.65);
	border-radius: 8px;
	cursor: pointer;
	transition: background .16s ease, color .16s ease, border-color .16s ease, transform .14s cubic-bezier(.34,1.56,.64,1);
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.pdf-fbtn-icon svg {
	stroke: rgba(200,200,220,0.65);
	transition: stroke .16s ease;
}

.pdf-fbtn-icon:hover {
	color: rgba(220,220,240,0.95);
	background: rgba(255,255,255,.1);
	transform: translateY(-1px);
}

.pdf-fbtn-icon:active {
	transform: scale(.88);
}

.pdf-fbtn-icon:hover svg {
	stroke: rgba(220,220,240,0.95);
}

.pdf-fbtn-icon.active {
	color: #fff;
	background: var(--accent);
	border-color: var(--accent);
	animation: pdfToolPop .22s cubic-bezier(.34,1.56,.64,1);
}

.pdf-fbtn-icon.active svg {
	stroke: #fff;
}

@keyframes pdfToolPop {
	0%   { transform: scale(.8); }
	55%  { transform: scale(1.12); }
	100% { transform: scale(1); }
}

.pdf-fsep {
	width: 22px;
	height: 1px;
	background: var(--border);
	margin: 3px auto;
	flex-shrink: 0;
	grid-column: 1 / -1;
	justify-self: center;
}

/* ---------- page badge: current page in a sharp rectangle, total outside ---------- */
.pdf-page-badge {
	display: flex;
	align-items: center;
	gap: 5px;
	font-family: var(--mono);
	font-size: 12px;
	color: rgba(200,200,220,0.7);
	flex-shrink: 0;
}

.pdf-page-input-wrap {
	background: rgba(255,255,255,0.1);
	border: 1px solid rgba(255,255,255,0.18);
	border-radius: 4px;
	padding: 2px 6px;
	display: flex;
	align-items: center;
	outline: none;
}

.pdf-page-input {
	width: 28px;
	background: transparent;
	border: none;
	outline: none;
	-webkit-appearance: none;
	appearance: none;
	box-shadow: none;
	color: #e8e8f0;
	font-family: var(--mono);
	font-size: 12px;
	font-weight: 700;
	text-align: center;
	padding: 0;
	caret-color: #e8e8f0;
}

.pdf-page-input:focus {
	outline: none;
	box-shadow: none;
	color: #e8e8f0;
}

.pdf-page-badge:focus-within .pdf-page-input-wrap {
	border-color: rgba(255,255,255,0.35);
}

.pdf-page-sep {
	color: rgba(200,200,220,0.5);
	font-size: 11px;
}

#pdfPageTotal {
	color: rgba(200,200,220,0.65);
}

/* ---------- table of contents panel — overlays its own pane, just below
   that pane's toolbar. Kept narrow on purpose: a slim, fast-scanning
   list beats a wide overlay, especially now that it has to fit inside
   a single (possibly resized-down) split pane rather than the whole stage.
   Height is fully dynamic: anchored by BOTH top (below the toolbar) and
   bottom (clear of the floating tool palette in the bottom-left corner),
   so it always sizes itself to whatever vertical space the pane's body
   actually has — shrinking/growing live with the window/pane — rather
   than guessing a vh-based max-height that can overlap the floating
   toolbox on short panes. ---------- */
.pdf-toc-panel {
	position: absolute;
	top: 41px;
	left: 0;
	bottom: 0;
	z-index: 35;
	width: 178px;
	max-height: none;
	overflow-y: auto;
	background: rgba(18,18,24,0.99);
	backdrop-filter: blur(16px);
	-webkit-backdrop-filter: blur(16px);
	border: none;
	border-right: 1px solid rgba(255,255,255,0.08);
	border-radius: 0;
	box-shadow: 4px 0 16px rgba(0,0,0,.5);
	padding: 4px 0 6px;
	scrollbar-width: thin;
	/* Slide in from the left instead of an instant display:none/block
	   snap. Stays in the layout (visibility, not display) so the
	   transform has something to animate from/to; pointer-events
	   off while closed so it doesn't intercept clicks while
	   invisible just off-screen. */
	transform: translateX(-100%);
	visibility: hidden;
	pointer-events: none;
	transition: transform .22s cubic-bezier(.22,.9,.32,1.1), visibility .22s;
}
.pdf-toc-panel.open {
	transform: translateX(0);
	visibility: visible;
	pointer-events: auto;
}

/* ---------- search bar: slides down from the toolbar, same idea as
   the TOC panel's slide-in but anchored to the top instead of the
   left edge. Highlighted matches are drawn as temporary overlay
   boxes positioned in % of the page, so they track zoom/rotation
   without any extra coordinate math at render time. ---------- */
.pdf-search-bar {
	position: absolute;
	top: 40px;
	left: 0;
	right: 0;
	z-index: 36;
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 7px 10px;
	background: rgba(18,18,24,0.99);
	backdrop-filter: blur(16px);
	-webkit-backdrop-filter: blur(16px);
	border-bottom: 1px solid rgba(255,255,255,0.08);
	box-shadow: 0 4px 16px rgba(0,0,0,.4);
	color: rgba(220,220,240,0.85);
	font-size: 12px;
	transform: translateY(-100%);
	visibility: hidden;
	pointer-events: none;
	transition: transform .2s cubic-bezier(.22,.9,.32,1.1), visibility .2s;
}
.pdf-search-bar.open {
	transform: translateY(0);
	visibility: visible;
	pointer-events: auto;
}
.pdf-search-input {
	flex: 1;
	min-width: 0;
	background: rgba(255,255,255,0.06);
	border: 1px solid rgba(255,255,255,0.14);
	border-radius: 6px;
	color: #fff;
	font-size: 12.5px;
	padding: 5px 9px;
	outline: none;
}
.pdf-search-input:focus {
	border-color: var(--accent);
}
.pdf-search-count {
	font-family: var(--mono);
	font-size: 11px;
	color: rgba(200,200,220,0.55);
	flex-shrink: 0;
	min-width: 38px;
	text-align: center;
}
.pdf-search-hit {
	position: absolute;
	background: rgba(255, 224, 80, 0.45);
	border: 1.5px solid rgba(255, 200, 40, 0.9);
	border-radius: 2px;
	pointer-events: none;
	z-index: 6;
	transition: background .15s ease, border-color .15s ease;
}
.pdf-search-hit.current {
	background: rgba(124,106,247,0.5);
	border-color: #fff;
}

.pdf-toc-title {
	font-size: 10px;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: rgba(200,200,220,0.5);
	padding: 4px 8px 6px;
	border-bottom: 1px solid rgba(255,255,255,0.07);
	margin-bottom: 4px;
	display: flex;
	align-items: center;
	justify-content: space-between;
}

/* For outline mode: horizontal row items */
.pdf-toc-item {
	display: flex;
	align-items: center;
	gap: 8px;
	width: 100%;
	text-align: left;
	padding: 4px 8px;
	border-radius: 0;
	background: transparent;
	color: rgba(220,220,240,0.85);
	font-size: 11.5px;
	cursor: pointer;
	border: none;
	line-height: 1.35;
}
.pdf-toc-item:hover {
	background: rgba(255,255,255,.08);
}
.pdf-toc-item.active {
	background: rgba(124,106,247,.18);
	color: var(--accent);
}

/* For thumb mode: vertical card layout matching the screenshot */
.pdf-toc-thumb-list {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	padding: 2px 0;
}
.pdf-toc-thumb-item {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	width: 100%;
	padding: 4px 6px;
	border-radius: 0;
	background: transparent;
	border: none;
	cursor: pointer;
	transition: background .12s;
}
.pdf-toc-thumb-item:hover {
	background: rgba(255,255,255,.06);
}
.pdf-toc-thumb-item.active {
	background: rgba(124,106,247,.15);
}
.pdf-toc-thumb-item.active .pdf-toc-item-thumb {
	border-color: var(--accent);
	box-shadow: 0 0 0 2px rgba(124,106,247,.45);
}
.pdf-toc-item-thumb {
	width: calc(100% - 4px);
	aspect-ratio: 3/4;
	height: auto;
	border-radius: 3px;
	background: #111;
	border: 1.5px solid rgba(255,255,255,0.1);
	overflow: hidden;
	display: block;
	flex-shrink: 0;
}
.pdf-toc-item-thumb canvas {
	width: 100%;
	height: 100%;
	display: block;
}
.pdf-toc-item-label {
	display: none;
}
.pdf-toc-item-page {
	font-family: var(--mono);
	font-size: 10px;
	font-weight: 600;
	color: rgba(200,200,220,0.45);
	text-align: center;
}
.pdf-toc-empty {
	padding: 10px 8px;
	font-size: 11px;
	color: rgba(200,200,220,0.4);
	text-align: center;
}

.pdf-color-swatches {
	/* display:contents makes this wrapper invisible to layout — its
	   buttons become direct grid items of the parent .pdf-float-br
	   grid, lining up in the exact same 2-col cells as the custom-
	   color button below them and every tool icon in the canvas
	   toolkit, instead of running their own separate nested grid
	   at a different cell size. */
	display: contents;
}

.pdf-swatch {
	width: 48px;/*32px*/
	height: 32px;
	border-radius: 8px;
	border: 1px solid transparent;
	cursor: pointer;
	flex-shrink: 0;
	padding: 0;
	transition: transform .12s, border-color .12s, background .12s;
}

.pdf-swatch:hover {
	transform: scale(1.08);
}

.pdf-swatch.active {
	border-color: #fff;
	box-shadow: 0 0 0 2px var(--surf2), 0 0 0 3px #fff;
}

/* custom color swatch: wraps the native <input type=color> and
   overlays a little picker glyph so it reads as a deliberate
   "choose a color" control, not a bare browser widget */
.pdf-color-custom {
	position: relative;
	width: 48px;/*32px*/
	height: 32px;
	flex-shrink: 0;
	border-radius: 8px;
	border: 1px solid transparent;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: transform .12s, border-color .12s;
	background: conic-gradient(from 0deg, #e8576a, #e8b84a, #40c9a8, #4a9de8, #7c6af7, #e8576a);
}
.pdf-color-custom:hover {
	transform: scale(1.08);
}
.pdf-color-custom.active {
	border-color: #fff;
	box-shadow: 0 0 0 2px var(--surf2), 0 0 0 3px #fff;
}
.pdf-color-custom-icon {
	width: 12px;
	height: 12px;
	color: #fff;
	filter: drop-shadow(0 0 2px rgba(0,0,0,.6));
	pointer-events: none;
}

/* While the inline RGB/HSV board is open, the swatch grid and the
   stroke-size slider collapse smoothly out of the way instead of
   competing with the board for the same small panel. They come right
   back, with the same animation, the moment the board closes. */
/* While the custom color board is open: hide every individual
   swatch button and the custom-color button itself. They can't be
   hidden via their old wrapper (#pdfColorSwatches is display:contents
   now — see .pdf-color-swatches above — so it has no box of its own
   to collapse; only its children render), so the hidden state has
   to target each swatch directly. */
.pdf-color-swatches.pdf-cb-hidden .pdf-swatch,
.pdf-color-custom.pdf-cb-hidden {
	max-height: 0 !important;
	width: 0 !important;
	opacity: 0 !important;
	margin: 0 !important;
	padding: 0 !important;
	border: none !important;
	overflow: hidden !important;
	pointer-events: none !important;
	transform: scaleY(.85);
	transition: max-height .18s ease, opacity .14s ease, transform .18s ease, margin .18s ease, width .18s ease;
}
#pdfSizeSlider.pdf-cb-hidden,
#pdfOpacitySlider.pdf-cb-hidden {
	max-height: 0 !important;
	opacity: 0 !important;
	margin: 0 !important;
	overflow: hidden !important;
	pointer-events: none !important;
	transform: scaleY(.85);
	transition: max-height .18s ease, opacity .14s ease, transform .18s ease, margin .18s ease;
}
.pdf-color-swatches,
#pdfSizeSlider,
#pdfOpacitySlider {
	max-height: 60px;
	opacity: 1;
	transform: scaleY(1);
	transition: max-height .18s ease, opacity .14s ease, transform .18s ease, margin .18s ease;
}
#pdfSizeSlider, #pdfOpacitySlider {
	grid-column: 1 / -1;
}

/* Inline color board: smooth height/opacity reveal instead of an
   instant display:none/block snap. */
.pdf-color-board-anim {
	transform-origin: top center;
	opacity: 0;
	transform: translateY(-4px) scaleY(.96);
	transition: opacity .16s ease, transform .18s cubic-bezier(.22,.9,.32,1.1);
}
.pdf-color-board-anim.pdf-cb-open {
	opacity: 1;
	transform: translateY(0) scaleY(1);
}
/* Custom-color-board "Select" button — hover fade used to be set via
   onmouseover/onmouseout (inline handlers); moved to CSS. */
.pdf-cb-select-btn {
	opacity: 1;
}
.pdf-cb-select-btn:hover {
	opacity: .8;
}
.pdf-color-picker {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: none;
	border-radius: 50%;
	padding: 0;
	background: none;
	cursor: pointer;
	opacity: 0;
}

/* unlabeled size/opacity sliders — thin track, accent dot thumb, zero bg box */
.pdf-mini-slider {
	width: 100px;/*64px*/
	margin: 3px 0;
	-webkit-appearance: none;
	appearance: none;
	background: none !important;
	background-color: transparent !important;
	cursor: pointer;
	border: none;
	outline: none;
	box-shadow: none;
}

/* WebKit track */
.pdf-mini-slider::-webkit-slider-runnable-track {
	height: 3px;
	border-radius: 2px;
	background: rgba(255,255,255,0.15);
}

/* WebKit thumb */
.pdf-mini-slider::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	width: 12px;
	height: 12px;
	border-radius: 50%;
	background: var(--thumb-color, var(--accent));
	margin-top: -4.5px;
	cursor: pointer;
	border: none;
	box-shadow: none;
	transition: transform .12s ease, background .15s ease;
}
.pdf-mini-slider:hover::-webkit-slider-thumb {
	transform: scale(1.2);
}
.pdf-mini-slider:active::-webkit-slider-thumb {
	transform: scale(1.35);
}

/* Firefox track */
.pdf-mini-slider::-moz-range-track {
	height: 3px;
	border-radius: 2px;
	background: rgba(255,255,255,0.15);
	border: none;
}

/* Firefox thumb */
.pdf-mini-slider::-moz-range-thumb {
	width: 12px;
	height: 12px;
	border-radius: 50%;
	background: var(--thumb-color, var(--accent));
	border: none;
	cursor: pointer;
	transition: background .15s ease;
}

.pdf-mini-slider:focus {
	outline: none;
	box-shadow: none;
}
.pdf-mini-slider:focus-visible {
	outline: none;
	box-shadow: none;
}
.pdf-mini-slider::-webkit-slider-thumb:focus {
	outline: none;
	box-shadow: none;
}

@media (max-width: 700px) {
	/* Each floating panel scales independently now — sized off its
	   OWN content/position, not as one shared "both float panels"
	   rule. Previously a single .pdf-float-bl, .pdf-float-br
	   selector meant any tweak to one panel's scale/size silently
	   moved the other too. */
	.pdf-float-bl {
		transform: scale(.9);
		transform-origin: bottom left;
	}
	.pdf-float-br {
		transform: scale(.9);
		transform-origin: bottom right;
	}
	.pdf-toc-panel {
		width: 150px;
	}
	.pdf-panes.is-split {
		flex-direction: column;
	}
	.pdf-panes.is-split .pdf-pane-resizer {
		width: 100%;
		height: 7px;
		flex: 0 0 7px;
		cursor: row-resize;
	}
	.pdf-panes.is-split .pdf-pane-resizer::after {
		width: 36px;
		height: 3px;
	}
}

/* ---------- dark mode ---------- */
/* Invert the PDF page background to near-black; invert+hue-rotate the
   render canvas so white paper becomes dark while preserving text/color */
.pdf-dark-mode .pdf-page-board {
	background: #111 !important;
	box-shadow: 0 6px 24px rgba(0,0,0,.7);
}
.pdf-dark-mode .pdf-canvas-wrap canvas:first-child {
	filter: invert(1) hue-rotate(180deg) brightness(0.85) contrast(0.9);
}
/* Draw canvas (annotations) stays unfiltered so ink colors stay true */
.pdf-dark-mode .pdf-canvas-wrap .pdf-draw-canvas {
	filter: none !important;
}
.pdf-dark-mode .pdf-page-num-tag {
	color: rgba(200,200,200,.7);
	background: rgba(0,0,0,.55);
}
.pdf-dark-mode .pdf-toc-item-thumb {
	background: #111;
}
.pdf-dark-mode .pdf-toc-item-thumb canvas {
	filter: invert(1) hue-rotate(180deg) brightness(0.85) contrast(0.9);
}

/* Dark-mode toggle button — same shape as other toolbar buttons */
#pdfDarkModeBtn {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	flex-shrink: 0;
	border: none;
	background: transparent;
	color: rgba(200,200,220,0.65);
	border-radius: 7px;
	cursor: pointer;
	transition: all .14s;
	-webkit-tap-highlight-color: transparent;
}
#pdfDarkModeBtn svg { stroke: rgba(200,200,220,0.65); }
#pdfDarkModeBtn:hover { color: #fff; background: rgba(255,255,255,.12); }
#pdfDarkModeBtn:hover svg { stroke: #fff; }
#pdfDarkModeBtn.active { color: #fff; background: var(--accent); }
#pdfDarkModeBtn.active svg { stroke: #fff; }

/* ---------- fullscreen mode ---------- */
/* Fullscreen toggle button — identical shape/states to dark mode */
#pdfFullscreenBtn {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	flex-shrink: 0;
	border: none;
	background: transparent;
	color: rgba(200,200,220,0.65);
	border-radius: 7px;
	cursor: pointer;
	transition: all .14s;
	-webkit-tap-highlight-color: transparent;
}
#pdfFullscreenBtn svg { stroke: rgba(200,200,220,0.65); }
#pdfFullscreenBtn:hover { color: #fff; background: rgba(255,255,255,.12); }
#pdfFullscreenBtn:hover svg { stroke: #fff; }
#pdfFullscreenBtn.active { color: #fff; background: var(--accent); }
#pdfFullscreenBtn.active svg { stroke: #fff; }

/* When fullscreen is on, the whole PDF reader panel ("tab-pdf") lifts
   out of the normal page flow and covers the viewport — tabstrip,
   toolbars, and floating tool/color panels included — so the reader
   body genuinely fills the screen instead of just the stage growing
   inside its existing card. */
#tab-pdf.pdf-fullscreen {
	position: fixed;
	inset: 0;
	z-index: 1000;
	background: var(--bg);
	display: flex;
	flex-direction: column;
	padding: 0;
	margin: 0;
	border-radius: 0;
}
#tab-pdf.pdf-fullscreen .pdf-tabstrip {
	border-radius: 0;
	flex-shrink: 0;
}
#tab-pdf.pdf-fullscreen .pdf-stage {
	flex: 1 1 auto;
	height: auto;
	min-height: 0;
	border-radius: 0;
}
/* Non-fullscreen: bound #tab-pdf to the actual viewport slack left
   after the header and bottom nav, instead of letting it grow with
   its content the way Home/Tracker/Club deliberately do -- see the
   long comment above this rule for why. Also subtracts the alert
   banner's reserved height (--alert-banner-h, 0px when it's not
   showing) -- that banner sits above the header and pushes
   everything below it down by its own height, which this
   calculation didn't originally account for, so a visible banner
   made #tab-pdf's fixed height overshoot the space actually left
   on screen and the outer page/body scrolled by that same amount
   instead of staying pinned. */
#tab-pdf {
	flex: none !important;
	height: calc(100dvh - var(--header-h, 64px) - var(--bottom-nav-h, 76px) - var(--alert-banner-h, 0px)) !important;
}
#tab-pdf.pdf-fullscreen {
	height: auto !important; /* position:fixed;inset:0 above already bounds it fully */
}

/* .bottom-nav and .sponsor-fab are outside .app in the DOM (see shell.js),
   so they sit in the ROOT stacking context with their own z-index (150 /
   140) -- #tab-pdf.pdf-fullscreen's z-index:1000 lives INSIDE .app's own
   stacking context (.app has position:relative;z-index:1) and can never
   out-rank a sibling of .app no matter how high it's set. That let
   .bottom-nav visually cover the bottom of #pdfToolsFloat/#pdfColorFloat
   whenever the PDF reader went fullscreen. Just hide both outright while
   fullscreen is on (see pdfToggleFullscreen() in pdfreader.js) instead of
   fighting that stacking-context boundary.  */
body.pdf-reader-fullscreen .bottom-nav,
body.pdf-reader-fullscreen .sponsor-fab {
	display: none !important;
}

/* ============================================================
   Own video player (.beu-player) -- see assets/js/video-player.js.
   Folded into this shared stylesheet (rather than its own file) like
   every other component on the site, so any page that drops in
   <div class="beu-player" data-src="..."> gets working styles with no
   extra <link> needed.
   ============================================================ */

.beu-player {
	position: relative;
	width: 100%;
	height: 100%;
	overflow: hidden;
	background: var(--surface);
	/* NOT flat black -- a flat black box sitting there while a video
	   loads reads as broken/blank. The poster (below) covers this in
	   practice, but the surface tone shows if there's no poster set. */
	border-radius: var(--r);
}

.beu-player-poster {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: center;
	background-color: var(--surface);
	transition: opacity .35s ease;
	z-index: 1;
}

/* Subtle shimmer over the poster while the first chunk is still in
   flight, so a poster-less player doesn't look inert/broken either. */
.beu-player-poster::after {
	content: '';
	position: absolute;
	inset: 0;
	background: linear-gradient(100deg, transparent 35%, rgba(255, 255, 255, .06) 50%, transparent 65%);
	background-size: 200% 100%;
	animation: beu-player-shimmer 1.6s ease-in-out infinite;
}

.beu-player.beu-player-ready .beu-player-poster {
	opacity: 0;
	pointer-events: none;
}

.beu-player.beu-player-ready .beu-player-poster::after {
	animation: none;
}

@keyframes beu-player-shimmer {
	0% { background-position: 200% 0; }
	100% { background-position: -200% 0; }
}

.beu-player-video {
	position: relative;
	z-index: 0;
	width: 100%;
	height: 100%;
	display: block;
	object-fit: cover;
	background: transparent;
}

.beu-player-spinner {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	display: none;
	gap: 5px;
	z-index: 2;
}

.beu-player-spinner span {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--accent-soft);
	display: inline-block;
	animation: beu-player-bounce 1s infinite ease-in-out both;
}

.beu-player-spinner span:nth-child(2) { animation-delay: .12s; }
.beu-player-spinner span:nth-child(3) { animation-delay: .24s; }

@keyframes beu-player-bounce {
	0%, 80%, 100% { transform: scale(.4); opacity: .5; }
	40% { transform: scale(1); opacity: 1; }
}

.beu-player.beu-player-buffering:not(.beu-player-ready) .beu-player-spinner,
.beu-player.beu-player-buffering.beu-player-playing .beu-player-spinner {
	display: flex;
}

/* ---------- controls bar (only rendered when data-controls is set) ---------- */
.beu-player-controls {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 3;
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 8px 10px;
	background: linear-gradient(to top, rgba(0, 0, 0, .65), transparent);
	opacity: 0;
	transform: translateY(6px);
	transition: opacity .2s ease, transform .2s ease;
}

.beu-player:hover .beu-player-controls,
.beu-player:focus-within .beu-player-controls {
	opacity: 1;
	transform: translateY(0);
}

.beu-ctl {
	background: transparent;
	border: none;
	color: #fff;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 4px;
	border-radius: 6px;
	flex: 0 0 auto;
	opacity: .9;
}

.beu-ctl:hover { opacity: 1; background: rgba(255, 255, 255, .12); }

.beu-ctl-play .ic-pause { display: none; }
.beu-ctl-play.is-playing .ic-play { display: none; }
.beu-ctl-play.is-playing .ic-pause { display: inline-flex; }

.beu-ctl-mute .ic-vol-off { display: none; }
.beu-ctl-mute.is-muted .ic-vol-on { display: none; }
.beu-ctl-mute.is-muted .ic-vol-off { display: inline-flex; }

.beu-ctl-progress {
	position: relative;
	flex: 1 1 auto;
	height: 4px;
	border-radius: 2px;
	background: rgba(255, 255, 255, .25);
	cursor: pointer;
	outline: none;
}

.beu-ctl-progress:focus-visible {
	box-shadow: 0 0 0 2px var(--accent);
}

.beu-ctl-buffered,
.beu-ctl-played {
	position: absolute;
	top: 0;
	left: 0;
	height: 100%;
	border-radius: 2px;
	width: 0%;
}

.beu-ctl-buffered { background: rgba(255, 255, 255, .35); }
.beu-ctl-played { background: var(--accent); }

.beu-ctl-knob {
	position: absolute;
	top: 50%;
	left: 0%;
	width: 10px;
	height: 10px;
	border-radius: 50%;
	background: var(--accent-soft);
	transform: translate(-50%, -50%);
	box-shadow: 0 0 0 2px rgba(0, 0, 0, .35);
}

@media (max-width: 640px) {
	.beu-player-controls { padding: 6px 8px; gap: 6px; }
	.beu-ctl-pip { display: none; } /* little value on small screens, saves space */
}

/* ============================================================
   Cookie consent banner -- see assets/js/cookie-consent.js.
   A single, dependency-free bar pinned to the viewport bottom,
   matching the toast pattern (#__homeToast in home.js) but persistent
   until the person makes a choice instead of auto-dismissing.
   ============================================================ */

.cookie-consent-banner {
	position: fixed;
	left: 50%;
	bottom: 16px;
	z-index: 500;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px 20px;
	width: min(680px, calc(100vw - 32px));
	transform: translateX(-50%) translateY(16px);
	opacity: 0;
	pointer-events: none;
	transition: opacity .25s ease, transform .25s ease;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: var(--r);
	box-shadow: 0 12px 32px -8px rgba(0, 0, 0, .5);
	padding: 16px 18px;
}

.cookie-consent-banner.cookie-consent-visible {
	opacity: 1;
	transform: translateX(-50%) translateY(0);
	pointer-events: auto;
}

.cookie-consent-text {
	flex: 1 1 320px;
	min-width: 0;
	font-size: 13px;
	line-height: 1.5;
	color: var(--text);
}

.cookie-consent-text a {
	color: var(--accent);
	font-weight: 600;
	text-decoration: none;
}

.cookie-consent-text a:hover {
	text-decoration: underline;
}

.cookie-consent-actions {
	display: flex;
	align-items: center;
	gap: 8px;
	flex: 0 0 auto;
	margin-left: auto;
}

@media (max-width: 480px) {
	.cookie-consent-banner {
		bottom: 0;
		width: 100vw;
		border-radius: var(--r) var(--r) 0 0;
		border-left: none;
		border-right: none;
		border-bottom: none;
		padding: 14px 16px calc(14px + env(safe-area-inset-bottom, 0px));
	}
	.cookie-consent-actions {
		width: 100%;
		margin-left: 0;
	}
	.cookie-consent-actions .btn {
		flex: 1 1 0;
	}
}
/* Step 4 username status: slightly deeper green for contrast on the light theme */
[data-auth-theme="light"] .username-status.is-ok { color: #178a70; }

/* ---- Signup wizard step 7: always-open, multi-select department list ---- */
.dept-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
	max-height: min(46vh, 340px);
	overflow-y: auto;
	padding: 2px 4px 2px 0;
	margin-bottom: 10px;
	overscroll-behavior: contain;
}
.dept-option {
	display: flex;
	align-items: center;
	gap: 12px;
	width: 100%;
	flex: none;
	text-align: left;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: var(--r);
	padding: 10px 12px;
	color: var(--text);
	font-size: 14px;
	cursor: pointer;
	transition: border-color .15s, background .15s, opacity .15s;
}
.dept-option:hover,
.dept-option:focus-visible { border-color: var(--accent); }
.dept-option[aria-checked="true"] {
	border-color: var(--accent);
	background: rgba(124, 106, 247, .12);
}
.dept-option.is-disabled { opacity: .45; cursor: not-allowed; }
.dept-option.is-disabled:hover { border-color: var(--border); }
.dept-check {
	flex: none;
	width: 18px;
	height: 18px;
	border-radius: 5px;
	border: 1.5px solid var(--muted);
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 12px;
	font-weight: 700;
	line-height: 1;
	color: #fff;
	transition: background .15s, border-color .15s;
}
.dept-option[aria-checked="true"] .dept-check { background: var(--accent); border-color: var(--accent); }
.dept-option[aria-checked="true"] .dept-check::before { content: '\2713'; }
.dept-name { flex: 1; min-width: 0; }
.dept-count { font-size: 12px; color: var(--muted); margin: 0 0 12px; }

/* Step 13: follower count next to the handle, and a clearer "Following" state */
.people-suggest-count { font-weight: 600; }
.people-suggest-follow-btn.following:hover { border-color: var(--err); color: var(--err); }

/* Signup wizard buttons, every step: Back on the left and Next/Continue/Verify on the
   right of the SAME row (steps with only a Next -- e.g. the username step -- keep it on
   the right). Overrides .auth-submit's width:100%, which used to push Next onto its
   own full-width line under Back on the OTP and profile-photo steps. */
#authSignupWizard .btn-row {
	display: flex;
	flex-wrap: nowrap;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
}
#authSignupWizard .btn-row .btn {
	width: auto;
	flex: 0 0 auto;
}
#authSignupWizard .btn-row .btn.primary {
	margin-left: auto;
}

/* ============================================================
   SOCIAL FEED (assets/js/social.js) -- Bluesky-style posts.
   Everything is namespaced .sp-* so it can't collide with the rest
   of the app. Avatars reuse .comment-avatar (circle + initials).
   ============================================================ */
.sp-section {
	margin: 0 0 24px;
}

.sp-tabs {
	display: flex;
	border-bottom: 1px solid var(--border);
	margin-bottom: 12px;
	/* Stay pinned under the sticky site header while the posts scroll beneath. */
	position: sticky;
	/* Tuck 1px under the (higher z-index) header and pad it back, so a
	   fractional header height / rounded --header-h can never leave a hairline
	   of scrolling posts between the two. */
	top: var(--sp-tabs-top, calc(var(--header-h, 0px) - 1px));   /* set live by spSyncTabsTop() in social.js */
	padding-top: 1px;
	z-index: 110;   /* below header (120), above posts */
	background: var(--bg);
	-webkit-backdrop-filter: blur(10px);
	backdrop-filter: blur(10px);
}

/* Keep the tabs docked below the header when the alert banner pushes the
   header down (see body.alert-banner-open header). */
body.alert-banner-open .sp-tabs {
	top: var(--sp-tabs-top, calc(var(--header-h, 0px) + var(--alert-banner-h, 0px) - 1px));
}

.sp-tab {
	flex: 1;
	background: none;
	border: 0;
	padding: 12px 6px 11px;
	color: var(--muted);
	font-family: var(--sans);
	font-size: 14px;
	font-weight: 700;
	letter-spacing: .01em;
	cursor: pointer;
	position: relative;
	transition: color .15s;
}

.sp-tab:hover { color: var(--text); }

.sp-tab.active {
	color: var(--text);
}

.sp-tab.active::after {
	content: '';
	position: absolute;
	left: 50%;
	bottom: -1px;
	width: min(56px, 60%);
	height: 3px;
	border-radius: 3px;
	background: var(--accent);
	transform: translateX(-50%);
}

/* ----- swipe between feeds (assets/js/feed-swipe.js) -----
   The underline is its own element so it can follow the finger; .has-ind (added by JS) retires the static one. */
.sp-tab-ind {
	position: absolute;
	left: 0;
	bottom: -1px;
	height: 3px;
	width: 0;
	border-radius: 3px;
	background: var(--accent);
	pointer-events: none;
	transition: transform .3s cubic-bezier(.22, .8, .25, 1), width .3s cubic-bezier(.22, .8, .25, 1);
}
.sp-tabs.has-ind .sp-tab.active::after { display: none; }
.sp-tabs.dragging .sp-tab-ind { transition: none; }

/* Vertical scrolling stays with the browser; horizontal drags (finger or mouse) belong to feed-swipe.js. */
.sp-pager {
	position: relative;
	touch-action: pan-y pinch-zoom;
}
.sp-section.sp-swiping { overflow-x: clip; }   /* clip (not hidden): the sticky tabs keep sticking */
.sp-section.sp-swiping .sp-pager {
	will-change: transform;
	-webkit-user-select: none;
	user-select: none;
}
.sp-section.sp-swiping,
.sp-section.sp-swiping * { cursor: grabbing !important; }
.sp-pager img, .sp-pager a { -webkit-user-drag: none; }

/* The neighbouring feed, parked just outside the pager while dragging. */
.sp-peek {
	position: absolute;
	left: 0;
	width: 100%;
	overflow: hidden;
	pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
	.sp-tab-ind { transition: none; }
}

/* ----- composer ----- */
.sp-composer {
	display: flex;
	gap: 10px;
	padding: 12px 14px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 14px;
}

.sp-composer-slot:not(:empty) { margin-bottom: 12px; }

.sp-composer-main {
	flex: 1;
	min-width: 0;
}

.sp-ta-wrap {
	position: relative;
}

.sp-ta-wrap .mention-dropdown {
	bottom: auto;
	top: 100%;
	margin: 6px 0 0;
}

.sp-ta {
	display: block;
	width: 100%;
	box-sizing: border-box;
	min-height: 40px;
	max-height: none;
	overflow: hidden;            /* height follows the text (see grow() in social.js) -> no scrollbar */
	scrollbar-width: none;
	resize: none;
	background: transparent;
	border: 0;
	outline: none;
	padding: 9px 0 6px;
	color: var(--text);
	font-family: var(--sans);
	font-size: 15px;
	line-height: 1.45;
}

.sp-ta::-webkit-scrollbar { display: none; }
.sp-ta::placeholder { color: var(--muted); }

.sp-previews {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.sp-previews:not(:empty) { margin: 6px 0 8px; }

.sp-preview {
	position: relative;
	width: 84px;
	height: 84px;
	border-radius: 10px;
	overflow: hidden;
	border: 1px solid var(--border);
	background: var(--surf2);
}

.sp-preview img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.sp-preview.uploading img { opacity: .45; }
.sp-preview.failed img { opacity: .3; }

/* Centre with negative margins, NOT transform: the spin animation (pyq-spin) animates `transform`, which would
   replace translate(-50%, -50%) and make the spinner jump off-centre while it rotates. */
.sp-preview .beu-spinner-inline {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 24px;
	height: 24px;
	margin: -12px 0 0 -12px;
	border-width: 3px;
	transform: none;
	transform-origin: 50% 50%;
}

.sp-preview-err {
	position: absolute;
	inset: auto 0 6px 0;
	text-align: center;
	font-size: 11px;
	font-weight: 700;
	color: var(--err);
}

.sp-preview-x {
	position: absolute;
	top: 4px;
	right: 4px;
	width: 22px;
	height: 22px;
	border-radius: 50%;
	border: 0;
	background: rgba(0, 0, 0, .65);
	color: #fff;
	font-size: 15px;
	line-height: 1;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
}

.sp-composer-quote {
	margin: 4px 0 8px;
	pointer-events: none;
}

.sp-error {
	color: var(--err);
	font-size: 12.5px;
	margin: 4px 0;
}

.sp-composer-bar {
	display: flex;
	align-items: center;
	gap: 8px;
	padding-top: 8px;
	border-top: 1px solid var(--border);
}

.sp-spacer { flex: 1; }

.sp-tool {
	background: none;
	border: 0;
	color: var(--accent-soft);
	padding: 6px;
	border-radius: 50%;
	cursor: pointer;
	display: inline-flex;
}

.sp-tool:hover:not(:disabled) { background: rgba(124, 106, 247, .14); }
.sp-tool:disabled { cursor: not-allowed; }

.sp-counter {
	font-family: var(--mono);
	font-size: 12px;
	color: var(--muted);
	min-width: 26px;
	text-align: right;
}

.sp-counter.warn { color: var(--warn); }
.sp-counter.over { color: var(--err); font-weight: 700; }

/* ----- feed list ----- */
.sp-new-pill {
	display: flex;
	align-items: center;
	gap: 6px;
	width: max-content;
	/* Stays visible while the feed scrolls: pinned just under the sticky tab bar. */
	position: sticky;
	top: var(--sp-pill-top, calc(var(--header-h, 0px) + 56px));
	z-index: 105;   /* below the tabs (110) and header (120), above posts */
	margin: 0 auto 10px;
	background: var(--accent);
	color: #fff;
	border: 0;
	border-radius: 999px;
	padding: 8px 16px;
	font-family: var(--sans);
	font-size: 13px;
	font-weight: 700;
	cursor: pointer;
	box-shadow: 0 6px 18px -6px rgba(124, 106, 247, .7);
}

.sp-new-pill-ico { display: block; flex: 0 0 auto; }

.sp-feed {
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 14px;
	overflow: hidden;
}

.sp-skel {
	height: 104px;
	border-radius: 0;
	border-bottom: 1px solid var(--border);
}

.sp-more {
	text-align: center;
	padding: 14px 0 2px;
	min-height: 24px;
}

.sp-end {
	font-size: 12px;
	color: var(--muted);
}

.sp-empty {
	padding: 34px 20px;
	text-align: center;
	color: var(--muted);
	font-size: 13.5px;
	line-height: 1.5;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 10px;
}

.sp-empty-title {
	color: var(--text);
	font-size: 15px;
	font-weight: 800;
}

/* ----- a post ----- */
.sp-post {
	padding: 12px 14px;
	border-bottom: 1px solid var(--border);
	cursor: pointer;
	transition: background .12s;
}

.sp-feed > .sp-post:last-child { border-bottom: 0; }
.sp-feed .sp-post:hover { background: rgba(255, 255, 255, .02); }

.sp-row {
	display: flex;
	gap: 10px;
	align-items: flex-start;
}

.sp-avatar {
	width: 40px;
	height: 40px;
	font-size: 14px;
	cursor: pointer;
}

.sp-avatar-sm { width: 32px; height: 32px; font-size: 11px; }
.sp-avatar-xs { width: 18px; height: 18px; font-size: 8px; cursor: default; }

.sp-avatar-col {
	flex-shrink: 0;
	align-self: stretch;
	position: relative;
}

.sp-main {
	flex: 1;
	min-width: 0;
}

.sp-reason {
	display: flex;
	align-items: center;
	gap: 6px;
	margin: -2px 0 4px 50px;
	font-size: 12px;
	font-weight: 600;
	color: var(--muted);
}

.sp-reason b { color: var(--text); }

.sp-head {
	display: flex;
	align-items: baseline;
	gap: 5px;
	min-width: 0;
	line-height: 1.3;
}

.sp-name {
	font-weight: 800;
	font-size: 14.5px;
	color: var(--text);
	cursor: pointer;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	flex-shrink: 1;
	min-width: 0;
	max-width: 55%;
}

.sp-name:hover { text-decoration: underline; }
.call-you { margin-left: 6px; font-weight: 500; opacity: .75; white-space: nowrap; }
.sp-you { flex-shrink: 0; margin-left: -3px; font-size: 13px; font-weight: 600; color: var(--muted); white-space: nowrap; }

.sp-handle {
	color: var(--muted);
	font-size: 13px;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	flex-shrink: 2;
	min-width: 0;
}

.sp-dot, .sp-time {
	color: var(--muted);
	font-size: 13px;
	white-space: nowrap;
	flex-shrink: 0;
}

.sp-follow-chip {
	margin-left: auto;
	flex-shrink: 0;
	align-self: center;
	background: var(--accent);
	border: 1px solid var(--accent);
	color: #fff;
	border-radius: 999px;
	padding: 6px 16px;
	font-family: var(--sans);
	font-size: 13.5px;
	font-weight: 700;
	line-height: 1.2;
	cursor: pointer;
	transition: background .15s, border-color .15s;
}

.sp-follow-chip:hover { background: color-mix(in srgb, var(--accent) 85%, #fff 15%); border-color: color-mix(in srgb, var(--accent) 85%, #fff 15%); }

.sp-replying {
	font-size: 12.5px;
	color: var(--muted);
	margin-top: 1px;
}

.sp-text {
	margin-top: 3px;
	font-size: 14.5px;
	line-height: 1.5;
	color: var(--text);
	overflow-wrap: anywhere;
	word-break: break-word;
}

.sp-text a { color: var(--accent-soft); word-break: break-all; }
.sp-hashtag { color: var(--accent-soft); cursor: pointer; }
.sp-hashtag:hover { text-decoration: underline; }
.sp-hashtag-more { display: block; margin: 14px auto 24px; }

/* images */
.sp-imgs {
	margin-top: 8px;
	display: grid;
	gap: 3px;
	border-radius: 12px;
	overflow: hidden;
	border: 1px solid var(--border);
}

.sp-img {
	display: block;
	padding: 0;
	margin: 0;
	border: 0;
	background: var(--surf2);
	cursor: zoom-in;
	overflow: hidden;
	min-width: 0;
}

.sp-img img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Single-image posts: the frame hugs the image's own aspect ratio (set
   inline from the real upload dimensions, see spImagesHtml() in social.js)
   instead of being force-cropped into a fixed 16:10 box, so the border
   matches the actual photo instead of chopping off part of it. */
/* The frame's width comes from an inline min(100%, <440px * ratio>) set in
   spImagesHtml(); until that is known (older posts, image not loaded yet)
   it falls back to the column width. Height follows from the inline
   aspect-ratio on .sp-img, so the border always wraps the image. */
.sp-imgs-1 { max-width: 100%; }
.sp-imgs-1 .sp-img { width: 100%; }
.sp-imgs-1 .sp-img img { height: auto; object-fit: cover; }
.sp-imgs-2 { grid-template-columns: 1fr 1fr; aspect-ratio: 16 / 10; }
.sp-imgs-3 { grid-template-columns: 2fr 1fr; grid-template-rows: 1fr 1fr; aspect-ratio: 16 / 10; }
.sp-imgs-3 .sp-img-0 { grid-row: 1 / 3; }
.sp-imgs-4 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; aspect-ratio: 16 / 10; }

/* quote post */
.sp-quote {
	margin-top: 8px;
	padding: 10px 12px;
	border: 1px solid var(--border);
	border-radius: 12px;
	cursor: pointer;
	background: rgba(255, 255, 255, .015);
	overflow: hidden;
}

.sp-quote:hover { background: var(--surf2); }
.sp-quote-missing { cursor: default; color: var(--muted); font-size: 13px; }
.sp-quote-missing:hover { background: rgba(255, 255, 255, .015); }

.sp-quote-head {
	display: flex;
	align-items: center;
	gap: 6px;
	min-width: 0;
	font-size: 13px;
}

.sp-quote-head .sp-name { font-size: 13.5px; }

.sp-quote-text {
	margin-top: 4px;
	font-size: 14px;
	line-height: 1.45;
	overflow-wrap: anywhere;
	display: -webkit-box;
	-webkit-line-clamp: 6;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.sp-quote-img {
	margin-top: 8px;
	border-radius: 8px;
	overflow: hidden;
	max-height: 180px;
}

.sp-quote-img img { display: block; width: 100%; height: 180px; object-fit: cover; }

/* action bar */
.sp-actions {
	display: flex;
	align-items: center;
	justify-content: flex-start;
	max-width: none;
	margin-top: 6px;
}

.sp-act-more {
	margin-left: auto;
}

.sp-act {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	min-width: 44px;
	background: none;
	border: 0;
	padding: 6px 8px 6px 0;
	color: var(--muted);
	font-family: var(--sans);
	font-size: 12.5px;
	font-weight: 600;
	cursor: pointer;
	transition: color .15s;
}

.sp-act svg { flex-shrink: 0; }
.sp-act:hover, .sp-act:focus-visible { outline: none; }
.sp-act-reply:hover { color: var(--accent-soft); }
.sp-act-repost:hover, .sp-act-repost.on { color: var(--accent-soft); }
.sp-act-like:hover, .sp-act-like.on { color: var(--accent-soft); }
.sp-act-views { cursor: default; }
.sp-act-more:hover { color: var(--text); }
.sp-act-more { min-width: 0; padding-right: 0; }
.sp-act-like.on svg { animation: commentHeartPop .4s ease; transform-origin: center; }

.sp-actions-big {
	max-width: none;
	justify-content: space-around;
	border-top: 1px solid var(--border);
	margin-top: 0;
	padding: 4px 0;
}

/* ----- thread overlay ----- */
.sp-thread {
	position: fixed;
	inset: 0;
	z-index: 1100;
	background: var(--bg);
	display: none;
	flex-direction: column;
}

.sp-thread.open { display: flex; }

.sp-thread-head {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: calc(env(safe-area-inset-top, 0px) + 6px) 12px 6px;
	min-height: 48px;
	border-bottom: 1px solid var(--border);
	background: var(--bg);
}

.sp-thread-back {
	background: none;
	border: 0;
	color: var(--text);
	padding: 8px;
	border-radius: 50%;
	cursor: pointer;
	display: inline-flex;
}

.sp-thread-back:hover { background: var(--surf2); }
.sp-thread-title { font-size: 16px; font-weight: 800; }

.sp-thread-body {
	flex: 1;
	overflow-y: auto;
	overscroll-behavior: contain;
	width: 100%;
	max-width: 680px;
	margin: 0 auto;
	box-sizing: border-box;
	border-left: 1px solid var(--border);
	border-right: 1px solid var(--border);
	padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 40px);
}

.sp-thread-loading {
	padding: 40px 0;
	text-align: center;
	color: var(--muted);
	font-size: 13px;
}

.sp-thread-body .sp-post { cursor: pointer; }
.sp-thread-body .sp-v-focus { cursor: default; }

.sp-v-parent { border-bottom: 0; padding-bottom: 0; }
.sp-connect-down .sp-avatar-col::after {
	content: '';
	position: absolute;
	left: 19px;
	top: 44px;
	bottom: -12px;
	width: 2px;
	border-radius: 2px;
	background: var(--border);
}

.sp-v-parent .sp-actions { display: none; }
.sp-v-parent .sp-main { padding-bottom: 12px; }

.sp-v-focus { padding: 14px 16px 0; cursor: default; }

.sp-focus-head {
	display: flex;
	align-items: center;
	gap: 10px;
}

.sp-focus-id { min-width: 0; }
.sp-focus-id .sp-head { flex-direction: column; align-items: flex-start; gap: 0; }
.sp-focus-id .sp-name { max-width: 100%; }

.sp-focus-body .sp-text { font-size: 17px; line-height: 1.5; margin-top: 10px; }
.sp-focus-body .sp-replying { margin-top: 8px; }

.sp-focus-time {
	margin-top: 12px;
	font-size: 13px;
	color: var(--muted);
}

.sp-stats {
	display: flex;
	gap: 16px;
	flex-wrap: wrap;
	padding: 12px 0;
	margin-top: 12px;
	border-top: 1px solid var(--border);
	font-size: 13px;
	color: var(--muted);
}

.sp-stats b { color: var(--text); font-weight: 800; }

.sp-v-focus .sp-actions-big { margin: 0 -16px; padding: 4px 16px; border-bottom: 1px solid var(--border); }

.sp-reply-prompt {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	background: none;
	border: 0;
	border-bottom: 1px solid var(--border);
	padding: 12px 16px;
	color: var(--muted);
	font-family: var(--sans);
	font-size: 14px;
	text-align: left;
	cursor: pointer;
}

.sp-reply-prompt:hover { background: rgba(255, 255, 255, .02); }

.sp-v-reply {
	padding-left: calc(14px + var(--sp-indent, 0) * 20px);
}

.sp-v-reply .sp-avatar { width: 34px; height: 34px; font-size: 12px; }
.sp-v-reply[data-sp-depth]:not([data-sp-depth="1"]):not([data-sp-depth="0"]) { border-left: 2px solid var(--border); margin-left: 14px; padding-left: calc(12px + (var(--sp-indent, 1) - 1) * 14px); }

.sp-no-replies {
	text-align: center;
	color: var(--muted);
	font-size: 13px;
	padding: 30px 0;
}

/* ----- reply / quote modal ----- */
.sp-modal-bg {
	position: fixed;
	inset: 0;
	z-index: 1250;
	background: rgba(13, 13, 15, .7);
	backdrop-filter: blur(4px);
	display: flex;
	align-items: flex-start;
	justify-content: center;
	padding: calc(env(safe-area-inset-top, 0px) + 5vh) 12px 12px;
	animation: fadeIn .15s ease;
}

.sp-modal {
	width: min(580px, 100%);
	max-height: 88vh;
	overflow-y: auto;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 16px;
	box-shadow: 0 16px 60px rgba(0, 0, 0, .55);
}

.sp-modal-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 12px 14px;
	border-bottom: 1px solid var(--border);
	font-weight: 800;
	font-size: 15px;
}

.sp-modal-cancel {
	background: none;
	border: 0;
	color: var(--accent-soft);
	font-family: var(--sans);
	font-size: 14px;
	font-weight: 700;
	cursor: pointer;
	padding: 4px 2px;
}

.sp-modal-headspace { min-width: 48px; }

.sp-modal-body .sp-composer {
	border: 0;
	border-radius: 0;
	background: transparent;
}

.sp-modal-context .sp-post {
	cursor: default;
	border-bottom: 0;
	padding-bottom: 0;
}

.sp-modal-context .sp-avatar-col::after {
	content: '';
	position: absolute;
	left: 19px;
	top: 44px;
	bottom: -12px;
	width: 2px;
	background: var(--border);
}

.sp-modal-context .sp-main { padding-bottom: 12px; }

/* ----- bottom sheet + lightbox ----- */
.sp-sheet-bg {
	position: fixed;
	inset: 0;
	z-index: 1300;
	background: rgba(13, 13, 15, .6);
	display: flex;
	align-items: flex-end;
	justify-content: center;
	animation: fadeIn .12s ease;
}

.sp-sheet {
	width: min(440px, 100%);
	background: var(--surface);
	border: 1px solid var(--border);
	border-bottom: 0;
	border-radius: 16px 16px 0 0;
	padding: 8px 8px calc(env(safe-area-inset-bottom, 0px) + 10px);
	animation: up .18s ease;
}

/* Every action sheet (Copy link, Cover image, post menus...) sits in the middle of the screen, at any width. */
.sp-sheet-bg { align-items: center; padding: 16px; box-sizing: border-box; }
.sp-sheet { border-radius: 16px; border-bottom: 1px solid var(--border); padding-bottom: 10px; max-height: 100%; overflow-y: auto; }

.sp-sheet-title {
	padding: 10px 12px 6px;
	font-size: 12.5px;
	color: var(--muted);
	font-weight: 700;
}

.sp-sheet-item {
	display: flex;
	align-items: center;
	gap: 12px;
	width: 100%;
	background: none;
	border: 0;
	border-radius: 10px;
	padding: 13px 12px;
	color: var(--text);
	font-family: var(--sans);
	font-size: 15px;
	font-weight: 600;
	text-align: left;
	cursor: pointer;
}

.sp-sheet-item:hover { background: var(--surf2); }
.sp-sheet-item.danger { color: var(--err); }
.sp-sheet-cancel {
	justify-content: center;
	text-align: center;
	margin-top: 8px;
	background: var(--surf2);
	color: var(--text);
	border: 0;
	border-radius: 12px;
	padding: 12px;
}
.sp-sheet-cancel:hover { background: color-mix(in srgb, var(--surf2) 78%, var(--text) 22%); }

.sp-lightbox {
	position: fixed;
	inset: 0;
	z-index: 1400;
	background: rgba(0, 0, 0, .92);
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: zoom-out;
	animation: fadeIn .15s ease;
}

.sp-lightbox img {
	max-width: 96vw;
	max-height: 92vh;
	object-fit: contain;
	border-radius: 6px;
}

.sp-lightbox-close {
	position: absolute;
	top: calc(env(safe-area-inset-top, 0px) + 12px);
	right: 14px;
	width: 38px;
	height: 38px;
	border-radius: 50%;
	border: 0;
	background: rgba(255, 255, 255, .14);
	color: #fff;
	font-size: 24px;
	line-height: 1;
	cursor: pointer;
}

/* ============================================================
   SHORTS (assets/js/shorts.js) -- full-screen vertical video feed.
   Everything is namespaced .sh-*. While the Shorts view is open,
   <body> gets .shorts-active (set by shortsOnViewChange()), which
   hides the app header so the player owns the whole screen above
   the bottom nav.
   ============================================================ */
body.shorts-active { overflow: hidden; }
body.shorts-active .app > header { display: none; }
body.shorts-active .app { padding-bottom: 0; }

.sh-root {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: var(--bottom-nav-h, calc(64px + env(safe-area-inset-bottom)));
	z-index: 100;
	background: #000;
	display: flex;
	justify-content: center;
	color: #fff;
}

.sh-stage {
	position: relative;
	width: min(100%, 520px);
	height: 100%;
	overflow: hidden;
	background: #000;
}

/* Ambient mode: a tiny canvas (assets/js/shorts.js keeps it painted with the
   current short's frame) blown up and heavily blurred behind the stage --
   visible only in the margins on a screen wider than the stage's own 520px
   cap; .sh-stage's own solid background simply covers it everywhere else.
   Sized and centered to match .sh-stage itself (not the full-width .sh-root
   it lives in) so the glow stays a tight halo hugging the player, like
   YouTube's own ambient mode, instead of washing out the entire page. */
.sh-ambient {
	position: absolute;
	top: 0;
	left: 50%;
	z-index: 0;
	width: min(100%, 520px);
	height: 100%;
	filter: blur(60px) saturate(1.6) brightness(.7);
	transform: translateX(-50%) scale(1.3);   /* small, YouTube-style bleed past the stage's own edge */
	opacity: 0;
	transition: opacity .5s ease;
	pointer-events: none;
}

.sh-ambient.on { opacity: 1; }

/* ----- top bar: sound | search (centre) | upload ----- */
.sh-topbar {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	z-index: 20;
	display: grid;
	grid-template-columns: auto minmax(0, 1fr) auto;
	align-items: center;
	gap: 8px;
	padding: calc(env(safe-area-inset-top, 0px) + 10px) 10px 22px;
	background: linear-gradient(rgba(0, 0, 0, .62), rgba(0, 0, 0, 0));
	pointer-events: none;
}

.sh-topbar > * { pointer-events: auto; }
.sh-top-side { display: flex; }
.sh-top-right { justify-content: flex-end; gap: 8px; }
.sh-fs-btn svg { display: block; }

.sh-icon-btn {
	width: 40px;
	height: 40px;
	border-radius: 50%;
	border: 0;
	background: rgba(30, 30, 36, .62);
	backdrop-filter: blur(6px);
	color: #fff;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	padding: 0;
}

.sh-icon-btn:active { transform: scale(.94); }
.sh-icon-btn.on { background: var(--accent); color: #fff; }

.sh-search {
	display: flex;
	align-items: center;
	gap: 8px;
	height: 40px;
	padding: 0 12px;
	margin: 0;
	border-radius: 999px;
	background: rgba(30, 30, 36, .72);
	border: 1px solid rgba(255, 255, 255, .16);
	backdrop-filter: blur(8px);
	color: rgba(255, 255, 255, .75);
}

.sh-search:focus-within { border-color: var(--accent); background: rgba(24, 24, 30, .92); }

.sh-search input {
	flex: 1;
	min-width: 0;
	background: transparent;
	border: 0;
	outline: none;
	color: #fff;
	font-family: var(--sans);
	font-size: 16px; /* >=16px stops iOS zooming the page on focus */
	padding: 0;
	-webkit-appearance: none;
	appearance: none;
}

.sh-search input::placeholder { color: rgba(255, 255, 255, .55); }
.sh-search input::-webkit-search-cancel-button { display: none; }

.sh-search-mic {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 28px;
	height: 28px;
	margin-right: -6px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: transparent;
	color: rgba(255, 255, 255, .75);
	cursor: pointer;
}
.sh-search-mic:hover { color: #fff; }
.sh-search-mic.listening { color: #fff; background: #e5484d; animation: beuMicPulse 1.1s ease-in-out infinite; }
html[data-auth-theme="light"] .sh-search-mic { color: var(--muted); }
html[data-auth-theme="light"] .sh-search-mic.listening { color: #fff; }

.sh-search-clear {
	background: rgba(255, 255, 255, .18);
	border: 0;
	border-radius: 50%;
	width: 22px;
	height: 22px;
	line-height: 1;
	color: #fff;
	font-size: 16px;
	cursor: pointer;
	padding: 0;
}

/* ----- the scroller (one snap-page per short) ----- */
.sh-scroller {
	position: relative;
	width: 100%;
	height: 100%;
	overflow-y: auto;
	scroll-snap-type: y mandatory;
	overscroll-behavior: contain;
	scrollbar-width: none;
	-webkit-overflow-scrolling: touch;
}

.sh-scroller::-webkit-scrollbar { display: none; }

.sh-item {
	position: relative;
	height: 100%;
	scroll-snap-align: start;
	scroll-snap-stop: always;
	overflow: hidden;
	background: #000;
}

.sh-media { position: absolute; inset: 0; }

.sh-burst {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	pointer-events: none;
	z-index: 8;
	opacity: 0;
	color: var(--accent-soft);
	filter: drop-shadow(0 4px 16px rgba(0, 0, 0, .5));
}

.sh-burst svg { width: 110px; height: 110px; }
.sh-burst.go { animation: shBurst .75s ease-out; }

@keyframes shBurst {
	0% { opacity: 0; transform: translate(-50%, -50%) scale(.3); }
	22% { opacity: 1; transform: translate(-50%, -50%) scale(1.15); }
	100% { opacity: 0; transform: translate(-50%, -62%) scale(1.5); }
}

.sh-media .bvp-host { position: absolute; inset: 0; }

/* Both the right-side action rail and the bottom-left publisher/name block
   sit just above the video's own control bar (play/pause, scrubber, volume,
   fullscreen -- see .bvp-bar below), so the two never overlap when the
   controls fade in on hover/tap. */
:root {
	--sh-ctrl-h: 66px;    /* approx. height of the shorts video control bar */
	--sh-gap: 14px;       /* margin-bottom breathing room used throughout */
}

/* ----- right rail: like / comment / share / more ----- */
.sh-rail {
	position: absolute;
	right: 6px;
	bottom: var(--sh-gap);
	transition: bottom .18s ease;
	z-index: 6;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 16px;
}

.sh-act {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 3px;
	min-width: 52px;
	background: none;
	border: 0;
	color: #fff;
	font-family: var(--sans);
	font-size: 12px;
	font-weight: 700;
	cursor: pointer;
	padding: 2px;
	text-shadow: 0 1px 6px rgba(0, 0, 0, .7);
	filter: drop-shadow(0 1px 4px rgba(0, 0, 0, .55));
	-webkit-tap-highlight-color: transparent;
}

.sh-act:active svg { transform: scale(.88); }
.sh-act svg { transition: transform .12s; }
.sh-act-like.on { color: var(--accent-soft); }
.sh-act-views { cursor: default; pointer-events: none; }
.sh-act-like.on svg { animation: commentHeartPop .4s ease; }
.sh-act-more { min-width: 40px; }

/* ----- bottom-left: publisher + follow + name ----- */
.sh-info {
	position: absolute;
	left: 0;
	right: 72px;
	bottom: var(--sh-gap);
	transition: bottom .18s ease;
	z-index: 6;
	padding: 0 14px 12px;
	pointer-events: none;
}

/* This used to lift .sh-rail / .sh-info only while the control bar was
   actually shown (.sh-ctrl-show, toggled by bsky-video-player.js's
   _renderControls()), animating them up and down on every hover/tap. They
   now sit at that lifted position permanently -- see the .sh-rail, .sh-info
   rule further down -- so this no longer does anything; left as a no-op
   rather than removed in case something still relies on .sh-ctrl-show being
   present on .sh-item. */
.sh-item.sh-ctrl-show .sh-rail,
.sh-item.sh-ctrl-show .sh-info {
	bottom: calc(var(--sh-ctrl-h) + var(--sh-gap));
}

/* Full-bleed dark backdrop behind the rail, the publisher/name block and the
   control bar -- spans the whole width and reaches the true bottom of the
   short, so those elements always read clearly regardless of what's playing
   underneath, instead of the publisher/name block carrying its own separate,
   hard-edged dark box (which left a visible seam next to the rail). */
.sh-scrim {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 46%;
	z-index: 5;
	pointer-events: none;
	background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, .72) 60%, rgba(0, 0, 0, .72));
}

.sh-info > * { pointer-events: auto; }

.sh-pub {
	display: flex;
	align-items: center;
	gap: 10px;
	min-width: 0;
}

.sh-avatar {
	width: 38px;
	height: 38px;
	flex-shrink: 0;
	border-radius: 50%;
	overflow: hidden;
	background: var(--surf2, #26262c);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	font-size: 13px;
	font-weight: 800;
	cursor: pointer;
	border: 1.5px solid rgba(255, 255, 255, .5);
}

.sh-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* live emoji avatar: show the whole animated emoji (not a cropped zoom) on the avatar disc */
.sh-avatar .emoji-avatar { width: 100%; height: 100%; padding: 2px; box-sizing: border-box; }
.sh-avatar img.emoji-avatar-img { object-fit: contain; }
.sh-avatar-sm { width: 32px; height: 32px; font-size: 11px; border-width: 1px; }

.sh-handle {
	font-size: 15px;
	font-weight: 800;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	min-width: 0;
	cursor: pointer;
	text-shadow: 0 1px 6px rgba(0, 0, 0, .7);
}

/* "(You)" next to the publisher: same look as .sp-you in the feed / profile posts (13px, 600, muted colour, sitting
   close to the name). .sh-pub has a 10px flex gap, so -8px leaves the same ~2px the post header ends up with. */
.sh-you {
	flex-shrink: 0;
	margin-left: -8px;
	font-size: 13px;
	font-weight: 600;
	white-space: nowrap;
	color: var(--muted);
	text-shadow: 0 1px 6px rgba(0, 0, 0, .7);
}

.sh-follow {
	flex-shrink: 0;
	height: 32px;
	padding: 0 16px;
	border: 0;
	border-radius: 999px;
	background: #fff;
	color: #111;
	font-family: var(--sans);
	font-size: 13.5px;
	font-weight: 800;
	cursor: pointer;
}

.sh-follow.on { background: rgba(255, 255, 255, .22); color: #fff; }

.sh-title {
	margin-top: 10px;
	font-size: 15px;
	line-height: 1.35;
	font-weight: 500;
	overflow-wrap: anywhere;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	cursor: pointer;
	text-shadow: 0 1px 6px rgba(0, 0, 0, .75);
}

.sh-title.expanded { -webkit-line-clamp: unset; max-height: 32vh; overflow-y: auto; }

.sh-hashtag {
	color: #8fd3ff;
	font-weight: 700;
}

.sh-card-title .sh-hashtag { color: var(--accent-soft); }

.sh-status {
	position: absolute;
	left: 50%;
	bottom: 18px;
	transform: translateX(-50%);
	z-index: 9;
	pointer-events: none;
}

.sh-empty {
	height: 100%;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 12px;
	padding: 24px;
	text-align: center;
	color: rgba(255, 255, 255, .72);
	font-size: 14px;
	line-height: 1.5;
}

.sh-empty-title { color: #fff; font-size: 17px; font-weight: 800; }

/* ----- search results ----- */
.sh-search-panel {
	position: absolute;
	inset: 0;
	z-index: 15;
	overflow-y: auto;
	background: #0d0d0f;
	padding: calc(env(safe-area-inset-top, 0px) + 70px) 12px 20px;
}

.sh-search-panel[hidden] { display: none; }
.sh-results-title { font-size: 13px; color: var(--muted, #8a8a96); font-weight: 700; margin: 0 2px 10px; }
.sh-results { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }

.sh-card {
	display: flex;
	flex-direction: column;
	gap: 5px;
	background: none;
	border: 0;
	padding: 0;
	color: #fff;
	text-align: left;
	font-family: var(--sans);
	cursor: pointer;
	min-width: 0;
}

.sh-card-thumb {
	position: relative;
	display: block;
	aspect-ratio: 9 / 16;
	border-radius: 12px;
	overflow: hidden;
	background: #1c1c22;
}

.sh-card-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sh-card-noposter { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 28px; color: rgba(255, 255, 255, .5); }
.sh-card-dur { position: absolute; right: 6px; bottom: 6px; background: rgba(0, 0, 0, .7); border-radius: 6px; padding: 2px 6px; font-size: 11px; font-weight: 700; }
.sh-card-title { font-size: 13.5px; font-weight: 700; line-height: 1.3; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow-wrap: anywhere; }
.sh-card-meta { font-size: 12px; color: var(--muted, #8a8a96); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sh-results-more { text-align: center; padding: 16px 0 4px; }

/* ----- comments sheet (slides over the bottom of the player) ----- */
.sh-comments {
	position: absolute;
	inset: 0;
	z-index: 25;
	display: none;
	align-items: flex-end;
	background: rgba(0, 0, 0, .5);
}

.sh-comments.open { display: flex; }

.sh-comments-panel {
	width: 100%;
	height: 70%;
	display: flex;
	flex-direction: column;
	background: #16161a;
	border-radius: 18px 18px 0 0;
	overflow: hidden;
	animation: up .2s ease;
}

.sh-comments-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 14px 16px 12px;
	font-weight: 800;
	font-size: 15px;
	border-bottom: 1px solid rgba(255, 255, 255, .09);
}

.sh-x {
	background: none;
	border: 0;
	color: #fff;
	font-size: 26px;
	line-height: 1;
	cursor: pointer;
	padding: 0 4px;
}

.sh-comments-list { flex: 1; overflow-y: auto; padding: 8px 16px; overscroll-behavior: contain; }
.sh-c-empty { text-align: center; color: rgba(255, 255, 255, .55); font-size: 13.5px; padding: 40px 0; }

.sh-comment { display: flex; gap: 10px; padding: 10px 0; }
.sh-c-main { flex: 1; min-width: 0; }
.sh-c-head { display: flex; gap: 8px; align-items: baseline; font-size: 12.5px; color: rgba(255, 255, 255, .55); }
.sh-c-head b { color: rgba(255, 255, 255, .9); font-weight: 700; }
.sh-c-body { font-size: 14.5px; line-height: 1.4; margin-top: 2px; overflow-wrap: anywhere; }
.sh-c-body a { color: var(--accent-soft, #a99cff); }
.sh-c-actions { display: flex; gap: 14px; margin-top: 2px; }
.sh-c-btn { background: none; border: 0; padding: 4px 0; color: rgba(255, 255, 255, .5); font-family: var(--sans); font-size: 12px; font-weight: 700; cursor: pointer; }
.sh-c-btn:hover { color: #fff; }

.sh-comments-form {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
	border-top: 1px solid rgba(255, 255, 255, .09);
}

.sh-comments-form input {
	flex: 1;
	min-width: 0;
	height: 40px;
	border-radius: 999px;
	border: 1px solid rgba(255, 255, 255, .14);
	background: rgba(255, 255, 255, .06);
	color: #fff;
	padding: 0 14px;
	font-family: var(--sans);
	font-size: 16px;
	outline: none;
}

.sh-comments-form input:focus { border-color: var(--accent); }

.sh-send {
	width: 40px;
	height: 40px;
	border-radius: 50%;
	border: 0;
	background: var(--accent);
	color: #fff;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
}

.sh-send:disabled { opacity: .35; cursor: not-allowed; }

/* ----- upload modal + action sheet (body-level, above the nav) ----- */
.sh-modal-bg {
	position: fixed;
	inset: 0;
	z-index: 1250;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 16px;
	background: rgba(13, 13, 15, .72);
	backdrop-filter: blur(4px);
	animation: fadeIn .15s ease;
}

.sh-modal {
	width: min(460px, 100%);
	max-height: 92vh;
	overflow-y: auto;
	background: var(--surface, #16161a);
	color: var(--text, #f2f2f5);
	border: 1px solid var(--border, #2a2a32);
	border-radius: 16px;
	box-shadow: 0 16px 60px rgba(0, 0, 0, .55);
}

.sh-modal .sh-x { color: var(--text, #f2f2f5); }
.sh-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; border-bottom: 1px solid var(--border, #2a2a32); font-size: 16px; }
.sh-up-body { padding: 16px; display: flex; flex-direction: column; gap: 14px; }

.sh-up-progress { position: relative; height: 26px; border-radius: 999px; background: var(--surf2, #26262c); overflow: hidden; }
.sh-up-progress i { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: var(--accent); transition: width .2s; }
.sh-up-progress span { position: relative; display: block; text-align: center; line-height: 26px; font-size: 12px; font-weight: 800; }

.sh-cc-list { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.sh-cc-list:empty { display: none; margin: 0; }
.sh-cc-chip { display: inline-flex; align-items: center; gap: 6px; padding: 5px 6px 5px 12px; border-radius: 999px; background: var(--surf2, #26262c); font-size: 12.5px; font-weight: 600; }
.sh-cc-chip button { display: flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 999px; border: 0; background: var(--surf3, #32323a); color: inherit; font-size: 14px; line-height: 1; cursor: pointer; }
.sh-cc-chip button:hover { background: var(--err, #ff6b81); color: #fff; }
.sh-cc-picker { display: flex; flex-direction: column; gap: 8px; padding: 10px; margin-bottom: 8px; border-radius: 10px; background: var(--surf2, #26262c); }
.sh-cc-picker input { width: 100%; }

/* ===== Publish a short: sheet layout (v82) ===== */
.sh-modal.sh-up {
	width: min(760px, 100%);
	max-height: min(92vh, 860px);
	max-height: min(92dvh, 860px);
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border-radius: 20px;
}
.sh-up-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 18px 20px 4px; }
.sh-up-head h2 { margin: 0; font-size: 19px; font-weight: 800; letter-spacing: -.01em; }
.sh-up-head p { margin: 3px 0 0; font-size: 13px; color: var(--muted, #8a8a96); }
.sh-up .sh-x { font-size: 28px; width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; padding: 0; margin: -4px -6px 0 0; }
.sh-up .sh-x:hover { background: var(--surf2, #26262c); }

.sh-steps { list-style: none; display: flex; align-items: center; gap: 0; margin: 14px 20px 0; padding: 0 0 14px; border-bottom: 1px solid var(--border, #2a2a32); }
.sh-steps li { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 700; color: var(--muted, #8a8a96); white-space: nowrap; }
.sh-steps li + li::before { content: ''; width: 28px; height: 2px; margin: 0 10px; border-radius: 2px; background: var(--border, #2a2a32); }
.sh-steps li i { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%; font-style: normal; font-size: 11.5px; background: var(--surf2, #26262c); color: var(--muted, #8a8a96); }
.sh-steps li.on { color: var(--text, #f2f2f5); }
.sh-steps li.on i { background: var(--accent); color: #fff; }
.sh-steps li.done { color: var(--text, #f2f2f5); }
.sh-steps li.done i { background: var(--ok, #2fb26f); color: #fff; }
.sh-steps li.done + li::before, .sh-steps li.on + li::before { background: var(--border, #2a2a32); }

.sh-up .sh-up-body { padding: 18px 20px; overflow-y: auto; flex: 1; min-height: 0; overscroll-behavior: contain; }
.sh-up-foot { display: flex; align-items: center; gap: 12px; padding: 14px 20px calc(14px + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--border, #2a2a32); background: var(--surface, #16161a); }
.sh-up-foot .sh-up-progress { flex: 1; }
.sh-up-foot .sh-publish { flex: 0 0 auto; width: auto; min-width: 150px; margin-left: auto; padding: 12px 24px; border-radius: 999px; font-weight: 800; }

/* drop zone */
.sh-dz {
	display: flex; flex-direction: column; align-items: center; gap: 8px;
	padding: 38px 20px 30px;
	border: 2px dashed var(--border, #2a2a32);
	border-radius: 16px;
	background: var(--surf2, #26262c);
	background: color-mix(in srgb, var(--surf2, #26262c) 55%, transparent);
	cursor: pointer; text-align: center;
	transition: border-color .15s, background .15s, transform .15s;
}
.sh-dz:hover, .sh-dz:focus-visible, .sh-dz.drag { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.sh-dz.drag { transform: scale(1.01); }
.sh-dz:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sh-dz-ic { display: flex; align-items: center; justify-content: center; width: 64px; height: 64px; border-radius: 50%; background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); margin-bottom: 4px; }
.sh-dz b { font-size: 17px; font-weight: 800; }
.sh-dz-or { font-size: 12.5px; color: var(--muted, #8a8a96); }
.sh-dz-btn { display: inline-block; padding: 10px 24px; border-radius: 999px; background: var(--accent); color: #fff; font-size: 14px; font-weight: 800; }
.sh-dz-facts-unused { list-style: none; display: flex; flex-wrap: wrap; justify-content: center; align-self: stretch; gap: 6px; margin: 10px 0 0; padding: 0; width: 100%; }
.sh-dz-facts-unused li { padding: 4px 11px; border-radius: 999px; background: var(--surf2, #26262c); color: var(--muted, #8a8a96); font-size: 11.5px; font-weight: 700; }

/* converting */
.sh-prep { --pct: 0%; display: block; padding: 22px 18px; border-radius: 16px; border: 1px solid var(--border, #2a2a32); background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 55%, transparent) var(--pct), var(--surf2, #26262c) var(--pct)); transition: background .25s ease; }
.sh-prep-line { font-size: 15px; }
.sh-prep-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 9px; }
.sh-prep-line { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 14px; }
.sh-prep-line span { font-weight: 800; font-variant-numeric: tabular-nums; }
.sh-prep small { font-size: 12px; color: var(--muted, #8a8a96); }
.sh-bar { height: 8px; border-radius: 999px; background: var(--surf3, #32323a); overflow: hidden; }
.sh-bar i { display: block; height: 100%; width: 0; border-radius: inherit; background: var(--accent); transition: width .25s ease; }
.sh-bar.sm { height: 5px; }

/* details: preview left, fields right */
.sh-up-grid { display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 22px; align-items: start; }
.sh-up-side { display: flex; flex-direction: column; gap: 12px; }
.sh-phone { position: relative; display: block; width: 100%; aspect-ratio: 9 / 16; padding: 0; border: 0; border-radius: 16px; overflow: hidden; background: #000; cursor: pointer; box-shadow: 0 6px 24px rgba(0, 0, 0, .35); }
.sh-phone img, .sh-phone video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.sh-phone video { opacity: 0; transition: opacity .15s; }
.sh-phone.playing video { opacity: 1; }
.sh-phone-play { position: absolute; left: 50%; top: 50%; width: 46px; height: 46px; margin: -23px 0 0 -23px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, .55); color: #fff; backdrop-filter: blur(4px); transition: opacity .15s; }
.sh-phone.playing .sh-phone-play { opacity: 0; }
.sh-phone-dur { position: absolute; right: 8px; bottom: 8px; padding: 2px 7px; border-radius: 6px; background: rgba(0, 0, 0, .65); color: #fff; font-size: 11.5px; font-weight: 800; }
.sh-file { min-width: 0; font-size: 12.5px; }
.sh-file-name { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sh-file-info { color: var(--muted, #8a8a96); margin: 2px 0 6px; }
.sh-link-btn { background: none; border: 0; padding: 4px 0; color: var(--accent); font-family: var(--sans); font-size: 13px; font-weight: 700; cursor: pointer; text-align: left; }
.sh-link-btn:hover { text-decoration: underline; }

.sh-up-fields { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
.sh-field { display: flex; flex-direction: column; gap: 7px; }
.sh-field > label { display: flex; align-items: baseline; justify-content: space-between; font-size: 11.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--muted, #8a8a96); }
.sh-field > label small { font-weight: 700; letter-spacing: 0; text-transform: none; }
.sh-field input[type="text"], .sh-field > input:not([type]), .sh-field > input#shTitleInput { width: 100%; padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border, #2a2a32); background: var(--surf2, #26262c); color: inherit; font-family: var(--sans); font-size: 15px; box-sizing: border-box; }

/* captions card */
.sh-cc-card { padding: 14px; border-radius: 14px; border: 1px solid var(--border, #2a2a32); gap: 10px; }
.sh-cc-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.sh-cc-title { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; }
.sh-cc-title svg { color: var(--accent); }
.sh-switch { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 12.5px; font-weight: 700; user-select: none; }
.sh-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.sh-switch i { position: relative; width: 36px; height: 21px; border-radius: 999px; background: var(--surf3, #32323a); transition: background .15s; flex-shrink: 0; }
.sh-switch i::after { content: ''; position: absolute; left: 2px; top: 2px; width: 17px; height: 17px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, .35); transition: transform .15s; }
.sh-switch input:checked + i { background: var(--accent); }
.sh-switch input:checked + i::after { transform: translateX(15px); }
.sh-switch input:focus-visible + i { outline: 2px solid var(--accent); outline-offset: 2px; }
.sh-cc-auto { display: flex; align-items: center; gap: 10px; font-size: 12.5px; }
.sh-cc-auto label { color: var(--muted, #8a8a96); font-weight: 700; white-space: nowrap; }
.sh-cc-auto select { flex: 1; min-width: 0; padding: 8px 10px; border-radius: 10px; border: 1px solid var(--border, #2a2a32); background: var(--surf2, #26262c); color: inherit; font-family: var(--sans); font-size: 13.5px; }
.sh-cc-status { display: flex; flex-direction: column; gap: 7px; padding: 10px 12px; border-radius: 10px; background: var(--surf2, #26262c); font-size: 12.5px; line-height: 1.4; }
.sh-cc-status-line { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.sh-cc-status[data-state="done"] { background: color-mix(in srgb, var(--ok, #2fb26f) 14%, transparent); }
.sh-cc-status[data-state="failed"] { background: color-mix(in srgb, var(--err, #ff6b81) 14%, transparent); }
.sh-cc-chip.auto { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.sh-cc-note { margin: 2px 0 0; font-size: 11.5px; line-height: 1.45; color: var(--muted, #8a8a96); }
.sh-cc-alt { display: inline-block; margin: 2px 2px 0 0; padding: 2px 10px; border: 1px solid var(--accent, #7c6cf0); border-radius: 999px; background: transparent; color: var(--accent, #7c6cf0); font: inherit; font-weight: 600; cursor: pointer; }
.sh-cc-alt:hover, .sh-cc-alt:focus-visible { background: var(--accent, #7c6cf0); color: #fff; outline: none; }
.sh-cc-card .sh-cc-list { margin-bottom: 0; }

/* phones: full-width bottom sheet, single column */
@media (max-width: 640px) {
	.sh-modal-bg:has(.sh-up) { align-items: flex-end; padding: 0; }
	.sh-modal.sh-up { width: 100%; max-height: 94vh; max-height: 94dvh; border-radius: 20px 20px 0 0; border-bottom: 0; }
	.sh-up-grid { grid-template-columns: 1fr; gap: 16px; }
	.sh-up-side { flex-direction: row; align-items: center; }
	.sh-phone { width: 84px; flex-shrink: 0; border-radius: 12px; }
	.sh-phone-play { transform: scale(.7); }
	.sh-up-foot .sh-publish { flex: 1; }
	.sh-steps li span { display: none; }
	.sh-steps li.on span { display: inline; }
	.sh-dz { padding: 28px 16px 22px; }
}
@media (prefers-reduced-motion: reduce) { .sh-dz, .sh-bar i, .sh-switch i, .sh-switch i::after, .sh-phone video { transition: none; } }

.sh-sheet-bg {
	position: fixed;
	inset: 0;
	z-index: 1300;
	background: rgba(13, 13, 15, .6);
	display: flex;
	align-items: flex-end;
	justify-content: center;
	animation: fadeIn .12s ease;
}

.sh-sheet {
	width: min(440px, 100%);
	background: var(--surface, #16161a);
	border: 1px solid var(--border, #2a2a32);
	border-bottom: 0;
	border-radius: 16px 16px 0 0;
	padding: 8px 8px calc(env(safe-area-inset-bottom, 0px) + 10px);
	animation: up .18s ease;
}

@media (min-width: 700px) {
	.sh-sheet-bg { align-items: center; }
	.sh-sheet { border-radius: 16px; border-bottom: 1px solid var(--border, #2a2a32); padding-bottom: 10px; }
}

.sh-sheet-item {
	display: flex;
	align-items: center;
	gap: 12px;
	width: 100%;
	background: none;
	border: 0;
	border-radius: 10px;
	padding: 13px 12px;
	color: var(--text, #f2f2f5);
	font-family: var(--sans);
	font-size: 15px;
	font-weight: 600;
	text-align: left;
	cursor: pointer;
}

.sh-sheet-item:hover { background: var(--surf2, #26262c); }
.sh-sheet-item.disabled, .sh-sheet-item:disabled { opacity: .55; cursor: default; pointer-events: none; }
.sh-sheet-item.disabled:hover { background: none; }
.sh-sheet-item.danger { color: var(--err, #ff6b81); }
.sh-sheet-item-main { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 0; }
.sh-sheet-item-main small { font-size: 12px; font-weight: 500; color: var(--muted, #8a8a96); }
.sh-sheet-item-right { margin-left: auto; display: flex; align-items: center; color: var(--accent); flex-shrink: 0; }
.sh-toggle { position: relative; width: 36px; height: 21px; border-radius: 999px; background: var(--surf3, #38383f); border: 1px solid var(--border, #2a2a32); flex-shrink: 0; box-sizing: border-box; transition: background .15s ease, border-color .15s ease; }
.sh-toggle-thumb { position: absolute; top: 1px; left: 1px; width: 17px; height: 17px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.35); transition: transform .15s ease; }
.sh-toggle.on { background: var(--accent); border-color: var(--accent); }
.sh-toggle.on .sh-toggle-thumb { transform: translateX(15px); }
.sh-sheet-val { font-size: 13px; font-weight: 600; color: var(--muted, #8a8a96); }
.sh-sheet-cancel { justify-content: center; color: var(--muted, #8a8a96); margin-top: 2px; border-top: 1px solid var(--border, #2a2a32); border-radius: 0 0 10px 10px; }
.sh-sheet-head { display: flex; align-items: center; gap: 10px; padding: 10px 12px 6px; font-size: 15px; font-weight: 800; color: var(--text, #f2f2f5); }
.sh-sheet-back { display: flex; align-items: center; justify-content: center; background: none; border: 0; padding: 4px; margin: -4px; color: var(--text, #f2f2f5); cursor: pointer; border-radius: 999px; }
.sh-sheet-back:hover { background: var(--surf2, #26262c); }
.sh-reenc-body { padding: 6px 12px 12px; display: flex; flex-direction: column; gap: 10px; }
.sh-reenc-line { font-size: 14px; font-weight: 600; line-height: 1.35; word-break: break-word; }
.sh-reenc-bar { height: 6px; border-radius: 999px; background: var(--surf2, #26262c); overflow: hidden; }
.sh-reenc-bar > div { height: 100%; background: var(--accent, #4f8cff); border-radius: inherit; transition: width .4s ease; }
.sh-reenc-sub { font-size: 12px; line-height: 1.4; color: var(--muted, #8a8a96); }
.sh-sheet-note { padding: 6px 12px 10px; font-size: 12px; line-height: 1.4; color: var(--muted, #8a8a96); }

/* ============================================================
   BLUESKY VIDEO PLAYER (assets/js/bsky-video-player.js)
   Styles ported from Bluesky's web player (web-controls/*.tsx,
   TimeIndicator.tsx, src/style.css) -- MIT, (c) Bluesky Social PBC.
   Everything is namespaced .bvp-*.
   ============================================================ */
.bvp {
	position: relative;
	width: 100%;
	height: 100%;
	overflow: hidden;
	background: #000;
	color: #fff;
	user-select: none;
	-webkit-user-select: none;
}

.bvp-fig { position: absolute; inset: 0; margin: 0; }

.bvp-video {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: contain;
	background: #000;
}

/* the control layer: an empty-space button, the bar at the bottom, spinner/error in the middle */
.bvp-ui {
	position: absolute;
	inset: 0;
	overflow: hidden;
	display: flex;
	flex-direction: column;
}

.bvp-empty {
	flex: 1;
	display: block;
	width: 100%;
	padding: 0;
	margin: 0;
	border: 0;
	background: transparent;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
	outline: none;
}

/* TimeIndicator.tsx: seconds remaining, while the controls are hidden */
.bvp-timeind {
	position: absolute;
	left: 6px;
	bottom: 6px;
	min-height: 21px;
	padding: 3px 6px;
	border-radius: 6px;
	background: rgba(0, 0, 0, .5);
	font-size: 12px;
	font-weight: 600;
	line-height: 1.25;
	font-variant-numeric: tabular-nums;
	pointer-events: none;
}

.bvp-bar {
	flex-shrink: 0;
	width: 100%;
	padding: 0 4px;
	box-sizing: border-box;
	background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, .4), rgba(0, 0, 0, .7));
	opacity: 0;
	transition: opacity .2s ease-in-out;
	pointer-events: none;
}

.bvp-bar.show { opacity: 1; pointer-events: auto; }

/* ----- scrubber ----- */
.bvp-scrub {
	height: 18px;
	width: 100%;
	padding: 0 4px;
	box-sizing: border-box;
	flex-shrink: 0;
	display: flex;
}

.bvp-touch .bvp-scrub { height: 32px; }

.bvp-scrub-hit {
	flex: 1;
	position: relative;
	display: flex;
	align-items: center;
	padding: 4px 0;
	cursor: pointer;
	touch-action: none;
}

.bvp-scrub.active .bvp-scrub-hit { cursor: pointer; }

.bvp-track {
	width: 100%;
	height: 3px;
	border-radius: 999px;
	overflow: hidden;
	background: #fff;
	transition: height .1s ease;
	pointer-events: none;
}

.bvp-scrub.hover .bvp-track,
.bvp-scrub.active .bvp-track { height: 6px; }

.bvp-fill { height: 100%; width: 0; background: var(--accent); }

.bvp-knob {
	position: absolute;
	left: -8px;
	top: 50%;
	margin-top: -8px;
	width: 16px;
	height: 16px;
	border-radius: 8px;
	pointer-events: none;
	outline: none;
}

.bvp-knob i {
	display: block;
	width: 100%;
	height: 100%;
	border-radius: 50%;
	background: var(--accent);
	transform: scale(0);
	transition: transform .1s ease;
}

.bvp-scrub.hover .bvp-knob i,
.bvp-scrub.focus .bvp-knob i { transform: scale(.6); }
.bvp-scrub.active .bvp-knob i { transform: scale(1); }

/* ----- hover/drag preview: small time (+ thumbnail, when a poster is set) above the track ----- */
.bvp-scrub-preview {
	position: absolute;
	bottom: 100%;
	transform: translateX(-50%);
	margin-bottom: 10px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	pointer-events: none;
}

.bvp-scrub-preview-thumb {
	width: 64px;
	height: 84px;
	border-radius: 6px;
	overflow: hidden;
	border: 1.5px solid rgba(255, 255, 255, .85);
	background: #000;
	box-shadow: 0 2px 10px rgba(0, 0, 0, .5);
}

/* The canvas is already drawn pre-cropped to this exact box (see
   _drawScrubPreviewFrame() in bsky-video-player.js), so it just needs to
   fill it -- no object-fit needed the way the old plain <img> used one. */
.bvp-scrub-preview-thumb canvas { width: 100%; height: 100%; display: block; }

.bvp-scrub-preview-time {
	padding: 2px 7px;
	border-radius: 5px;
	background: rgba(0, 0, 0, .75);
	color: #fff;
	font-size: 11.5px;
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

/* ----- button row ----- */
.bvp-row {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 0 4px 8px;
}

.bvp-flex { flex: 1; }

.bvp-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 8px;
	border: 0;
	border-radius: 999px;
	background: transparent;
	cursor: pointer;
	transition: background-color .1s;
	-webkit-tap-highlight-color: transparent;
}

.bvp-btn:hover,
.bvp-btn:focus-visible { background: rgba(255, 255, 255, .2); outline: none; }

.bvp-clock {
	padding: 0 4px;
	font-size: 13px;
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	color: #fff;
}

/* ----- volume (VolumeControl.tsx + src/style.css vertical range) ----- */
.bvp-vol { position: relative; }

.bvp-vol-pop {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 100%;
	height: 100px;
	padding: 0 0 4px;
	box-sizing: border-box;
}

.bvp-vol-pop::before {
	content: '';
	position: absolute;
	inset: 0 0 4px 0;
	border-radius: 4px;
	background: rgba(0, 0, 0, .6);
}

.bvp-vol-pop input {
	position: relative;
	display: block;
	margin: 6px auto;
	height: calc(100% - 16px);
}

.bvp-vol input[type='range'][orient='vertical'] {
	writing-mode: vertical-lr;
	direction: rtl;
	width: 16px;
	vertical-align: bottom;
	-webkit-appearance: none;
	appearance: none;
	background: transparent;
	cursor: pointer;
}

.bvp-vol input[type='range'][orient='vertical']::-webkit-slider-runnable-track {
	background: white;
	height: 100%;
	width: 4px;
	border-radius: 4px;
}

.bvp-vol input[type='range'][orient='vertical']::-moz-range-track {
	background: white;
	height: 100%;
	width: 4px;
	border-radius: 4px;
}

.bvp-vol input[type='range']::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	border-radius: 50%;
	background-color: white;
	height: 16px;
	width: 16px;
	margin-left: -6px;
}

.bvp-vol input[type='range'][orient='vertical']::-moz-range-thumb {
	border: none;
	border-radius: 50%;
	background-color: white;
	height: 16px;
	width: 16px;
	margin-left: -6px;
}

/* ----- spinner / error ----- */
.bvp-center {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	pointer-events: none;
}

.bvp-center.bvp-center-error { pointer-events: auto; background: rgba(0, 0, 0, .55); }

.bvp-spinner {
	width: 34px;
	height: 34px;
	border-radius: 50%;
	border: 3px solid rgba(255, 255, 255, .3);
	border-top-color: #fff;
	animation: bvpSpin .8s linear infinite;
}

@keyframes bvpSpin { to { transform: rotate(360deg); } }

.bvp-error { display: flex; flex-direction: column; align-items: center; gap: 12px; font-size: 14px; }

.bvp-retry {
	border: 0;
	border-radius: 999px;
	padding: 8px 18px;
	background: rgba(255, 255, 255, .92);
	color: #111;
	font-family: var(--sans);
	font-size: 13.5px;
	font-weight: 800;
	cursor: pointer;
}

.bvp-fullscreen .bvp-video { object-fit: contain; }

/* The dark backdrop behind the rail/info/controls (.sh-scrim) sits above the raw
   video pixels but must stay behind the actual controls layer, or the pause/
   seek/volume row becomes invisible and unclickable underneath it.
   .bvp-ui stays BELOW .sh-rail/.sh-info's own z-index (6) by default -- .bvp-ui
   contains .bvp-empty, the full-bleed invisible tap-to-play/pause catcher, and
   raising the whole layer above the rail meant that catcher, not the Like/
   Comment/Share/More buttons visually sitting on top of it, was actually
   receiving the click on that whole region: the buttons looked pressable but
   silently did nothing. It only needs to rise above the rail for one thing --
   the scrub hover/drag preview (.bvp-scrub-preview), which paints a preview
   thumbnail that can land over the rail while dragging near the right/bottom
   of the track and needs to be on top of it there -- so the raise is scoped to
   just while a drag is in progress (.bvp-scrub.active, toggled in
   bsky-video-player.js's scrubber pointerdown/pointerup) rather than applying
   to the whole layer all the time. */
.sh-item .bvp-ui { z-index: 5; }
.sh-item .bvp-ui:has(.bvp-scrub.active) { z-index: 7; }
/* The time-remaining/duration badge (e.g. "0:03") is dropped entirely in
   Shorts -- the always-on progress line at the bottom already shows how far
   into the short you are, so this extra badge is just clutter on top of it. */
.sh-item .bvp-timeind { display: none !important; }
/* The player defaults to object-fit:contain (letterboxed), which left visible
   black bars around the video whenever its aspect ratio didn't exactly match
   the Shorts stage. Shorts should fill the whole box edge-to-edge like any
   reels-style feed, so it's cover here -- cropping slightly rather than
   showing black bars. True fullscreen (the expand button) is the one case
   that still shows the whole, uncropped frame, so it stays "contain". */
.sh-item .bvp-video { object-fit: cover; }
.sh-item .bvp.bvp-fullscreen .bvp-video { object-fit: contain; }
/* The player's own controls (pause/timeline/duration/volume/fullscreen) fade in
   on hover/tap and sit at the very bottom of the short. This used to be a
   margin-bottom, which left a dead, uncovered gap between the bar's own black
   background and the true bottom edge of the short (nothing rendered there,
   so whatever sat behind -- app chrome, the next short's edge -- showed
   through). Using padding-bottom instead keeps the same breathing room above
   the true edge while letting the bar's own dark gradient background extend
   all the way down to it, flush with no gap. The total space reserved is
   unchanged, so the rail and the publisher/name block above (.sh-rail /
   .sh-info), which reserve exactly --sh-ctrl-h + --sh-gap so nothing
   overlaps, don't need to change. */
.sh-item .bvp-bar { padding-bottom: calc(var(--sh-gap) + 2px); }

/* ---- Details/rail no longer shift when the control bar fades in ----
   Previously .sh-rail / .sh-info sat close to the bottom and only lifted
   clear of the control bar's height while it was actually shown, animating
   between the two on every hover/tap (see the base .sh-rail / .sh-info
   rules and the now-removed .sh-ctrl-show variant above). Pinning them at
   the lifted position permanently removes that shift -- they always leave
   the same --sh-ctrl-h + --sh-gap of room, so the control bar (which fades
   in/out in that same reserved space via .bvp-bar's own opacity, not by
   moving) simply appears below them, instead of them jumping to make room
   for it. */
.sh-rail,
.sh-info {
	bottom: calc(var(--sh-ctrl-h) + var(--sh-gap));
	transition: none;
}
/* Fullscreen moved off the control bar into the "more options" (⋮) sheet --
   see shFullscreen() / the "Fullscreen" item in shMore(), shorts.js. Hidden
   here rather than removed from the markup in bsky-video-player.js, since
   that player is shared with other embeds elsewhere on the site where the
   button should stay put. */
.sh-item .bvp-fs { display: none; }

/* ---- Volume replaced by the single top-left mute icon (#shTopMute, wired
   up in shorts.js/view-shorts.js) -- audio is shared across every short
   (see BskyVideo.muted), so one icon next to Search covers all of them,
   TikTok/YouTube-Shorts style, instead of a per-video control. ---- */
.sh-item .bvp-vol { display: none; }

/* ---- Play/pause moved to a big centered icon, TikTok style ----
   Taken out of .bvp-row's flow (which is now otherwise empty -- see the
   clock/row rules below) and centered over the whole video via its nearest
   positioned ancestor, .bvp-ui. Still fades in/out exactly when the old bar
   did (paused, hovered, or keyboard-focused) via .sh-ctrl-show, the same
   class _renderControls() already toggles on .sh-item for that -- just no
   longer tied to .bvp-bar's own opacity, since the bar itself is now always
   visible (see below). */
.sh-item .bvp-playpause {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	padding: 20px;
	background: rgba(0, 0, 0, .35);
	opacity: 0;
	pointer-events: none;
	transition: opacity .15s ease;
}
.sh-item.sh-ctrl-show .bvp-playpause { opacity: 1; pointer-events: auto; }
.sh-item .bvp-playpause svg { width: 46px; height: 46px; }

/* ---- Duration line pinned to the true bottom, fading in only on hover or
   while paused -- same as YouTube's own Shorts progress bar actually
   behaves (it's hidden the rest of the time, not always on -- a previous
   version of this file forced it always-on here, which doesn't match).
   .bvp-bar's own base rule (opacity:0, shown via .show) already does
   exactly this, driven by _showControls()/_renderControls() in
   bsky-video-player.js (paused-and-focused, or hovered) -- the same state
   that already toggles .sh-item.sh-ctrl-show for the rail spacing below --
   so there's nothing Shorts-specific to override here, just not fighting
   the default. The row behind the line no longer has any visible content
   (playpause moved out above, volume/fullscreen hidden, and the persistent
   clock text is dropped in favor of the hover/drag time preview -- see
   .bvp-scrub-preview in the shared player styles above), so it's zeroed
   out rather than removed outright, since the playpause button above is
   still one of its DOM children. */
.sh-item .bvp-row { padding: 0; min-height: 0; }
.sh-item .bvp-clock { display: none; }
/* Rail/publisher block only need to clear the thin progress line now (not
   a full button row), so they can sit much closer to it than the default
   --sh-ctrl-h (which still applies as-is to non-Shorts embeds using this
   same player). */
.sh-item { --sh-ctrl-h: 40px; }

/* ============ MY SHORTS row (edit-profile modal) + shorts grid (view-profile modal) ============
   see profile.js openUserProfile()/_renderUserProfileBody()/_loadProfileShorts(),
   shell.js #profileModal / #userProfileModal. The grid reuses .sh-card / .sh-results
   from Shorts' own search results (assets/js/shorts.js) so the cards match exactly. */
.profile-my-shorts-row {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 12px 14px;
	margin-top: 2px;
	color: var(--text);
	font-family: var(--sans);
	font-size: 14.5px;
	font-weight: 700;
	cursor: pointer;
	text-align: left;
}

.profile-my-shorts-row:hover { background: var(--surface); border-color: var(--accent); }
.profile-my-shorts-row span:first-of-type { flex: 1; }
.profile-my-shorts-chevron { color: var(--muted); flex-shrink: 0; }

/* Give the view-profile modal room to scroll once a shorts grid is added below
   the usual avatar/name/friend-action content -- it previously had no cap. */
.user-profile-modal-box {
	max-height: 88vh;
	overflow-y: auto;
}

.user-profile-shorts {
	width: 100%;
	margin-top: 14px;
	padding-top: 14px;
	border-top: 1px solid var(--border);
}

.user-profile-shorts-head {
	display: flex;
	align-items: center;
	gap: 7px;
	margin-bottom: 10px;
	font-size: 13px;
	font-weight: 800;
	color: var(--muted);
	text-transform: uppercase;
	letter-spacing: .02em;
}

.user-profile-shorts-grid.sh-results {
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 8px;
}

.user-profile-shorts-grid .sh-card-dur { font-size: 10px; padding: 1px 5px; }
.user-profile-shorts-grid .sh-card-thumb { aspect-ratio: 4 / 5; }
.user-profile-shorts-grid .sh-card-meta { font-size: 11px; }

.user-profile-shorts-loading {
	grid-column: 1 / -1;
	display: flex;
	justify-content: center;
	padding: 18px 0;
}

.user-profile-shorts-empty-text {
	grid-column: 1 / -1;
	text-align: center;
	color: var(--muted);
	font-size: 13px;
	padding: 14px 0;
}

.user-profile-shorts-empty {
	grid-column: 1 / -1;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 8px;
	padding: 22px 10px;
	border: 1.5px dashed var(--border);
	border-radius: 12px;
	background: none;
	color: var(--accent-soft);
	font-family: var(--sans);
	font-size: 13.5px;
	font-weight: 700;
	cursor: pointer;
}

.user-profile-shorts-empty:hover { border-color: var(--accent); background: var(--surf2); }

/* ===== Header search box (YouTube-style, top-center) ===== */
.header-search-wrap {
	position: relative;
	flex: 1 1 260px;
	max-width: 560px;
	min-width: 0;
	margin: 0 auto;
	display: flex;
	align-items: center;
}
.header-search-field {
	flex: 1;
	min-width: 0;
	display: flex;
	align-items: stretch;
	height: 40px;
	border: 1px solid var(--border);
	border-radius: 999px;
	overflow: hidden;
	background: var(--bg);
	transition: border-color .15s;
}
.header-search-field:focus-within { border-color: var(--accent); }
/* Doubled selector so the global input/input:focus rules (border, box-shadow,
   background) can't draw a second rounded box inside the pill on focus. */
.header-search-field .header-search-input,
.header-search-field .header-search-input:focus {
	flex: 1;
	width: auto;
	min-width: 0;
	background: transparent;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	outline: 0;
	color: var(--text);
	font: inherit;
	font-size: 14px;
	padding: 0 4px 0 0;
}
.header-search-input::placeholder { color: var(--muted); }
.header-search-clear {
	background: none;
	border: 0;
	color: var(--muted);
	font-size: 20px;
	cursor: pointer;
	padding: 0 10px;
}
.header-search-clear:hover { color: var(--text); }
.header-search-submit {
	flex-shrink: 0;
	width: 44px;
	display: flex;
	align-items: center;
	justify-content: center;
	background: transparent;
	border: 0;
	padding: 0 0 0 4px;
	color: var(--muted);
	cursor: pointer;
}
.header-search-submit:hover { color: var(--text); }
.header-search-toggle, .header-search-back {
	display: none;
	width: 40px;
	height: 40px;
	flex-shrink: 0;
	align-items: center;
	justify-content: center;
	background: none;
	border: 0;
	border-radius: 50%;
	color: var(--text);
	cursor: pointer;
}
.header-search-toggle:hover, .header-search-back:hover { background: var(--surf2); }
.header-search-results {
	position: absolute;
	top: calc(100% + 8px);
	left: 0;
	right: 0;
	max-height: 60vh;
	overflow-y: auto;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 12px;
	box-shadow: 0 12px 32px rgba(0,0,0,.45);
	padding: 6px;
	z-index: 200;
}
.hs-label { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); padding: 8px 8px 4px; }
.hs-row { display: flex; align-items: center; gap: 10px; width: 100%; padding: 7px 8px; background: none; border: 0; border-radius: 8px; color: var(--text); text-align: left; cursor: pointer; font: inherit; }
.hs-row:hover, .hs-row.hs-active { background: var(--surf2); }
.hs-ico { width: 30px; height: 30px; border-radius: 9px; overflow: hidden; flex-shrink: 0; background: var(--surf2); display: flex; align-items: center; justify-content: center; }
.hs-name { font-size: 13px; font-weight: 600; }
.hs-sub { font-size: 11px; color: var(--muted); }
.hs-empty { padding: 14px 10px; font-size: 12px; color: var(--muted); text-align: center; }

/* Narrow screens: collapse to a search icon; tapping it swaps the whole
   header for back-arrow + full-width search bar (like YouTube mobile). */
@media (max-width: 720px) {
	.header-search-toggle { display: flex; margin-left: auto; }
	.header-search-wrap { display: none; }
	header.hs-open .brand,
	header.hs-open .header-r,
	header.hs-open .header-search-toggle { display: none; }
	header.hs-open .header-search-wrap { display: flex; flex: 1 1 100%; max-width: none; gap: 6px; }
	header.hs-open .header-search-back { display: flex; }
}

/* Search history rows */
.hs-row .hs-hist-ico { width: 30px; height: 30px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: var(--muted); }
.hs-row .hs-hist-q { flex: 1; min-width: 0; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hs-hist-x { background: none; border: 0; color: var(--muted); font-size: 18px; line-height: 1; padding: 2px 8px; cursor: pointer; border-radius: 6px; }
.hs-hist-x:hover { color: var(--text); background: var(--border); }
.hs-head { display: flex; align-items: center; justify-content: space-between; padding: 8px 8px 4px; }
.hs-head .hs-label { padding: 0; }
.hs-tags { display: flex; flex-wrap: wrap; gap: 6px; padding: 2px 8px 8px; }
.hs-tag { border: 1px solid var(--border); background: var(--surf2); color: var(--accent-soft); font: 600 12.5px var(--sans); padding: 5px 11px; border-radius: 999px; cursor: pointer; }
.hs-tag:hover, .hs-tag.hs-active { background: var(--border); }
.hs-top-ico { color: var(--accent-soft); }
.hs-top-n { font-size: 11px; color: var(--muted); padding: 0 8px; font-variant-numeric: tabular-nums; }
.hs-clear-all { background: none; border: 0; color: var(--accent-soft); font-size: 12px; cursor: pointer; }

/* ===== Shorts: lower timeline + controls, title not clickable, volume slider ===== */
.sh-item { --sh-ctrl-h: 20px; --sh-gap: 4px; }
/* timeline: thinner, shorter hit area, sitting right on the bottom edge */
.sh-item .bvp-bar { padding-bottom: 2px; }
.sh-item .bvp-scrub { height: 12px; }
.sh-item.bvp-touch .bvp-scrub, .sh-item .bvp-touch .bvp-scrub { height: 22px; }
.sh-item .bvp-scrub-hit { padding: 2px 0; }
.sh-item .bvp-track { height: 2px; }
.sh-item .bvp-scrub.hover .bvp-track,
.sh-item .bvp-scrub.active .bvp-track { height: 4px; }
/* rail + publisher/title block hug the bottom */
.sh-item .sh-info { padding-bottom: 4px; }
.sh-item .sh-rail { padding-bottom: 0; }
/* only the #hashtag is tappable, never the title text itself */
.sh-title { cursor: default; }
.sh-hashtag { cursor: pointer; }

/* volume slider under the speaker button */
.sh-vol-wrap { position: relative; flex-direction: column; align-items: center; }
.sh-vol-pop {
	position: absolute;
	top: 100%;
	left: 50%;
	transform: translateX(-50%);
	padding-top: 8px;
	display: none;
	z-index: 25;
}
.sh-vol-wrap:hover .sh-vol-pop,
.sh-vol-wrap:has(:focus-visible) .sh-vol-pop,
.sh-vol-wrap.open .sh-vol-pop { display: block; }
.sh-vol-track {
	position: relative;
	width: 34px;
	height: 110px;
	border-radius: 999px;
	background: rgba(30, 30, 36, .72);
	backdrop-filter: blur(6px);
	cursor: pointer;
	touch-action: none;
	outline: none;
}
.sh-vol-track::before {
	content: '';
	position: absolute;
	left: 50%;
	top: 10px;
	bottom: 10px;
	width: 4px;
	margin-left: -2px;
	border-radius: 999px;
	background: rgba(255, 255, 255, .35);
}
.sh-vol-fill {
	position: absolute;
	left: 50%;
	bottom: 14px;
	width: 4px;
	margin-left: -2px;
	border-radius: 999px;
	background: #fff;
}
.sh-vol-thumb {
	position: absolute;
	left: 50%;
	margin-left: -6px;
	width: 12px;
	height: 12px;
	border-radius: 50%;
	background: #fff;
	box-shadow: 0 0 0 2px rgba(0, 0, 0, .35);
}

/* ---- Speaker + volume slider merged into ONE rounded rectangle ----
   While the slider is showing, the speaker button and the popover share
   the same fill and are stacked flush (button rounded on top, popover
   rounded on the bottom), so they read as a single pill. */
.sh-vol-wrap:hover .sh-icon-btn,
.sh-vol-wrap:has(:focus-visible) .sh-icon-btn,
.sh-vol-wrap.open .sh-icon-btn {
	background: rgba(30, 30, 36, .78);
	border-radius: 20px 20px 0 0;
}
.sh-vol-pop {
	top: 100%;
	padding-top: 0;
	width: 40px;
	background: rgba(30, 30, 36, .78);
	backdrop-filter: blur(6px);
	border-radius: 0 0 20px 20px;
}
.sh-vol-track {
	width: 40px;
	height: 104px;
	background: transparent;
	backdrop-filter: none;
	border-radius: 0 0 20px 20px;
}
.sh-vol-track::before { top: 4px; bottom: 14px; }

/* ===== No purple focus ring on inputs (global) =====
   Typing in any input/textarea/select or input-pill wrapper no longer draws
   an accent border or glow. Placed last so it wins over every earlier
   :focus / :focus-within rule; each wrapper keeps its normal resting border. */
input:focus,
select:focus,
textarea:focus,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[contenteditable]:focus {
	outline: none !important;
	box-shadow: none !important;
}
input:focus,
select:focus,
textarea:focus {
	border-color: var(--border);
}
.college-combo-trigger:focus,
.college-combo.is-open .college-combo-trigger,
.username-input:focus-within,
.username-input.is-ok:focus-within,
.username-input.is-bad:focus-within,
.gemma-compose-row:focus-within,
.header-search-field:focus-within,
.sh-search:focus-within,
.comment-input:focus,
.pyq-search:focus,
.club-msg-input:focus,
.report-other-input:focus,
.pdf-search-input:focus,
.pdf-page-input:focus,
.gemma-history-search:focus,
.sh-comments-form input:focus {
	box-shadow: none !important;
	outline: none !important;
}
.college-combo-trigger:focus,
.college-combo.is-open .college-combo-trigger,
.username-input:focus-within,
.gemma-compose-row:focus-within,
.header-search-field:focus-within,
.comment-input:focus { border-color: var(--border); }
.username-input.is-ok:focus-within { border-color: var(--ok); }
.username-input.is-bad:focus-within { border-color: var(--err); }
.sh-search:focus-within { border-color: rgba(255, 255, 255, .16); }
.sh-comments-form input:focus { border-color: rgba(255, 255, 255, .14); }
.pdf-search-input:focus { border-color: rgba(255, 255, 255, .14); }
/* Borderless composer fields (reply/post box, chat, search pills): keep border off */
.sp-ta:focus,
.gemma-input:focus,
.sh-search input:focus,
.header-search-field .header-search-input:focus,
.username-input input:focus { border: 0; }

/* ===== Multi-image lightbox: prev/next arrows + top-centre dots ===== */
.sp-lb-nav {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	width: 42px;
	height: 42px;
	border-radius: 50%;
	border: 0;
	background: rgba(255, 255, 255, .16);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	z-index: 2;
	backdrop-filter: blur(6px);
	transition: background .15s;
}
.sp-lb-nav:hover { background: rgba(255, 255, 255, .3); }
.sp-lb-nav:active { transform: translateY(-50%) scale(.94); }
.sp-lb-prev { left: 14px; }
.sp-lb-next { right: 14px; }
.sp-lb-dots {
	position: absolute;
	top: calc(env(safe-area-inset-top, 0px) + 24px);
	left: 50%;
	transform: translateX(-50%);
	display: flex;
	gap: 6px;
	z-index: 2;
	pointer-events: none;
}
.sp-lb-dots span {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: rgba(255, 255, 255, .4);
	transition: background .15s, transform .15s;
}
.sp-lb-dots span.on { background: #fff; transform: scale(1.15); }
.sp-lightbox-close { z-index: 2; }
@media (max-width: 600px) {
	.sp-lb-nav { width: 36px; height: 36px; }
	.sp-lb-prev { left: 8px; }
	.sp-lb-next { right: 8px; }
}

/* ===== Video posts (<= 60 s, 480p) ===== */
.sp-video {
	margin-top: 8px;
	border-radius: 12px;
	overflow: hidden;
	border: 1px solid var(--border);
	background: #000;
	line-height: 0;
}
.sp-video video {
	display: block;
	width: 100%;
	height: auto;
	max-height: 560px;
	object-fit: contain;
	background: #000;
}
.sp-video-portrait { max-width: 340px; }

/* composer: video preview chip */
.sp-vpreview {
	position: relative;
	width: 100%;
	max-width: 260px;
	aspect-ratio: 16 / 9;
	border-radius: 10px;
	overflow: hidden;
	border: 1px solid var(--border);
	background: var(--surf2);
}
.sp-vpreview img,
.sp-vpreview-ph { width: 100%; height: 100%; object-fit: cover; display: block; }
.sp-vpreview.failed img { opacity: .3; }
.sp-vpreview-badge {
	position: absolute;
	left: 6px;
	bottom: 6px;
	padding: 1px 6px;
	border-radius: 6px;
	background: rgba(0, 0, 0, .7);
	color: #fff;
	font-size: 11px;
	font-weight: 700;
	line-height: 1.5;
}
.sp-vpreview-prog {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	gap: 6px;
	padding: 10px;
	background: linear-gradient(0deg, rgba(0, 0, 0, .75), rgba(0, 0, 0, .35) 70%, rgba(0, 0, 0, .15));
}
.sp-vpreview-label { color: #fff; font-size: 12px; font-weight: 600; }
.sp-vpreview-bar { height: 4px; border-radius: 4px; background: rgba(255, 255, 255, .25); overflow: hidden; }
.sp-vpreview-bar i { display: block; height: 100%; background: var(--accent); border-radius: 4px; transition: width .3s ease; }
.sp-vpreview-x {
	position: absolute;
	top: 5px;
	right: 5px;
	width: 22px;
	height: 22px;
	border-radius: 50%;
	border: 0;
	background: rgba(0, 0, 0, .65);
	color: #fff;
	font-size: 15px;
	line-height: 1;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* ===== Link cards (Open Graph preview under a post's link) ===== */
.sp-linkcard {
	margin-top: 8px;
	border: 1px solid var(--border);
	border-radius: 12px;
	overflow: hidden;
	background: var(--surface);
}
.sp-linkcard-loading { min-height: 64px; }
.sp-lc-skel {
	height: 64px;
	background: linear-gradient(90deg, var(--surf2), rgba(255, 255, 255, .04), var(--surf2));
	background-size: 200% 100%;
	animation: sp-lc-shimmer 1.2s linear infinite;
}
@keyframes sp-lc-shimmer { to { background-position: -200% 0; } }
.sp-lc-thumb { display: block; line-height: 0; background: var(--surf2); }
.sp-lc-thumb { aspect-ratio: 1.91 / 1; max-height: 560px; overflow: hidden; }
.sp-lc-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.sp-lc-media {
	position: relative;
	display: block;
	width: 100%;
	aspect-ratio: 16 / 9;
	background: #000;
	line-height: 0;
	cursor: pointer;
	overflow: hidden;
}
.sp-lc-media.portrait { aspect-ratio: 9 / 16; max-height: 520px; width: auto; max-width: 100%; margin: 0 auto; }
.sp-lc-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sp-lc-play {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 54px;
	height: 54px;
	border-radius: 50%;
	background: rgba(0, 0, 0, .6);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	backdrop-filter: blur(4px);
	transition: background .15s, transform .15s;
}
.sp-lc-media:hover .sp-lc-play { background: rgba(0, 0, 0, .8); transform: translate(-50%, -50%) scale(1.06); }
.sp-lc-embed { cursor: default; }
.sp-lc-embed iframe { width: 100%; height: 100%; border: 0; display: block; }
.sp-lc-body {
	display: block;
	padding: 10px 12px 11px;
	color: var(--text);
	text-decoration: none;
	border-top: 1px solid var(--border);
}
.sp-lc-thumb + .sp-lc-body { border-top: 0; }
.sp-lc-body:hover .sp-lc-title { text-decoration: underline; }
.sp-lc-title {
	font-size: 14.5px;
	font-weight: 700;
	line-height: 1.3;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	overflow-wrap: anywhere;
}
.sp-lc-desc {
	margin-top: 2px;
	font-size: 13px;
	color: var(--muted);
	line-height: 1.35;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	overflow-wrap: anywhere;
}
.sp-lc-host {
	margin-top: 7px;
	padding-top: 7px;
	border-top: 1px solid var(--border);
	display: flex;
	align-items: center;
	gap: 5px;
	font-size: 12px;
	color: var(--muted);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
/* Long links inside post text: one line, cut with "…" (label is pre-shortened in JS). */
.sp-text a.club-msg-link { word-break: normal; overflow-wrap: normal; white-space: nowrap; }

/* ===== Composer: 300-char ring limiter + top-right Post button ===== */
.sp-composer { position: relative; }
/* Drag & drop target: dashed accent frame + hint while files are dragged over the post box */
.sp-composer.sp-dragover { outline: 2px dashed var(--accent); outline-offset: -3px; border-radius: 12px; }
.sp-composer.sp-dragover::after {
	content: 'Drop pictures or a video to attach';
	position: absolute; inset: 0; z-index: 5;
	display: flex; align-items: center; justify-content: center;
	background: color-mix(in srgb, var(--accent) 14%, var(--surface));
	color: var(--accent-soft); font: 700 14px var(--sans);
	border-radius: 12px; pointer-events: none;
}
/* Post button sits in the bar, right of the counter; the compose modal moves it into its header. */
/* Icon-only send button (paper plane) */
.sp-submit { display: inline-flex; align-items: center; justify-content: center; }
.sp-composer-bar > .sp-submit, .sp-modal-headslot .sp-submit { padding: 6px 12px; min-width: 44px; }
.sp-composer-bar > .sp-submit { border-radius: 10px; font-weight: 700; flex-shrink: 0; }
.sp-submit .sp-submit-ico { display: block; width: 20px; height: 20px; }
/* Post button: solid accent (the .btn.primary default is the grey surface colour) */
.btn.primary.sp-submit { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn.primary.sp-submit:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); filter: brightness(1.1); }
.btn.primary.sp-submit:disabled { background: var(--accent); border-color: var(--accent); color: #fff; opacity: .45; cursor: default; }

/* In the compose modal the Post button lives in the header, opposite Cancel */
.sp-modal-headslot { min-width: 64px; display: flex; justify-content: flex-end; }
.sp-modal-headslot .sp-submit {
	position: static;
	border-radius: 10px;
	padding: 6px 16px;
	font-weight: 700;
}
.sp-modal-cancel { border-radius: 0; background: none; padding: 4px 2px; }   /* plain text, no box */
.sp-modal-head > span:not(.sp-modal-headslot) { flex: 1; text-align: center; }
.sp-modal-head .sp-modal-cancel { flex: 0 0 auto; min-width: 64px; text-align: left; }

/* Counter = ring + label; spinner while posting / uploading */
.sp-counter {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	min-width: 0;
	text-align: left;
}
.sp-counter-label { font-family: var(--mono); font-size: 12px; color: var(--accent-soft); white-space: nowrap; }
.sp-counter.warn .sp-counter-label { color: var(--warn); }
.sp-counter.over .sp-counter-label { color: var(--err); font-weight: 700; }
.sp-ring { display: block; }
.sp-ring-bg { fill: none; stroke: var(--border); stroke-width: 2.6; }
.sp-ring-fg { fill: none; stroke: var(--accent-soft); stroke-width: 2.6; stroke-linecap: round; transition: stroke-dashoffset .12s linear, stroke .15s; }
.sp-counter.warn .sp-ring-fg { stroke: var(--warn); }
.sp-counter.over .sp-ring-fg { stroke: var(--err); }
.sp-ring-spin {
	width: 24px;
	height: 24px;
	box-sizing: border-box;
	border-radius: 50%;
	border: 2.6px solid var(--border);
	border-top-color: var(--accent-soft);
	animation: sp-ring-rot .8s linear infinite;
}
@keyframes sp-ring-rot { to { transform: rotate(360deg); } }

/* ============================================================
   Home feed: "Suggested for you" people row (Discover) + Sponsored post
   card (all feeds) -- see social.js section 8.
   ============================================================ */
.sp-suggest {
	padding: 12px 0 14px;
	border-bottom: 1px solid var(--border);
	background: rgba(124, 106, 247, .05);
}

.sp-suggest-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	padding: 0 14px 10px;
}

.sp-suggest-title {
	font-size: 15px;
	font-weight: 800;
	color: var(--text);
}

.sp-suggest-more {
	background: none;
	border: 0;
	padding: 2px 0;
	color: var(--accent-soft);
	font-family: var(--sans);
	font-size: 13px;
	font-weight: 700;
	cursor: pointer;
}

.sp-suggest-more:hover { text-decoration: underline; }

.sp-suggest-row {
	display: flex;
	gap: 10px;
	padding: 0 14px 2px;
	overflow-x: auto;
	overflow-y: hidden;
	overscroll-behavior-x: contain;   /* end of the row must not drag the page / trigger browser swipe-back */
	touch-action: pan-x pan-y;        /* no diagonal wobble, no pinch-zoom jitter */
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
	cursor: grab;
}

.sp-suggest-row.dragging { cursor: grabbing; user-select: none; scroll-snap-type: none; }
.sp-suggest-row::-webkit-scrollbar { display: none; }

.sp-suggest-card {
	position: relative;
	flex: 0 0 158px;
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 4px;
	padding: 16px 10px 12px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 14px;
}

.sp-suggest-x {
	position: absolute;
	top: 6px;
	right: 6px;
	width: 22px;
	height: 22px;
	border: 0;
	border-radius: 50%;
	background: transparent;
	color: var(--muted);
	font-size: 11px;
	line-height: 22px;
	cursor: pointer;
}

.sp-suggest-x:hover { background: var(--border); color: var(--text); }

.sp-suggest-av {
	width: 64px;
	height: 64px;
	border-radius: 50%;
	background: var(--accent);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 22px;
	font-weight: 800;
	overflow: hidden;
	cursor: pointer;
	flex-shrink: 0;
}

.sp-suggest-av img { width: 100%; height: 100%; object-fit: cover; display: block; }

.sp-suggest-name {
	max-width: 100%;
	margin-top: 4px;
	font-size: 14px;
	font-weight: 800;
	color: var(--text);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	cursor: pointer;
}

.sp-suggest-sub {
	width: 100%;
	min-height: 32px;
	font-size: 12px;
	line-height: 1.3;
	color: var(--muted);
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	overflow-wrap: anywhere;
}

.sp-suggest-follow {
	width: 100%;
	margin-top: 6px;
	height: 34px;
	border: 0;
	border-radius: 999px;
	background: var(--accent);
	color: #fff;
	font-family: var(--sans);
	font-size: 13.5px;
	font-weight: 800;
	cursor: pointer;
	transition: background .12s, color .12s;
}

.sp-suggest-follow:hover { filter: brightness(1.1); }

.sp-suggest-follow.on {
	background: transparent;
	color: var(--muted);
	box-shadow: inset 0 0 0 1px var(--border);
}

.sp-suggest-skel { align-items: center; min-height: 190px; gap: 10px; }
.sp-suggest-skel-av { width: 64px; height: 64px; border-radius: 50%; }

/* Sponsored post card (a normal-looking post with a "Sponsored" tag + optional button) */
.sp-sponsored { cursor: default; }
.sp-feed .sp-sponsored:hover { background: transparent; }

.sp-sponsored-tag {
	display: inline-block;
	padding: 1px 8px;
	border-radius: 999px;
	background: rgba(232, 147, 74, .18);
	color: var(--a2, #e8934a);
	font-size: 10.5px;
	font-weight: 800;
	letter-spacing: .06em;
	text-transform: uppercase;
}

.sp-sponsored-title {
	margin: 2px 0 2px;
	font-size: 15px;
	font-weight: 800;
	color: var(--text);
	overflow-wrap: anywhere;
}

.sp-sponsored-cta {
	display: inline-block;
	margin-top: 10px;
	padding: 7px 16px;
	border-radius: 999px;
	background: var(--accent);
	color: #fff;
	font-size: 13px;
	font-weight: 800;
	text-decoration: none;
}

.sp-sponsored-cta:hover { filter: brightness(1.1); }

/* ============================================================
   Shorts feed: "Sponsored" tag + optional link button on a sponsored short
   ============================================================ */
.sh-sponsored {
	display: flex;
	align-items: center;
	gap: 8px;
	margin-bottom: 8px;
}

.sh-sponsored-tag {
	padding: 2px 9px;
	border-radius: 999px;
	background: rgba(232, 147, 74, .92);
	color: #1a1206;
	font-size: 10px;
	font-weight: 800;
	letter-spacing: .08em;
	text-transform: uppercase;
}

.sh-cta {
	padding: 5px 14px;
	border-radius: 999px;
	background: #fff;
	color: #111;
	font-size: 12.5px;
	font-weight: 800;
	text-decoration: none;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	max-width: 60%;
}

/* ============================================================
   Sponsor menu: "Sponsor a Short" picker
   ============================================================ */
.sponsor-short-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
	max-height: 260px;
	overflow-y: auto;
	margin-top: 6px;
}

.sponsor-short-opt {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	padding: 8px;
	text-align: left;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 10px;
	color: var(--text);
	font-family: var(--sans);
	cursor: pointer;
	transition: border-color .12s, background .12s;
}

.sponsor-short-opt:hover { border-color: var(--accent); }
.sponsor-short-opt.on { border-color: var(--accent); background: rgba(124, 106, 247, .14); box-shadow: 0 0 0 1px var(--accent); }

.sponsor-short-thumb {
	flex: 0 0 44px;
	height: 60px;
	border-radius: 6px;
	background-color: var(--border);
	background-size: cover;
	background-position: center;
}

.sponsor-short-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sponsor-short-title { font-size: 13.5px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sponsor-short-sub { font-size: 11.5px; color: var(--muted); }

/* ===== Shorts speaker: waves draw in / out with the volume level ===== */
.sh-spk .sh-wave {
	stroke-dasharray: 1;
	stroke-dashoffset: 1;
	opacity: 0;
	transition: stroke-dashoffset .28s ease, opacity .2s ease;
}
[data-level="low"] .sh-wave1,
[data-level="high"] .sh-wave1,
[data-level="high"] .sh-wave2,
[data-level="off"] .sh-x { stroke-dashoffset: 0; opacity: 1; }
[data-level="high"] .sh-wave2 { transition-delay: .08s; }
[data-level="off"] .sh-x { transition-delay: .06s; }
#shTopMute .sh-spk { transform-origin: 30% 50%; }
#shTopMute.sh-spk-bump .sh-spk { animation: sh-spk-bump .32s ease; }
@keyframes sh-spk-bump { 0% { transform: scale(1); } 40% { transform: scale(1.16); } 100% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) {
	.sh-spk .sh-wave { transition: none; }
	#shTopMute.sh-spk-bump .sh-spk { animation: none; }
}

/* ============================================================
   AUDIO CALLS (call.js / api/call-*.php) — in-call panel, the
   incoming-DM-call overlay, and the "N in call" club header badge.
   Reuses the app's existing overlay conventions (.modal-bg-style dark
   blurred backdrop, --surface cards, --accent/--err/--ok action colors)
   rather than inventing a new visual language for this one feature.
   ============================================================ */

.call-badge-btn {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	border: 1px solid color-mix(in srgb, var(--ok) 40%, var(--border));
	background: color-mix(in srgb, var(--ok) 14%, transparent);
	color: var(--ok);
	border-radius: 999px;
	padding: 5px 10px;
	font-size: 11.5px;
	font-weight: 600;
	white-space: nowrap;
	cursor: default;
}
.call-badge-btn.joinable {
	cursor: pointer;
	color: var(--accent);
	border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
	background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.call-badge-btn.joinable:hover {
	background: color-mix(in srgb, var(--accent) 22%, transparent);
}

.call-overlay,
.call-incoming-overlay {
	position: fixed;
	inset: 0;
	z-index: 200;
	background: rgba(13, 13, 15, .88);
	backdrop-filter: blur(6px);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 20px;
}

.call-panel {
	width: 100%;
	max-width: 420px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 16px;
	padding: 22px 20px;
	display: flex;
	flex-direction: column;
	gap: 18px;
	animation: up .22s ease;
}
.call-panel-header {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
}
.call-panel-title {
	font-weight: 700;
	font-size: 16px;
	color: var(--text);
}
.call-panel-duration {
	font-family: var(--mono);
	color: var(--muted);
	font-size: 13px;
}

.call-participants-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
	gap: 16px;
	max-height: 50vh;
	overflow-y: auto;
	padding: 4px 0;
}
.call-participant {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	text-align: center;
}
.call-participant.pending {
	opacity: .55;
}
.call-participant-name {
	font-size: 12.5px;
	color: var(--text);
	max-width: 84px;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.call-participant-status {
	font-size: 10.5px;
	color: var(--muted);
	min-height: 13px;
}

.call-avatar {
	width: 56px;
	height: 56px;
	border-radius: 50%;
	overflow: hidden;
	background: var(--surf2);
	display: flex;
	align-items: center;
	justify-content: center;
	border: 2px solid transparent;
}
.call-avatar.big {
	width: 84px;
	height: 84px;
}
.call-avatar.speaking-ring {
	border-color: color-mix(in srgb, var(--ok) 55%, transparent);
}
.call-avatar.pulse {
	animation: callPulse 1.6s ease-in-out infinite;
}
.call-avatar-img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}
.call-avatar-fallback {
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 700;
	font-size: 20px;
	color: var(--accent-soft);
	background: color-mix(in srgb, var(--accent) 18%, var(--surf2));
}

@keyframes callPulse {
	0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 45%, transparent); }
	50% { box-shadow: 0 0 0 14px transparent; }
}

.call-controls {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 18px;
}
.call-ctrl-btn {
	width: 52px;
	height: 52px;
	border-radius: 50%;
	border: 1px solid var(--border);
	background: var(--surf2);
	color: var(--text);
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	transition: background .15s ease, transform .1s ease;
}
.call-ctrl-btn:hover {
	transform: translateY(-1px);
	background: color-mix(in srgb, var(--accent) 14%, var(--surf2));
	border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
}
.call-ctrl-btn.active:hover {
	background: color-mix(in srgb, var(--accent) 30%, var(--surf2));
}
.call-ctrl-btn.active {
	background: color-mix(in srgb, var(--accent) 22%, var(--surf2));
	color: var(--accent);
	border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
}
.call-ctrl-btn.end {
	background: var(--err);
	border-color: var(--err);
	color: #fff;
}
.call-ctrl-btn.end:hover {
	background: color-mix(in srgb, var(--err) 85%, black);
}
.call-ctrl-btn.accept {
	background: var(--ok);
	border-color: var(--ok);
	color: #0d0d0f;
}
.call-ctrl-btn.accept:hover {
	background: color-mix(in srgb, var(--ok) 85%, black);
}

.call-incoming-panel {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 10px;
	text-align: center;
}
.call-incoming-name {
	font-size: 18px;
	font-weight: 700;
	color: var(--text);
	margin-top: 6px;
}
.call-incoming-sub {
	font-size: 13px;
	color: var(--muted);
}
.call-incoming-actions {
	display: flex;
	gap: 28px;
	margin-top: 18px;
}

/* ============================================================
   VIDEO CALLS — layered on top of the audio-call panel above.
   Same call-panel/call-controls/call-ctrl-btn shell; a video call just
   swaps the avatar grid for a tile grid of <video> elements (one per
   joined participant, single guy or a whole group) and adds a camera
   toggle next to the mute button.
   ============================================================ */

.call-panel-video {
	max-width: 640px;
}

.call-video-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
	gap: 10px;
	max-height: 56vh;
	overflow-y: auto;
	padding: 2px;
}
.call-video-tile {
	position: relative;
	aspect-ratio: 4 / 3;
	border-radius: 12px;
	overflow: hidden;
	background: var(--surf2);
	border: 1px solid var(--border);
}
.call-video-avatar-img,
.call-video-avatar-fallback {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}
.call-video-avatar-fallback {
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 700;
	font-size: 22px;
	color: var(--accent-soft);
	background: color-mix(in srgb, var(--accent) 18%, var(--surf2));
}
.call-video-el {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	background: #000;
}
.call-video-local {
	transform: scaleX(-1); /* mirror my own camera, like every other video call app */
}
.call-video-name {
	position: absolute;
	left: 8px;
	bottom: 8px;
	z-index: 2;
	padding: 2px 9px;
	border-radius: 999px;
	background: rgba(0, 0, 0, .55);
	color: #fff;
	font-size: 11px;
	font-weight: 600;
	white-space: nowrap;
	max-width: calc(100% - 16px);
	overflow: hidden;
	text-overflow: ellipsis;
}

.call-pending-row {
	margin-top: 2px;
	max-height: none;
	overflow: visible;
}

/* ---- screen share ---- */
.call-panel-sharing {
	max-width: 760px;
}
.call-screen-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 10px;
	margin-bottom: 10px;
}
.call-screen-tile {
	position: relative;
	width: 100%;
	aspect-ratio: 16 / 9;
	border-radius: 12px;
	overflow: hidden;
	background: #000;
	border: 1px solid var(--border);
}
.call-screen-tile .call-video-el {
	object-fit: contain; /* unlike a camera tile, never crop a shared screen */
	background: #000;
}
.call-screen-tile .call-video-name {
	background: rgba(0, 0, 0, .6);
}

/* ============================================================
   LIVE CLASS — a docked two-column layout (video stage + a
   chat/poll/hand-raise sidebar) on top of the same call-panel shell
   everything else in this file styles. Still just a call.
   ============================================================ */

.call-panel-live {
	max-width: 960px;
	width: 92vw;
}
.live-body {
	display: flex;
	gap: 14px;
	align-items: flex-start;
}
.live-stage {
	flex: 1 1 60%;
	min-width: 0;
}
.live-sidebar {
	flex: 0 0 280px;
	display: flex;
	flex-direction: column;
	gap: 10px;
	max-height: 66vh;
	overflow-y: auto;
}
@media (max-width: 760px) {
	.live-body {
		flex-direction: column;
	}
	.live-sidebar {
		flex: 0 0 auto;
		width: 100%;
		max-height: 46vh;
	}
}

.call-video-tile-host {
	outline: 2px solid var(--accent);
	outline-offset: -2px;
}

.live-sidebar-section {
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 12px;
	padding: 10px;
}
.live-sidebar-title {
	display: flex;
	align-items: center;
	justify-content: space-between;
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .03em;
	color: var(--muted);
	margin-bottom: 8px;
}

/* -- chat -- */
.live-chat-section {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	min-height: 160px;
}
.live-chat-messages {
	flex: 1 1 auto;
	overflow-y: auto;
	max-height: 220px;
	display: flex;
	flex-direction: column;
	gap: 4px;
	margin-bottom: 8px;
}
.live-chat-msg {
	font-size: 13px;
	line-height: 1.35;
	word-break: break-word;
}
.live-chat-msg-name {
	font-weight: 700;
	color: var(--accent-soft);
	margin-right: 4px;
}
.live-chat-composer {
	display: flex;
	gap: 6px;
}
.live-chat-composer input {
	flex: 1 1 auto;
	min-width: 0;
	border-radius: 999px;
	border: 1px solid var(--border);
	background: var(--surface);
	color: inherit;
	padding: 7px 12px;
	font-size: 13px;
}
.live-chat-send-btn {
	flex: 0 0 auto;
	border: none;
	border-radius: 999px;
	width: 32px;
	height: 32px;
	background: var(--accent);
	color: #fff;
	cursor: pointer;
	font-size: 14px;
}

/* -- poll -- */
.live-poll-new-btn,
.live-poll-close-btn,
.live-hand-lower-btn {
	border: none;
	background: color-mix(in srgb, var(--accent) 16%, transparent);
	color: var(--accent-soft);
	border-radius: 999px;
	padding: 3px 10px;
	font-size: 11px;
	font-weight: 700;
	cursor: pointer;
	text-transform: none;
	letter-spacing: 0;
}
.live-poll-empty {
	font-size: 13px;
	color: var(--muted);
}
.live-poll-question {
	font-size: 13px;
	font-weight: 700;
	margin-bottom: 8px;
}
.live-poll-closed-tag {
	font-weight: 600;
	color: var(--muted);
	text-transform: uppercase;
	font-size: 10px;
}
.live-poll-options {
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.live-poll-option {
	position: relative;
	border: 1px solid var(--border);
	border-radius: 8px;
	padding: 7px 10px;
	font-size: 13px;
	cursor: pointer;
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: space-between;
	background: var(--surface);
}
.live-poll-option.closed {
	cursor: default;
}
.live-poll-option.mine {
	border-color: var(--accent);
}
.live-poll-option-fill {
	position: absolute;
	inset: 0;
	background: color-mix(in srgb, var(--accent) 14%, transparent);
	z-index: 0;
	transition: width 200ms ease;
}
.live-poll-option-label,
.live-poll-option-pct {
	position: relative;
	z-index: 1;
}
.live-poll-option-pct {
	font-weight: 700;
	color: var(--muted);
	margin-left: 8px;
	flex: 0 0 auto;
}
.live-poll-meta {
	margin-top: 8px;
	font-size: 12px;
	color: var(--muted);
	display: flex;
	align-items: center;
	justify-content: space-between;
}

/* -- hand raise -- */
.live-hand-queue {
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.live-hand-row {
	display: flex;
	align-items: center;
	gap: 8px;
}
.live-hand-avatar {
	flex: 0 0 auto;
	width: 26px;
	height: 26px;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 11px;
	font-weight: 700;
	color: var(--accent-soft);
	background: color-mix(in srgb, var(--accent) 18%, var(--surf2));
}
.live-hand-name {
	flex: 1 1 auto;
	font-size: 13px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.live-hand-raise-btn {
	width: 100%;
	border: 1px solid var(--border);
	border-radius: 10px;
	background: var(--surface);
	color: inherit;
	padding: 9px;
	font-size: 13px;
	font-weight: 600;
	cursor: pointer;
}
.live-hand-raise-btn.active {
	border-color: var(--accent);
	background: color-mix(in srgb, var(--accent) 14%, transparent);
	color: var(--accent-soft);
}

/* -- "Go Live" header button -- */
.live-go-btn {
	color: #fff;
	background: #d0263a;
	border: none;
}
.live-go-btn:hover {
	background: #b31f30;
}

/* ============================================================
   SHARED BOARD — a transparent live-drawing layer over the stage
   (video tiles, or usefully over an active screen share underneath
   it — annotate a shared slide/screen live, same idea).
   ============================================================ */

.call-stage-area {
	position: relative;
}
.call-board-layer {
	position: absolute;
	inset: 0;
	z-index: 5;
	pointer-events: none; /* only the canvas itself (and its toolbar) capture input */
}
.call-board-canvas {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	touch-action: none; /* we handle drag ourselves -- don't let the browser scroll/zoom mid-stroke */
	cursor: crosshair;
	pointer-events: auto;
}
.call-board-toolbar {
	position: absolute;
	top: 8px;
	left: 8px;
	z-index: 6;
	display: flex;
	align-items: center;
	gap: 6px;
	padding: 5px 7px;
	border-radius: 999px;
	background: rgba(18, 18, 18, .72);
	pointer-events: auto;
}
.call-board-swatch {
	width: 18px;
	height: 18px;
	border-radius: 50%;
	border: 2px solid transparent;
	cursor: pointer;
	padding: 0;
}
.call-board-swatch.active {
	border-color: #fff;
}
.call-board-tool-btn {
	border: none;
	background: rgba(255, 255, 255, .1);
	color: #fff;
	border-radius: 50%;
	width: 24px;
	height: 24px;
	font-size: 12px;
	line-height: 1;
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0;
}
.call-board-tool-btn.active {
	background: var(--accent);
	color: #fff;
}
.call-board-tool-btn.disabled {
	opacity: .35;
	cursor: default;
}
.call-board-clear-btn {
	margin-left: 4px;
	border: none;
	background: rgba(255, 255, 255, .14);
	color: #fff;
	border-radius: 999px;
	padding: 3px 10px;
	font-size: 11px;
	font-weight: 700;
	cursor: pointer;
}

/* ============================================================
   PLAIN AUDIO CALL PANEL — the redesigned "Audio Call" layout used
   whenever a call has no video, isn't Live Class, no screen share, and
   no board active (see _renderCallOverlay's dispatch to
   _renderPlainAudioCallPanel). Everything else (video/live/sharing/
   board) keeps using the earlier .call-panel layouts above.
   ============================================================ */

.call-audio-panel {
	max-width: 500px;
	gap: 22px;
}
.call-audio-header {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 12px;
}
.call-audio-title {
	font-weight: 700;
	font-size: 17px;
	color: var(--text);
}
.call-audio-subtitle {
	display: flex;
	align-items: center;
	gap: 5px;
	font-size: 12px;
	color: var(--muted);
	margin-top: 3px;
}
.call-audio-subtitle svg {
	flex: 0 0 auto;
	opacity: .8;
}
.call-audio-meta {
	display: flex;
	align-items: center;
	gap: 7px;
	color: var(--muted);
	flex: 0 0 auto;
}
.call-net-icon {
	display: flex;
	color: var(--muted);
}
.call-ping {
	font-family: var(--mono);
	font-size: 12px;
}
.call-meta-sep {
	opacity: .35;
	font-size: 12px;
}
.call-audio-meta .call-panel-duration {
	color: var(--accent);
	font-weight: 700;
	font-family: var(--mono);
}
.call-min-btn {
	width: 26px;
	height: 26px;
	border-radius: 50%;
	border: none;
	background: var(--surf2);
	color: var(--text);
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	flex: 0 0 auto;
}
.call-min-btn:hover {
	background: color-mix(in srgb, var(--accent) 16%, var(--surf2));
}

/* -- avatars + waveform -- */
.call-audio-avatars.two-party {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 26px;
}
.call-audio-avatars.multi-party {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	justify-content: center;
	gap: 22px;
}
.call-audio-avatar-tile {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 8px;
	min-width: 90px;
}
.call-audio-avatar-ring {
	width: 88px;
	height: 88px;
	border-radius: 50%;
	overflow: hidden;
	background: var(--surf2);
	display: flex;
	align-items: center;
	justify-content: center;
	transition: box-shadow 150ms ease;
}
.call-audio-avatar-tile.speaking .call-audio-avatar-ring {
	box-shadow: 0 0 0 3px var(--accent), 0 0 18px 2px color-mix(in srgb, var(--accent) 55%, transparent);
}
.call-audio-avatar-tile.pending .call-audio-avatar-ring {
	opacity: .55;
}
.call-audio-avatar-img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}
.call-audio-avatar-fallback {
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 28px;
	font-weight: 700;
	color: var(--accent-soft);
	background: color-mix(in srgb, var(--accent) 18%, var(--surf2));
}
.call-audio-name {
	font-size: 14px;
	font-weight: 700;
	color: var(--text);
	max-width: 120px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.call-audio-status {
	display: flex;
	align-items: center;
	gap: 5px;
	font-size: 12px;
	color: var(--muted);
}
.call-audio-status.speaking {
	color: var(--accent);
	font-weight: 700;
}
.call-audio-status-pending {
	font-style: italic;
}

.call-waveform {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 3px;
	height: 40px;
	width: 140px;
	flex: 0 0 auto;
}
.call-waveform-top {
	width: 100%;
	margin-bottom: 6px;
}
.call-wave-bar {
	width: 3px;
	height: 4px;
	border-radius: 2px;
	background: linear-gradient(180deg, var(--accent), color-mix(in srgb, var(--accent) 40%, #7b5cff));
	opacity: .35;
	transition: height 90ms ease, opacity 150ms ease;
}
.call-waveform.active .call-wave-bar {
	opacity: 1;
}

/* -- controls row, each icon with a label beneath it -- */
.call-audio-controls {
	display: flex;
	align-items: flex-start;
	justify-content: center;
	gap: 26px;
}
.call-audio-ctrl-wrap {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
}
.call-ctrl-label {
	font-size: 11px;
	color: var(--muted);
}

.call-more-menu {
	display: none;
	position: absolute;
	bottom: calc(100% + 10px);
	left: 50%;
	transform: translateX(-50%);
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 12px;
	padding: 6px;
	flex-direction: column;
	gap: 2px;
	min-width: 160px;
	box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
	z-index: 30; /* above the 1:1 self-view tile (.call-vgrid-tile.pip is z-index 5) and the shared-board layer (5/6), so the tile never covers the menu */
}
.call-more-menu.open {
	display: flex;
}
.call-more-item {
	border: none;
	background: none;
	color: var(--text);
	text-align: left;
	padding: 8px 10px;
	border-radius: 8px;
	font-size: 13px;
	cursor: pointer;
	white-space: nowrap;
}
.call-more-item:hover {
	background: color-mix(in srgb, var(--accent) 14%, transparent);
}

/* ============================================================
   DYNAMIC SPLIT SCREEN -- everyone connected gets an equal share.
   JS sets --call-cols / --call-rows and data-n (see _callSplitAttrs):
   1 -> full, 2 -> side by side, 3 -> 2+1, 4 -> 2x2, 5-6 -> 3 per row,
   7-9 -> 3x3, 10+ -> 4 per row. A short last row stretches across.
   ============================================================ */
.call-split {
	--call-gap: 10px;
	display: flex;
	flex-wrap: wrap;
	align-content: stretch;
	gap: var(--call-gap);
	width: 100%;
	height: min(62vh, max(260px, calc(var(--call-rows, 1) * 150px)));
}
.call-split > * {
	flex: 1 1 calc((100% - (var(--call-cols, 1) - 1) * var(--call-gap)) / var(--call-cols, 1) - 1px);
	min-width: 0;
	min-height: 0;
}
/* two people on a phone: stack instead of squeezing side by side */
@media (max-width: 560px) {
	.call-split[data-n="2"] { --call-cols: 1; }
}
.call-split-tile {
	position: relative;
	justify-content: center;
	min-width: 0;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 14px;
	overflow: hidden;
	padding: 8px;
}
.call-split-tile .call-audio-avatar-ring {
	width: clamp(44px, 38%, 104px);
	height: auto;
	aspect-ratio: 1;
}
.call-split-tile .call-audio-avatar-fallback { font-size: clamp(18px, 3.2vw, 32px); }
.call-split-tile .call-audio-name { max-width: 100%; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.call-split.dense .call-audio-status { display: none; }
.call-split.dense .call-audio-name { font-size: 12px; }
/* video tiles fill their share instead of keeping 4:3 */
.call-vgrid.call-split .call-vgrid-tile { aspect-ratio: auto; }
.call-pending-line {
	margin-top: 10px;
	text-align: center;
	font-size: 12px;
	color: var(--muted);
}

/* "Normalize audio" -- the only item left in the call's More menu */
.call-norm-item {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 14px;
}
.call-norm-state {
	font-size: 11px;
	font-weight: 700;
	padding: 2px 8px;
	border-radius: 999px;
	background: var(--surface);
	color: var(--muted);
}
.call-norm-item.on .call-norm-state {
	background: var(--accent);
	color: #fff;
}

/* long label ("Shift to Video Call") wraps under its icon */
.call-audio-ctrl-wrap .call-ctrl-label {
	text-align: center;
	max-width: 78px;
	line-height: 1.15;
}
@media (max-width: 700px) {
	.call-audio-body { flex-direction: column; }
	.call-audio-main { width: 100%; }
	.call-audio-roster-col { flex: 0 0 auto; width: 100%; border-left: none; padding-left: 0; border-top: 1px solid var(--border); padding-top: 14px; }
}
/* ============================================================
   MINIMIZED CALL PILL — replaces the full-screen dimmed overlay with a
   small persistent widget so the call stays reachable while the person
   keeps using the rest of the app.
   ============================================================ */

.call-overlay-minimized {
	position: fixed;
	/* sits above the bottom nav instead of covering it */
	inset: auto 14px calc(var(--bottom-nav-h, calc(76px + env(safe-area-inset-bottom))) + 14px) auto;
	background: transparent;
	backdrop-filter: none;
	padding: 0;
	display: block;
	pointer-events: none; /* only the pill itself, not the whole corner, should catch clicks */
	z-index: 200;
}
.call-min-pill {
	pointer-events: auto;
	display: flex;
	align-items: center;
	gap: 10px;
	max-width: min(280px, calc(100vw - 28px));
	padding: 5px 5px 5px 5px;
	background: color-mix(in srgb, var(--surface) 92%, transparent);
	-webkit-backdrop-filter: blur(14px);
	backdrop-filter: blur(14px);
	border: 1px solid color-mix(in srgb, var(--border) 75%, transparent);
	border-radius: 999px;
	box-shadow: 0 14px 34px -8px rgba(0, 0, 0, .6);
	cursor: pointer;
	transition: background .15s ease;
}
.call-min-pill:hover { background: color-mix(in srgb, var(--surf2) 94%, transparent); }
.call-min-avatar {
	flex: 0 0 auto;
	width: 38px;
	height: 38px;
	border-radius: 50%;
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--surf2);
	box-shadow: 0 0 0 2px var(--surface), 0 0 0 3.5px color-mix(in srgb, var(--ok) 70%, transparent);
}
.call-min-avatar-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.call-min-avatar-fallback {
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 15px;
	font-weight: 700;
	color: var(--accent-soft);
	background: color-mix(in srgb, var(--accent) 18%, var(--surf2));
}
.call-min-text {
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 1px;
	padding-left: 2px;
}
.call-min-label {
	font-size: 13px;
	font-weight: 650;
	line-height: 1.2;
	color: var(--text);
	max-width: 150px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.call-min-pill .call-panel-duration {
	font-size: 12px;
	line-height: 1.2;
	font-variant-numeric: tabular-nums;
	color: var(--muted);
	white-space: nowrap;
}
.call-min-pill .call-panel-duration[data-st="Relay unreachable"],
.call-min-pill .call-panel-duration[data-st="Declined"] { color: var(--warn); }
.call-min-end-btn {
	flex: 0 0 auto;
	width: 38px;
	height: 38px;
	margin-left: 4px;
	border-radius: 50%;
	border: none;
	background: var(--err);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	transition: background .15s ease, transform .1s ease;
}
.call-min-end-btn:hover { background: color-mix(in srgb, var(--err) 88%, #000); }
.call-min-end-btn:active { transform: scale(.94); }
.call-min-end-btn:focus-visible { outline: 2px solid var(--accent-soft); outline-offset: 2px; }

/* ============================================================
   AUDIO CALL PANEL v2 — wider two-column layout: an avatar grid on the
   left, a searchable roster (or an empty state) on the right. Extends
   the .call-audio-panel rules above rather than replacing them.
   ============================================================ */

.call-audio-panel {
	max-width: 940px;
}
.call-audio-body {
	display: flex;
	align-items: flex-start;
	gap: 24px;
}
.call-audio-main {
	flex: 1 1 auto;
	min-width: 0;
}
.call-audio-roster-col {
	flex: 0 0 280px;
	border-left: 1px solid var(--border);
	padding-left: 22px;
}
.call-audio-avatars.multi-party {
	display: flex;
	flex-wrap: wrap;
	gap: 26px 22px;
}
.call-audio-avatars.multi-party .call-audio-avatar-tile {
	flex: 0 0 auto;
	width: 88px;
}
.call-audio-status-icon {
	display: flex;
	color: inherit;
}
.call-audio-status.speaking .call-audio-status-icon {
	color: var(--accent);
}

/* -- roster panel -- */
.call-roster-panel {
	display: flex;
	flex-direction: column;
	gap: 14px;
	height: 100%;
}
.call-roster-list {
	display: flex;
	flex-direction: column;
	gap: 4px;
	max-height: 340px;
	overflow-y: auto;
}
.call-roster-row {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 6px 4px;
	border-radius: 10px;
}
.call-roster-row:hover {
	background: var(--surf2);
}
.call-roster-avatar {
	width: 36px;
	height: 36px;
	border-radius: 50%;
	overflow: hidden;
	flex: 0 0 auto;
	background: var(--surf2);
}
.call-roster-avatar-img,
.call-roster-avatar-fallback {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 13px;
	font-weight: 700;
	color: var(--accent-soft);
	background: color-mix(in srgb, var(--accent) 18%, var(--surf2));
}
.call-roster-info {
	min-width: 0;
	flex: 1 1 auto;
}
.call-roster-name {
	font-size: 13px;
	font-weight: 700;
	color: var(--text);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.call-roster-row .call-audio-status {
	margin-top: 1px;
}

.call-roster-empty {
	flex: 1 1 auto;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	text-align: center;
	gap: 8px;
	color: var(--muted);
	padding: 30px 10px;
}
.call-roster-empty svg {
	opacity: .5;
	margin-bottom: 4px;
}
.call-roster-empty-title {
	font-size: 13px;
	font-weight: 700;
	color: var(--text);
}
.call-roster-empty-sub {
	font-size: 12px;
}

/* ============================================================
   ADD MEMBER PANEL — its own small nested dialog (friends / club
   members / by ID), opened from the "More" menu.
   ============================================================ */

.call-addmember-overlay {
	position: fixed;
	inset: 0;
	z-index: 220; /* above .call-overlay's 200, so it layers on top of the call panel */
	background: rgba(13, 13, 15, .6);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 20px;
}
.call-addmember-panel {
	width: 100%;
	max-width: 380px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 16px;
	padding: 18px;
	display: flex;
	flex-direction: column;
	gap: 14px;
	animation: up .2s ease;
}
.call-addmember-header {
	display: flex;
	align-items: center;
	justify-content: space-between;
}
.call-addmember-title {
	font-weight: 700;
	font-size: 15px;
	color: var(--text);
}
.call-addmember-tabs {
	display: flex;
	gap: 6px;
	border-bottom: 1px solid var(--border);
	padding-bottom: 10px;
}
.call-addmember-tab {
	border: none;
	background: var(--surf2);
	color: var(--muted);
	border-radius: 999px;
	padding: 6px 12px;
	font-size: 12px;
	font-weight: 700;
	cursor: pointer;
}
.call-addmember-tab.active {
	background: color-mix(in srgb, var(--accent) 20%, var(--surf2));
	color: var(--accent-soft);
}
.call-addmember-body {
	max-height: 340px;
	overflow-y: auto;
}
.call-addmember-list {
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.call-addmember-row {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 6px 4px;
}
.call-addmember-row .call-roster-name {
	flex: 1 1 auto;
}
.call-addmember-invite-btn {
	flex: 0 0 auto;
	border: none;
	background: color-mix(in srgb, var(--accent) 18%, transparent);
	color: var(--accent-soft);
	border-radius: 999px;
	padding: 5px 14px;
	font-size: 12px;
	font-weight: 700;
	cursor: pointer;
}
.call-addmember-empty {
	padding: 24px 10px;
	text-align: center;
}
.call-addmember-id-form {
	display: flex;
	gap: 8px;
}
.call-addmember-input {
	flex: 1 1 auto;
	min-width: 0;
	border: 1px solid var(--border);
	background: var(--surf2);
	color: var(--text);
	border-radius: 10px;
	padding: 9px 12px;
	font-size: 13px;
}

/* ============================================================
   VIDEO CALL PANEL — same header/body/roster shell as the audio panel
   above, with a presenter view + a video tile grid on the left instead
   of the plain avatar grid.
   ============================================================ */

.call-video-panel {
	max-width: 1080px;
}
.call-video-panel .call-audio-main {
	display: flex;
	flex-direction: column;
	gap: 14px;
}

/* -- roster row: presenting state + mute badge + ⋮ menu -- */
.call-roster-row {
	position: relative;
}
.call-roster-row.presenting {
	background: color-mix(in srgb, var(--accent) 8%, transparent);
	border-radius: 10px;
}
.call-audio-status.presenting {
	color: var(--accent);
	font-weight: 700;
}
.call-audio-status.presenting .call-audio-status-icon {
	color: var(--accent);
}
.call-roster-mute-badge {
	flex: 0 0 auto;
	color: var(--err);
	display: flex;
	align-items: center;
	margin-right: 2px;
}
.call-roster-menu-wrap {
	position: relative;
	flex: 0 0 auto;
}
.call-roster-menu-btn {
	width: 22px;
	height: 22px;
	border: none;
	background: none;
	color: var(--muted);
	cursor: pointer;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 6px;
}
.call-roster-menu-btn:hover {
	background: var(--surf2);
	color: var(--text);
}
.call-roster-row-menu {
	display: none;
	position: absolute;
	top: calc(100% + 4px);
	right: 0;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 6px;
	flex-direction: column;
	gap: 2px;
	min-width: 150px;
	box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
	z-index: 3;
}
.call-roster-row-menu.open {
	display: flex;
}
.call-more-item-danger {
	color: var(--err);
}
.call-more-item-danger:hover {
	background: color-mix(in srgb, var(--err) 14%, transparent);
}

/* -- presenter strip -- */
.call-presenter-badge-wrap {
	position: relative;
	display: inline-block;
}
.call-presenter-badge {
	display: flex;
	align-items: center;
	gap: 7px;
	border: none;
	background: var(--accent);
	color: #fff;
	border-radius: 10px 10px 0 0;
	padding: 7px 14px;
	font-size: 13px;
	font-weight: 700;
	cursor: default;
}
.call-presenter-badge-wrap:has(.call-presenter-menu) .call-presenter-badge {
	cursor: pointer;
}
.call-presenter-menu {
	display: none;
	position: absolute;
	top: 100%;
	left: 0;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 6px;
	flex-direction: column;
	gap: 2px;
	min-width: 160px;
	box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
	z-index: 4;
}
.call-presenter-menu.open {
	display: flex;
}
.call-presenter-preview {
	position: relative;
	border: 2px solid var(--accent);
	border-top-left-radius: 0;
	border-radius: 14px;
	overflow: hidden;
	background: #000;
	aspect-ratio: 16 / 9;
	max-height: 380px;
}
.call-presenter-screen-tile {
	position: absolute;
	inset: 0;
	border-radius: 0;
	border: none;
}
.call-presenter-pip {
	position: absolute;
	top: 12px;
	right: 12px;
	width: 52px;
	height: 52px;
	border-radius: 50%;
	overflow: hidden;
	border: 2px solid #fff;
	box-shadow: 0 4px 12px rgba(0, 0, 0, .4);
	z-index: 2;
}
.call-presenter-pip img,
.call-presenter-pip .call-vgrid-fallback-initials {
	width: 100%;
	height: 100%;
	object-fit: cover;
}
.call-presenter-expand-btn {
	position: absolute;
	top: 12px;
	left: 12px;
	width: 30px;
	height: 30px;
	border-radius: 8px;
	border: none;
	background: rgba(0, 0, 0, .55);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	z-index: 2;
}

/* -- video tile grid -- */
.call-vgrid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
	gap: 12px;
}
.call-vgrid-with-presenter {
	grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
}
.call-vgrid-tile {
	position: relative;
	aspect-ratio: 4 / 3;
	border-radius: 12px;
	overflow: hidden;
	background: var(--surf2);
	border: 1px solid var(--border);
}
.call-vgrid-tile.pinned {
	outline: 2px solid var(--accent);
	outline-offset: -2px;
}
.call-vgrid-tile.speaking {
	outline: 2px solid var(--accent);
	outline-offset: -2px;
	box-shadow: 0 0 14px 1px color-mix(in srgb, var(--accent) 50%, transparent);
}
.call-vgrid-tile.pending {
	opacity: .5;
}
.call-vgrid-fallback,
.call-vgrid-fallback-img,
.call-vgrid-fallback-initials {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}
.call-vgrid-fallback-initials {
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 700;
	font-size: 20px;
	color: var(--accent-soft);
	background: color-mix(in srgb, var(--accent) 18%, var(--surf2));
}
/* the actual <video> element (call-video-el, from earlier rounds) gets
   moved into this tile by _callPlaceVideoEl -- position it the same
   way as the fallback avatar underneath it */
.call-vgrid-tile .call-video-el {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	z-index: 1;
}
.call-vgrid-mute-badge {
	position: absolute;
	top: 6px;
	right: 6px;
	z-index: 2;
	width: 22px;
	height: 22px;
	border-radius: 50%;
	background: rgba(0, 0, 0, .6);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
}
.call-vgrid-label {
	position: absolute;
	left: 8px;
	bottom: 8px;
	z-index: 2;
	display: flex;
	align-items: center;
	gap: 5px;
	padding: 3px 8px;
	border-radius: 999px;
	background: rgba(0, 0, 0, .55);
	color: #fff;
	font-size: 11px;
	font-weight: 600;
	max-width: calc(100% - 16px);
}
.call-vgrid-name {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.call-vgrid-more-tile {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 4px;
	cursor: pointer;
	color: var(--muted);
}
.call-vgrid-more-tile:hover {
	background: color-mix(in srgb, var(--accent) 10%, var(--surf2));
}
.call-vgrid-more-count {
	width: 38px;
	height: 38px;
	border-radius: 50%;
	background: var(--surf2);
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 700;
	font-size: 13px;
	color: var(--text);
}
.call-vgrid-more-label {
	font-size: 11px;
}

/* ---------- Calls tab ---------- */
.calls-card { margin-bottom: 14px; }
.calls-act-btn { display: inline-flex; align-items: center; justify-content: center; padding: 6px 9px; }
.calls-missed .club-list-item-name,
.calls-meta.calls-missed { color: var(--err, #ff5c6c); }
.calls-meta.calls-missed { margin-top: 2px; }

/* ============================================================
   Home / Profile: header + sticky tab bar read as ONE block.
   The header is normally 92% opaque with a blur and a bottom border, and
   it carries 14px of empty bottom padding -- so under a sticky tab bar there
   was a visible strip between the search row and the tabs where posts
   ghosted through. Here the header is solid, borderless (the tab bar draws
   its own divider) and tighter underneath, so the tabs sit right under it.
   Scoped to the two views that have a sticky tab bar; <body data-view> is
   set by nav.js.
   ============================================================ */
body[data-view="home"] header,
body[data-view="profile"] header {
	background: var(--bg);
	-webkit-backdrop-filter: none;
	backdrop-filter: none;
	/* Same header padding as every other tab (see the base header rule), so the gap under the search row is
	   identical on Home / Profile / Shorts / Friends / Calls / Settings. */
}

/* ============================================================
   Shorts: the elapsed-time (progress) line is ALWAYS visible, pinned flush
   to the very bottom edge of the short -- i.e. sitting right on top of the
   bottom nav -- instead of fading in only on hover / while paused.
   It used to be opacity:0 until .show, and its 3px track floated ~7px above
   the edge (bar padding + a vertically-centred hit area).
   Drag-to-seek keeps working: the whole 12px strip (22px on touch) is the
   hit area, with the visible line at its bottom, growing upward on
   hover / drag.
   ============================================================ */
.sh-item .bvp-bar {
	opacity: 1;
	pointer-events: auto;
	background: none;     /* no dark gradient over the video -- just the line */
	padding: 0;
}
.sh-item .bvp-scrub { padding: 0; }
.sh-item .bvp-scrub-hit { align-items: flex-end; padding: 0; }
.sh-item .bvp-track {
	height: 3px;
	border-radius: 0;
	background: rgba(255, 255, 255, .3);
}
.sh-item .bvp-scrub.hover .bvp-track,
.sh-item .bvp-scrub.active .bvp-track { height: 5px; }
.sh-item .bvp-fill { background: #fff; }
/* the drag knob is centred on the track (now at the bottom of the hit area) */
.sh-item .bvp-knob { top: auto; bottom: -6.5px; margin-top: 0; }

/* ---- posting fields for the recommender (reco.js) ---- */

/* ===== Composer: visibility picker (pill + menu), left of the 0/300 ring ===== */
/* Each audience has its own colour (--vis-c): Everyone = accent, My College = sky, Friends = green. */
.sp-vis, .sp-vis-menu .sp-vm-item { --vis-c: var(--accent); }
.sp-vis[data-vis="college"], .sp-vm-item[data-k="college"] { --vis-c: #4aa8f0; }
.sp-vis[data-vis="friends"], .sp-vm-item[data-k="friends"] { --vis-c: var(--ok); }

.sp-vis {
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: 7px;
	height: 32px;
	padding: 0 9px 0 10px;
	border: 1px solid var(--border);
	border-radius: 0;
	background: var(--surf2);
	color: var(--text);
	font: 600 12.5px var(--sans);
	letter-spacing: 0;
	text-transform: none;
	line-height: 1;
	cursor: pointer;
	flex-shrink: 0;
	transition: background .15s, border-color .15s, transform .1s;
}
.sp-vis:hover { border-color: color-mix(in srgb, var(--accent) 50%, var(--border)); }
.sp-vis:active { transform: scale(.97); }
.sp-vis:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sp-vis[aria-expanded="true"] {
	border-color: color-mix(in srgb, var(--accent) 70%, var(--border));
}
.sp-vis[aria-expanded="true"] .sp-vis-chev { transform: rotate(180deg); opacity: 1; }
.sp-vis-ico { display: inline-flex; align-items: center; flex-shrink: 0; color: var(--muted); }
.sp-vis-ico svg { width: 14px; height: 14px; }
.sp-vis-body { display: flex; flex-direction: row; align-items: center; gap: 6px; min-width: 0; white-space: nowrap; }
.sp-vis-lbl { font-size: 9.5px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.sp-vis-txt { font-size: 12.5px; font-weight: 600; color: var(--text); white-space: nowrap; }
.sp-vis-chev { flex-shrink: 0; margin-left: 2px; color: var(--muted); opacity: .8; transition: transform .18s, opacity .15s; }

/* ---- menu (desktop: popover next to the pill; phones: bottom sheet) ---- */
.sp-vis-menu {
	position: fixed;
	z-index: 1200;
	width: 210px;
	max-width: calc(100vw - 16px);
	box-sizing: border-box;
	padding: 6px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 12px;
	box-shadow: 0 18px 48px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .25);
	transform-origin: bottom right;
	animation: sp-vm-pop .16s cubic-bezier(.2, .8, .2, 1);
}
.sp-vm-title {
	padding: 6px 10px 8px;
	font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
	color: var(--muted);
}
.sp-vm-item {
	position: relative;
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	column-gap: 10px;
	align-items: center;
	width: 100%;
	margin-top: 4px;
	padding: 8px 10px;
	border: 1px solid var(--border);
	border-radius: 8px;
	background: transparent;
	color: var(--accent-soft);
	text-align: left;
	font: inherit;
	cursor: pointer;
	animation: sp-vm-row .22s cubic-bezier(.2, .8, .2, 1) both;
	animation-delay: calc(var(--i, 0) * 35ms);
	transition: background .12s, border-color .12s;
}
.sp-vm-item:hover:not(:disabled), .sp-vm-item:focus-visible { background: var(--surf2); outline: none; }
.sp-vm-item.on,
.sp-vm-item.on:hover:not(:disabled) {
	background: color-mix(in srgb, var(--accent-soft) 14%, transparent);
	border-color: var(--accent-soft);
}
.sp-vm-item:disabled { opacity: .45; cursor: not-allowed; }
.sp-vm-ico {
	display: inline-flex; align-items: center; justify-content: center;
	width: 26px; height: 26px; flex-shrink: 0;
	color: var(--muted);
}
.sp-vm-item.on .sp-vm-ico { color: var(--accent-soft); }
.sp-vm-ico svg { width: 16px; height: 16px; }
.sp-vm-txt { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.sp-vm-txt b { font-size: 14px; font-weight: 700; color: var(--accent-soft); }
.sp-vm-txt small { font-size: 12px; color: var(--muted); line-height: 1.3; }
.sp-vm-radio {
	display: inline-flex; align-items: center; justify-content: center;
	width: 20px; height: 20px; border-radius: 5px; box-sizing: border-box;
	border: 2px solid var(--border);
	color: #fff;
	transition: background .12s, border-color .12s;
}
.sp-vm-radio svg { width: 12px; height: 12px; opacity: 0; transform: scale(.6); transition: opacity .12s, transform .15s; }
.sp-vm-item.on .sp-vm-radio { background: var(--vis-c); border-color: var(--vis-c); }
.sp-vm-item.on .sp-vm-radio svg { opacity: 1; transform: none; }
.sp-vm-foot { margin: 6px 4px 2px; padding: 9px 10px 4px; border-top: 1px solid var(--border); font-size: 11.5px; line-height: 1.35; color: var(--muted); }

.sp-vis-scrim { display: none; }
@keyframes sp-vm-pop { from { opacity: 0; transform: translateY(6px) scale(.97); } to { opacity: 1; transform: none; } }
@keyframes sp-vm-row { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@keyframes sp-vm-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes sp-vm-sheet { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
	.sp-vis-menu, .sp-vm-item, .sp-vis-scrim { animation: none !important; }
}
@media (max-width: 520px) {
	.sp-vis-scrim { display: block; position: fixed; inset: 0; z-index: 1199; background: rgba(0, 0, 0, .5); animation: sp-vm-fade .18s; }
	.sp-vis-menu {
		left: 8px !important; right: 8px; top: auto !important;
		bottom: calc(8px + env(safe-area-inset-bottom, 0px));
		width: auto; max-width: none; border-radius: 14px; transform-origin: bottom center;
		animation: sp-vm-sheet .24s cubic-bezier(.2, .8, .2, 1);
	}
	.sp-vis-menu::before { content: ''; display: block; width: 36px; height: 4px; margin: 2px auto 6px; border-radius: 2px; background: var(--border); }
	.sp-vm-item { padding: 10px; }
}

/* over-length video notice (auto-trimmed to 60 s) */
.sp-note { color: var(--warn); font-size: 12.5px; margin: 4px 0; line-height: 1.35; }
.sp-vpreview-plus { margin-left: 2px; font-weight: 800; }

/* ===== Profile hover card (assets/js/social.js: _sphc*) ===== */
.sp-hovercard {
	position: absolute;
	z-index: 400;
	width: 300px;
	max-width: calc(100vw - 16px);
	box-sizing: border-box;
	padding: 14px 16px 16px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 14px;
	box-shadow: 0 12px 36px rgba(0, 0, 0, .5);
	animation: sp-hc-in .12s ease-out;
}
@keyframes sp-hc-in { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }
.sp-hc-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.sp-hc-av { width: 64px; height: 64px; font-size: 22px; cursor: pointer; flex-shrink: 0; }
.sp-hc-follow {
	border: 0; border-radius: 999px; padding: 8px 16px;
	background: var(--accent); color: #fff; font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer;
}
.sp-hc-follow:hover:not(:disabled) { filter: brightness(1.1); }
.sp-hc-follow:disabled { opacity: .6; cursor: default; }
.sp-hc-follow.on { background: transparent; color: var(--text); border: 1px solid var(--border); }
.sp-hc-follow.on:hover:not(:disabled) { border-color: var(--err); color: var(--err); }
.sp-hc-name { font-weight: 800; font-size: 17px; line-height: 1.25; cursor: pointer; overflow-wrap: anywhere; }
.sp-hc-name:hover { text-decoration: underline; }
.sp-hc-handle { color: var(--muted); font-size: 13.5px; margin-top: 1px; overflow-wrap: anywhere; }
.sp-hc-fy { margin-left: 6px; padding: 1px 7px; border-radius: 6px; background: var(--surf2); font-size: 11px; font-weight: 600; }
.sp-hc-counts { display: flex; gap: 16px; margin-top: 10px; font-size: 13.5px; color: var(--muted); }
.sp-hc-counts b { color: var(--text); font-weight: 800; }
.sp-hc-bio {
	margin-top: 10px; font-size: 13.5px; line-height: 1.4; color: var(--text); overflow-wrap: anywhere;
	display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.sp-hc-college { color: var(--muted); }
.sp-hc-skav { width: 64px; height: 64px; border-radius: 50%; }
.sp-hc-skl { height: 12px; border-radius: 6px; margin-top: 10px; }
@media (prefers-reduced-motion: reduce) { .sp-hovercard { animation: none; } }

/* Light theme: the sticky header and bottom nav used a hard-coded near-black
   background, so in light mode they stayed dark (and the dark wordmark text on
   them disappeared). Follow the theme like everything else. */
html[data-auth-theme="light"] header,
html[data-auth-theme="light"] .bottom-nav {
	background: rgba(244, 244, 248, .92);
}

/* One typeface everywhere: Roboto Condensed. Form controls and code-ish elements never fall back to the browser/monospace default. */
button, input, select, textarea, optgroup, code, kbd, samp, pre {
	font-family: var(--sans);
}

/* ---- Avatar picker, laid out like the system emoji panel (see avatarpicker.js) ----
   category icon row -> search -> "Frequently used" -> one section per category. Only the grid scrolls. */
.avatar-picker-modal-box {
	width: 460px;
	height: min(82vh, 640px);
	max-height: 82vh;
	overflow: hidden;
}
#avatarPickerGrid {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
}
.avatar-picker-tabs {
	display: flex;
	justify-content: space-between;
	gap: 0;
	margin-bottom: 10px;
	border-bottom: 1px solid var(--border);
}
.avatar-picker-tab {
	position: relative;
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	height: 40px;
	background: none;
	border: 0;
	padding: 0;
	color: var(--muted);
	cursor: pointer;
	transition: color .15s, background .15s;
	-webkit-tap-highlight-color: transparent;
}
.avatar-picker-tab:hover { color: var(--text); background: var(--surf2); border-radius: 8px 8px 0 0; }
.avatar-picker-tab.active { color: var(--accent); }
.avatar-picker-tab.active::after {
	content: '';
	position: absolute;
	left: 14%;
	right: 14%;
	bottom: -1px;
	height: 3px;
	border-radius: 3px 3px 0 0;
	background: var(--accent);
}
.avatar-picker-search-row {
	position: static;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 9px 12px;
	margin-bottom: 8px;
	flex: none;
}
#avatarPickerResults {
	position: relative;
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-width: thin;
}
#avatarPickerResults .avatar-picker-group-label {
	text-transform: none;
	letter-spacing: 0;
	font-size: 13px;
	font-weight: 600;
	color: var(--text);
	border-bottom: 0;
	margin: 10px 0 4px;
	padding: 0 2px;
}
#avatarPickerResults .avatar-picker-section:first-child .avatar-picker-group-label { margin-top: 2px; }
#avatarPickerResults .avatar-picker-grid {
	grid-template-columns: repeat(9, 1fr);
	gap: 2px;
}
#avatarPickerResults .avatar-picker-item {
	background: none;
	border: 0;
	border-radius: 50%;
	overflow: visible;
	box-shadow: none;
}
#avatarPickerResults .avatar-picker-item img { width: 76%; height: 76%; }
/* emoji with no animated version fall back to the plain glyph (see emojiImgFallback) */
#avatarPickerResults .avatar-picker-item { font-size: 24px; line-height: 1; }
.avatar-picker-tabs { overflow: hidden; }
#avatarPickerResults .avatar-picker-item:hover,
#avatarPickerResults .avatar-picker-item:focus-visible {
	background: var(--surf2);
	border: 0;
	transform: scale(1.1);
	box-shadow: none;
	outline: none;
}
@media (max-width: 480px) {
	.avatar-picker-modal-box { width: 100%; }
	#avatarPickerResults .avatar-picker-grid { grid-template-columns: repeat(8, 1fr); }
	.avatar-picker-tab { height: 38px; }
}

/* ---- Avatar picker: no search row, no sideways scroll, content flush to the box edges ---- */
.avatar-picker-search-row { display: none !important; }
.avatar-picker-modal-box { padding: 14px 0 0; gap: 8px; }
.avatar-picker-modal-box .modal-header { padding: 0 14px; }
.avatar-picker-tabs { margin-bottom: 4px; }
#avatarPickerResults { overflow-x: hidden; padding: 0 2px; }
#avatarPickerResults .avatar-picker-group-label { margin-left: 0; margin-right: 0; padding: 0 8px; }
#avatarPickerResults .avatar-picker-grid { width: 100%; box-sizing: border-box; }

.pf-avatar-open { cursor: zoom-in; }

/* ---- Avatar picker emoji tiles: real emoji glyphs, not 500 animated GIFs ---- */
#avatarPickerResults .avatar-picker-item {
	font-family: "Segoe UI Emoji", "Apple Color Emoji", "Noto Color Emoji", "Twemoji Mozilla", "EmojiOne Color", sans-serif;
	font-size: 26px;
	line-height: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	aspect-ratio: 1;
	padding: 0;
	color: var(--text);
}

/* ---- Settings -> Account: editable rows, badges, editor dialog ---- */
button.st-info-edit {
	width: 100%;
	background: none;
	border: 0;
	border-bottom: 1px solid var(--border);
	color: inherit;
	font-family: var(--sans);
	text-align: left;
	cursor: pointer;
	align-items: center;
}
button.st-info-edit:hover { background: var(--surf2); }
.st-info-edit .st-info-value { flex: 1; min-width: 0; }
.st-info-edit .st-row-chev { display: flex; flex-shrink: 0; color: var(--muted); }
.st-add { color: var(--accent-soft); font-weight: 700; }
.st-lock { display: inline-flex; vertical-align: -2px; margin-left: 6px; color: var(--muted); opacity: .8; }
.st-lock svg { width: 13px; height: 13px; }
.st-badge { display: inline-block; margin-left: 6px; padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 700; vertical-align: 1px; }
.st-badge-ok { background: color-mix(in srgb, var(--ok) 18%, transparent); color: var(--ok); }
.st-badge-warn { background: color-mix(in srgb, var(--err) 14%, transparent); color: var(--err); }
.st-info-action { align-items: center; }
.st-info-action .st-info-label { font-size: 13px; }
.st-edit-box { width: min(420px, 92vw); }
.st-edit-hint { margin: -4px 0 0; }
.st-edit-input {
	width: 100%;
	box-sizing: border-box;
	background: var(--surf2);
	color: var(--text);
	border: 1px solid var(--border);
	border-radius: 10px;
	padding: 11px 12px;
	font: 600 15px var(--sans);
	outline: none;
}
.st-edit-input:focus { border-color: var(--accent-soft); }
textarea.st-edit-input { resize: none; height: 120px; min-height: 120px; max-height: 120px; overflow-y: auto; scrollbar-width: none; -ms-overflow-style: none; }
textarea.st-edit-input::-webkit-scrollbar { display: none; width: 0; height: 0; }
.st-otp-input { text-align: center; letter-spacing: .5em; font-size: 22px; }
.st-edit-err { min-height: 0; color: var(--err); font-size: 13px; }
.st-edit-err:empty { display: none; }
.st-edit-actions { display: flex; gap: 10px; justify-content: flex-end; }
.st-edit-actions .btn { min-width: 96px; }

/* ---- Profile: public About line ---- */
.pf-about { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 10px; font-size: 13.5px; color: var(--muted); }
.pf-about span, .pf-about a { display: inline-flex; align-items: center; gap: 5px; }
.pf-about a { color: var(--accent-soft); text-decoration: none; }
.pf-about a:hover { text-decoration: underline; }

/* ---- login: QR sign-in step ---- */
.auth-qr-wrap { display: flex; flex-direction: column; align-items: center; gap: 12px; margin: 14px 0 6px; }
.auth-qr-box { padding: 12px; background: #fff; border-radius: 14px; min-width: 224px; min-height: 224px; display: flex; align-items: center; justify-content: center; transition: opacity .2s; }
.auth-qr-box img, .auth-qr-box canvas { display: block; max-width: 100%; height: auto; }
.auth-qr-box { position: relative; }
.auth-qr-box .auth-qr-logo { position: absolute; left: 50%; top: 50%; width: 54px; height: 54px; transform: translate(-50%, -50%); border-radius: 50%; background: #fff; display: grid; place-items: center; box-shadow: 0 0 0 1px rgba(0,0,0,.06); pointer-events: none; }
.auth-qr-box .auth-qr-logo img, .auth-qr-box .auth-qr-logo svg { width: 46px; height: 46px; border-radius: 50%; display: block; }
.auth-qr-status { font-size: 13px; color: var(--muted, #8a8a99); text-align: center; min-height: 18px; }

/* Wide, edge-to-edge rows and list items do not shrink when pressed (the global button:active scale made them
   pull in from the left and right and leave a gap); small buttons and icons keep the press effect. */
:is(.college-combo-trigger, .username-suggest-item, .chat-msg-menu button, .club-list-item, .syllabus-branch-btn,
	.pdf-toc-item, .pdf-toc-thumb-item, .dept-option, .sp-reply-prompt, .sp-sheet-item, .sh-sheet-item,
	.profile-my-shorts-row, .hs-row, .sp-lc-media, .sponsor-short-opt, .sp-vm-item):active {
	transform: none;
}

/* Verified icon next to a name (verifiedBadge() in helpers.js) */
.vf-badge {
	display: inline-flex;
	vertical-align: -0.14em;
	margin-left: 4px;
	font-size: 0.95em;
	line-height: 1;
}
.vf-badge svg { display: block; }

.sponsor-verified-preview {
	display: flex;
	align-items: center;
	padding: 12px 14px;
	border: 1px solid var(--border, rgba(127,127,127,.25));
	border-radius: 12px;
	font-weight: 700;
	font-size: 16px;
}
.sponsor-verified-preview .vf-badge { font-size: 1.1em; }
.sponsor-verified-note {
	display: flex; flex-direction: column; gap: 4px;
	padding: 12px 14px; margin-bottom: 14px;
	border-radius: 12px; border: 1px solid var(--border, rgba(127,127,127,.25));
	font-size: 13px; line-height: 1.45;
}
.sponsor-verified-note b { font-size: 14px; display: inline-flex; align-items: center; gap: 4px; }
.sponsor-verified-note span { color: var(--muted, inherit); }
.sponsor-verified-note.active { border-color: color-mix(in srgb, #1d9bf0 55%, transparent); background: color-mix(in srgb, #1d9bf0 10%, transparent); }
.sponsor-verified-note.pending { border-color: color-mix(in srgb, #f5a623 55%, transparent); background: color-mix(in srgb, #f5a623 10%, transparent); }

/* CALLS tab: no card chrome (background / border / radius), and the content starts at the same distance from the
   header as Settings does (Settings' title bar has 4px top padding and no card margin). */
.card.calls-card {
	background: none;
	border: 0;
	border-radius: 0;
	margin-top: 0;
	padding: 0;      /* the title row below (.fr-head) carries the same 4px top padding as the Friends tab */
}

/* Screenshot prevention (assets/js/screenshot-guard.js) */
#beuShotCover {
	display: none;
	position: fixed;
	inset: 0;
	z-index: 2147483647;
	align-items: center;
	justify-content: center;
	background: var(--bg, #0d0d0f);
	color: var(--muted, #9aa0a6);
	text-align: center;
	font-weight: 600;
}
#beuShotCover .beu-shot-msg { display: flex; flex-direction: column; align-items: center; gap: 10px; }
html.beu-shotguard body {
	-webkit-user-select: none;
	user-select: none;
	-webkit-touch-callout: none;
}
html.beu-shotguard input, html.beu-shotguard textarea, html.beu-shotguard select, html.beu-shotguard [contenteditable="true"] {
	-webkit-user-select: text;
	user-select: text;
}
html.beu-shotguard img { -webkit-user-drag: none; }
@media print {
	html.beu-shotguard body { display: none !important; }
}

/* ==========================================================================
   FRIENDS -- WhatsApp-style list + split view (beuclub theme colours)
   Small screens: full-width list, a chat opens over it (existing behaviour).
   Wide screens (>= 821px): list on the left, chat / placeholder on the right.
   ========================================================================== */
.fr-shell {
	display: flex;
	/* flex:none (NOT flex:1): .fr-shell sits in .app-view, a column flexbox whose height isn't capped, and
	   with flex:1 its flex-basis (0) overrode the fixed height below -- the shell then grew to fit every
	   message and the WHOLE PAGE scrolled (Friends header, chat header and compose bar all scrolled away).
	   Pinned to the viewport between the top header and bottom nav, only .club-messages / .fr-scroll scroll. */
	flex: none;
	min-height: 0;
	height: calc(100dvh - var(--header-h, 64px) - var(--bottom-nav-h, calc(76px + env(safe-area-inset-bottom))) - 6px);
}

.fr-list-pane {
	flex: 1;
	min-width: 0;
	min-height: 0;
	display: flex;
	flex-direction: column;
}

.fr-card {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
}

.fr-head {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 14px 6px 10px;
	flex-shrink: 0;
}

.fr-title {
	font-size: 22px;
	font-weight: 800;
	letter-spacing: .01em;
	color: var(--text);
}

.fr-head-actions { position: relative; display: flex; align-items: center; }

.fr-icon-btn {
	width: 36px;
	height: 36px;
	display: flex;
	align-items: center;
	justify-content: center;
	border: none;
	border-radius: 50%;
	background: transparent;
	color: var(--text);
	cursor: pointer;
	transition: background .15s;
}
.fr-icon-btn:hover { background: var(--surf2); }

.fr-menu {
	position: absolute;
	top: 40px;
	right: 0;
	z-index: 400;
	min-width: 180px;
	padding: 6px;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 12px;
	box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
}
.fr-menu-item {
	display: block;
	width: 100%;
	padding: 10px 12px;
	border: none;
	border-radius: 8px;
	background: transparent;
	color: var(--text);
	font: inherit;
	font-size: 14px;
	text-align: left;
	cursor: pointer;
}
.fr-menu-item:hover { background: color-mix(in srgb, var(--accent) 16%, transparent); }

.fr-scroll {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding-bottom: 8px;
}
.fr-scroll .people-section { margin-top: 0; padding: 0 2px; }
.fr-scroll .people-section + .people-section { margin-top: 10px; }
.fr-list { overflow: visible; gap: 2px; flex: none; }

.fr-row {
	display: flex;
	align-items: center;
	gap: 14px;
	padding: 10px 10px;
	border-radius: 14px;
	cursor: pointer;
	color: var(--text);
	transition: background .15s;
}
.fr-row:hover { background: var(--surf2); }
.fr-row.active { background: color-mix(in srgb, var(--accent) 20%, var(--surface)); }

.fr-avatar { position: relative; width: 50px; height: 50px; flex-shrink: 0; }
.fr-avatar-img {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	overflow: hidden;
	background: var(--accent);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 800;
	font-size: 16px;
}
.fr-avatar-img img { border-radius: 0 !important; width: 100%; height: 100%; object-fit: cover; }
.fr-online {
	position: absolute;
	right: 0;
	bottom: 0;
	width: 13px;
	height: 13px;
	border-radius: 50%;
	background: var(--ok);
	border: 2px solid var(--bg);
}
.fr-row.active .fr-online { border-color: color-mix(in srgb, var(--accent) 20%, var(--surface)); }

.fr-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.fr-line1, .fr-line2 { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; }
.fr-name {
	font-size: 15.5px;
	font-weight: 700;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	min-width: 0;
}
.fr-time { font-size: 12px; color: var(--muted); flex-shrink: 0; }
.fr-row.unread .fr-time { color: var(--accent-soft); }
.fr-sub {
	font-size: 13.5px;
	color: var(--muted);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	min-width: 0;
}
.fr-status { display: flex; align-items: center; gap: 4px; flex-shrink: 0; min-height: 20px; color: var(--muted); }
.fr-status-ico { display: flex; align-items: center; }
.fr-badge {
	min-width: 20px;
	height: 20px;
	padding: 0 6px;
	border-radius: 10px;
	background: var(--accent);
	color: #fff;
	font-size: 11.5px;
	font-weight: 800;
	line-height: 20px;
	text-align: center;
}
.fr-more {
	display: none;
	align-items: center;
	justify-content: center;
	width: 22px;
	height: 22px;
	border: none;
	border-radius: 50%;
	background: transparent;
	color: var(--muted);
	cursor: pointer;
}
.fr-more:hover { color: var(--text); background: var(--border); }
@media (hover: hover) {
	.fr-row:hover .fr-more { display: flex; }
}

/* placeholder pane (wide screens only) */
.fr-empty-pane { display: none; }

.fr-chat-pane .club-back-btn { }

@media (min-width: 768px) {
	.fr-list-pane {
		flex: 0 0 clamp(300px, 38%, 400px);
		padding-right: 8px;
		border-right: 1px solid var(--border);
	}
	.fr-empty-pane {
		flex: 1;
		min-width: 0;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: 10px;
		text-align: center;
		padding: 20px;
	}
	.fr-view.fr-chat-open .fr-empty-pane { display: none; }
	.fr-empty-icon {
		width: 110px;
		height: 110px;
		border-radius: 50%;
		display: flex;
		align-items: center;
		justify-content: center;
		background: color-mix(in srgb, var(--accent) 16%, var(--surface));
		color: var(--accent-soft);
		margin-bottom: 10px;
	}
	.fr-empty-title { font-size: 18px; font-weight: 700; color: var(--text); }
	.fr-empty-sub { font-size: 14px; color: var(--muted); }
	.fr-empty-bar { width: 28px; height: 3px; border-radius: 2px; background: var(--accent); margin-top: 8px; }

	/* the DM chat sits in the right column instead of floating over the app */
	#viewFriends .fr-chat-pane {
		position: static !important;
		transform: none !important;
		top: auto !important;
		bottom: auto !important;
		left: auto !important;
		width: auto !important;
		max-width: none !important;
		flex: 1;
		min-width: 0;
		min-height: 0;
		z-index: auto;
		padding: 0;
	}
	#viewFriends .fr-chat-pane .club-back-btn { display: none; }
}

/* small screens: opening a chat replaces the list */
@media (max-width: 767px) {
	.fr-view.fr-chat-open .fr-list-pane { display: none; }
}

/* ==========================================================================
   FRIENDS DM CHAT -- WhatsApp-style (scoped to #dmChatPane so Club chat,
   which shares the .club-msg classes, is untouched)
   ========================================================================== */
#dmChatPane {
	--dm-out: color-mix(in srgb, var(--accent) 42%, #141219);
	--dm-in: var(--surf2);
	padding: 0;
	background: var(--bg);
}

/* header */
#dmChatPane .dm-head {
	display: flex;
	align-items: center;
	gap: 10px;
	margin: 0;
	padding: 8px 10px;
	background: var(--surface);
	border-bottom: 1px solid var(--border);
}
#dmChatPane .club-back-btn { background: transparent; border: none; width: 34px; height: 34px; border-radius: 50%; }
#dmChatPane .club-back-btn:hover { background: var(--surf2); }
.dm-head-avatar {
	position: relative;
	width: 42px;
	height: 42px;
	flex-shrink: 0;
	border-radius: 50%;
	overflow: hidden;
	background: var(--accent);
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 800;
	cursor: pointer;
}
.dm-head-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 0 !important; }
.dm-head-text { flex: 1; min-width: 0; cursor: pointer; display: flex; flex-direction: column; gap: 1px; }
#dmChatPane .club-chat-title { font-size: 16.5px; font-weight: 700; flex: none; }
.dm-head-sub { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dm-head-avatar.online + .dm-head-text .dm-head-sub { color: var(--ok); }
.dm-head-btn {
	width: 38px;
	height: 38px;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	border: none;
	border-radius: 50%;
	background: transparent;
	color: var(--text);
	cursor: pointer;
}
.dm-head-btn:hover { background: var(--surf2); }
#dmChatPane .fr-menu { top: 42px; }

/* message area -- plain theme background (the doodle wallpaper image was removed) */
#dmChatPane .club-messages {
	overscroll-behavior: contain;   /* reaching the top/bottom of the messages never scrolls the page behind */
	gap: 3px;
	padding: 10px 12px 12px;
	background-color: var(--bg);
	background-image: none;
}

#dmChatPane .chat-date-divider { margin: 10px 0 6px; }
#dmChatPane .chat-date-divider span {
	background: color-mix(in srgb, var(--surf2) 92%, transparent);
	border: none;
	font-size: 12px;
	font-weight: 600;
	color: var(--muted);
	padding: 4px 12px;
	border-radius: 8px;
}

/* bubbles */
#dmChatPane .club-msg { max-width: 82%; gap: 0; align-items: flex-start; position: relative; margin-top: 2px; }
#dmChatPane .club-msg .club-msg-avatar,
#dmChatPane .club-msg .club-msg-name { display: none; }
#dmChatPane .club-msg-bubble {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: flex-end;
	column-gap: 10px;
	background: var(--dm-in);
	border: none;
	border-radius: 10px;
	border-top-left-radius: 2px;
	padding: 7px 10px 6px 11px;
	color: var(--text);
	box-shadow: 0 1px 1px rgba(0, 0, 0, .25);
}
#dmChatPane .club-msg.mine .club-msg-bubble {
	background: var(--dm-out);
	border: none;
	border-top-left-radius: 10px;
	border-top-right-radius: 2px;
}
/* tails */
#dmChatPane .club-msg-bubble::before {
	content: "";
	position: absolute;
	top: 0;
	left: -7px;
	width: 9px;
	height: 12px;
	background: var(--dm-in);
	clip-path: polygon(100% 0, 0 0, 100% 100%);
}
#dmChatPane .club-msg.mine .club-msg-bubble::before {
	left: auto;
	right: -7px;
	background: var(--dm-out);
	clip-path: polygon(0 0, 100% 0, 0 100%);
}
#dmChatPane .club-msg-text { font-size: 15px; line-height: 1.4; flex: 0 1 auto; max-width: 100%; }
#dmChatPane .club-msg-image { max-width: 260px; border-radius: 6px; }
#dmChatPane .club-msg-bubble > .club-msg-image,
#dmChatPane .club-msg-bubble > .club-msg-caption,
#dmChatPane .club-msg-bubble > .club-msg-reactions,
#dmChatPane .club-msg-bubble > .club-msg-edit-row { flex: 1 1 100%; }
#dmChatPane .club-msg-time {
	display: flex;
	align-items: center;
	gap: 3px;
	flex: 0 0 auto;
	margin: 3px 0 0 auto;
	font-size: 11.5px;
	color: color-mix(in srgb, var(--text) 55%, transparent);
	line-height: 1;
	padding-top: 4px;
}
#dmChatPane .dm-tick { display: inline-flex; align-items: center; color: color-mix(in srgb, var(--text) 50%, transparent); }
#dmChatPane .dm-tick-read { color: #53bdeb; }
#dmChatPane .dm-edited { margin-right: 3px; }
#dmChatPane .club-msg-edited { display: none; }
#dmChatPane .club-msg-reactions { margin-top: 4px; }

/* compose bar */
#dmChatPane .dm-compose {
	align-items: flex-end;
	gap: 8px;
	margin: 0;
	padding: 6px 10px 10px;
	background: var(--bg);
}
.dm-input-wrap {
	flex: 1;
	min-width: 0;
	display: flex;
	align-items: flex-end;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 24px;
	padding: 0 6px 0 4px;
}
.dm-input-wrap:focus-within { border-color: var(--border); }
#dmChatPane .dm-input-wrap .club-msg-input {
	background: transparent;
	border: none;
	border-radius: 0;
	padding: 11px 10px;
	font-size: 15px;
	min-height: 44px;
}
#dmChatPane .dm-input-wrap .club-msg-input:focus { border: none; outline: none; box-shadow: none; }
#dmChatPane .dm-input-wrap .club-attach-btn {
	background: transparent;
	border: none;
	width: 40px;
	height: 44px;
	border-radius: 50%;
}
.dm-send-btn {
	width: 46px;
	height: 46px;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	border: none;
	border-radius: 50%;
	background: var(--accent);
	color: #fff;
	cursor: pointer;
}
.dm-send-btn:disabled { opacity: .5; cursor: default; }

/* DM header three-dot menu (View profile / Mute / Disable forwarding / ...) */
#dmChatPane .dm-menu { min-width: 250px; padding: 6px; top: 44px; right: 0; border-radius: 12px; }
.dm-menu-item { display: flex; align-items: center; gap: 14px; padding: 11px 12px; font-size: 15px; }
.dm-menu-item svg { flex-shrink: 0; color: var(--muted); }
.dm-menu-item:hover svg { color: var(--text); }
.dm-menu-item.danger, .dm-menu-item.danger svg { color: var(--err); }
.dm-menu-item.danger:hover { background: color-mix(in srgb, var(--err) 14%, transparent); }
.dm-menu-sep { height: 1px; margin: 6px 4px; background: var(--border); }

/* "Disable forwarding" -- copying / saving blocked inside the chat */
.dm-noforward-note {
	flex-shrink: 0;
	text-align: center;
	font-size: 12px;
	padding: 5px 10px;
	color: var(--muted);
	background: var(--surface);
	border-bottom: 1px solid var(--border);
}
#dmChatPane.dm-noforward .club-messages {
	-webkit-user-select: none;
	user-select: none;
	-webkit-touch-callout: none;
}
#dmChatPane.dm-noforward .club-messages img { -webkit-user-drag: none; user-drag: none; }

/* ==========================================================================
   DM MESSAGE MENU (three dots beside each bubble), reply, pinned bar, forward
   ========================================================================== */
#dmChatPane .club-msg-actions.dm-msg-more {
	flex-direction: row;
	align-self: flex-start;
	margin: 2px 4px 0;
	opacity: .8;
}
#dmChatPane .club-msg:hover .dm-msg-more,
#dmChatPane .club-msg:focus-within .dm-msg-more { opacity: 1; }
.dm-msg-more-btn {
	width: 28px;
	height: 28px;
	display: flex;
	align-items: center;
	justify-content: center;
	border: none;
	border-radius: 50%;
	background: transparent;
	color: color-mix(in srgb, var(--text) 62%, transparent);
	cursor: pointer;
}
.dm-msg-more-btn:hover { background: var(--surf2); color: var(--text); }

.dm-msg-menu {
	position: fixed;
	z-index: 1250;
	min-width: 200px;
	max-width: calc(100vw - 16px);
	padding: 6px;
	border-radius: 14px;
	background: var(--surface);
	border: 1px solid var(--border);
	box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
	animation: fadeIn .12s ease;
}
.dm-msg-menu-item {
	display: flex;
	align-items: center;
	gap: 16px;
	width: 100%;
	padding: 11px 14px;
	border: none;
	border-radius: 10px;
	background: transparent;
	color: var(--text);
	font-size: 15px;
	text-align: left;
	cursor: pointer;
}
.dm-msg-menu-item svg { flex-shrink: 0; color: var(--muted); }
.dm-msg-menu-item:hover { background: var(--surf2); }
.dm-msg-menu-item:hover svg { color: var(--text); }
.dm-msg-menu-item.danger, .dm-msg-menu-item.danger svg { color: var(--err); }
.dm-msg-menu-item.danger:hover { background: color-mix(in srgb, var(--err) 14%, transparent); }

/* quoted message at the top of a reply bubble */
#dmChatPane .dm-reply-quote {
	flex: 1 1 100%;
	min-width: 0;
	margin: 0 0 5px;
	padding: 5px 9px;
	border-left: 3px solid var(--accent-soft);
	border-radius: 6px;
	background: rgba(0, 0, 0, .2);
	cursor: pointer;
	text-align: left;
}
#dmChatPane .dm-reply-quote.own { border-left-color: #53bdeb; }
.dm-reply-name { font-size: 12.5px; font-weight: 700; color: var(--accent-soft); }
#dmChatPane .dm-reply-quote.own .dm-reply-name { color: #53bdeb; }
.dm-reply-text {
	font-size: 13px;
	color: color-mix(in srgb, var(--text) 70%, transparent);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* "Replying to ..." bar above the compose box */
.dm-reply-bar {
	display: flex;
	align-items: center;
	gap: 6px;
	flex-shrink: 0;
	padding: 6px 10px 0;
	background: var(--bg);
}
.dm-reply-bar-body {
	flex: 1;
	min-width: 0;
	padding: 6px 10px;
	border-left: 3px solid var(--accent-soft);
	border-radius: 8px;
	background: var(--surf2);
}
.dm-reply-close {
	width: 32px;
	height: 32px;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	border: none;
	border-radius: 50%;
	background: transparent;
	color: var(--muted);
	cursor: pointer;
}
.dm-reply-close:hover { background: var(--surf2); color: var(--text); }

/* pinned-message strip under the chat header */
.dm-pinned-bar {
	display: flex;
	align-items: center;
	gap: 10px;
	flex-shrink: 0;
	padding: 7px 12px;
	background: var(--surface);
	border-bottom: 1px solid var(--border);
	color: var(--accent-soft);
	cursor: pointer;
}
/* Press feedback: only the label (title + text) zooms -- the pin icon and the bar itself stay put. This beats the
   global [data-onclick]:active scale(.96), which shrank the whole strip. */
#dmPinnedBar, #dmPinnedBar:active { transform: none; transition: none; }
.dm-pinned-body { flex: 1; min-width: 0; transform-origin: left center; transition: transform .12s ease; }
#dmPinnedBar:active .dm-pinned-body { transform: scale(0.96); }
/* the bar moves on to the next pin: new text slides/fades in rather than snapping */
@keyframes dmPinSwap { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
.dm-pinned-body.dm-pin-swap { animation: dmPinSwap .24s cubic-bezier(.2, .7, .3, 1); }
@media (prefers-reduced-motion: reduce) { .dm-pinned-body.dm-pin-swap { animation: none; } }
.dm-pinned-title { font-size: 11.5px; font-weight: 700; color: var(--accent-soft); }
.dm-pinned-text {
	font-size: 13px;
	color: var(--text);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* flash the original when a quote / pinned strip jumps to it */
/* The flash is a colour wash on the BUBBLE only (inset shadow, fades out). It never touches the row's size, radius,
   transform or background, so the message can't visibly shrink/grow while it flashes or when the flash ends. */
/* The bubble's little corner "head" (the ::before tail) has its own background, so an inset shadow on the bubble left it
   un-highlighted and the corner looked cut off. The wash is now a background-colour fade run on the bubble AND its tail
   with the same timing, so the whole shape (curve + tail) lights up and fades together, smoothly in and out. */
#dmChatPane .club-msg-bubble { --dm-base: var(--dm-in); }
#dmChatPane .club-msg.mine .club-msg-bubble { --dm-base: var(--dm-out); }
@keyframes dmMsgFlash {
	0%   { background: var(--dm-base); }
	16%, 58% { background: color-mix(in srgb, var(--accent) 52%, var(--dm-base)); }
	100% { background: var(--dm-base); }
}
#dmChatPane .club-msg.dm-msg-flash { animation: none; transform: none; }
#dmChatPane .club-msg.dm-msg-flash .club-msg-bubble,
#dmChatPane .club-msg.dm-msg-flash .club-msg-bubble::before { animation: dmMsgFlash 1.7s cubic-bezier(.4, 0, .2, 1); }
#dmChatPane .club-msg.dm-msg-flash, #dmChatPane .club-msg.dm-msg-flash .club-msg-bubble { transition: none; }

/* Forward picker */
.dm-fwd-box { width: 92%; max-width: 380px; }
.dm-fwd-preview {
	margin: 2px 0 6px;
	padding: 6px 10px;
	border-left: 3px solid var(--accent-soft);
	border-radius: 6px;
	background: var(--surf2);
	font-size: 13px;
	color: var(--muted);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.dm-fwd-list {
	max-height: min(46vh, 340px);
	overflow-y: auto;
	margin: 6px 0 10px;
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.dm-fwd-row {
	display: flex;
	align-items: center;
	gap: 12px;
	width: 100%;
	padding: 8px 10px;
	border: none;
	border-radius: 10px;
	background: transparent;
	color: var(--text);
	text-align: left;
	cursor: pointer;
}
.dm-fwd-row:hover { background: var(--surf2); }
.dm-fwd-avatar {
	width: 38px;
	height: 38px;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	overflow: hidden;
	background: var(--accent);
	color: #fff;
	font-weight: 800;
}
.dm-fwd-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50% !important; }
.dm-fwd-name { flex: 1; min-width: 0; font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dm-fwd-check {
	width: 22px;
	height: 22px;
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	border: 2px solid var(--border);
	border-radius: 50%;
	color: transparent;
}
.dm-fwd-row.selected .dm-fwd-check { background: var(--accent); border-color: var(--accent); color: #fff; }

/* ============================================================
   DM contact panel (tap the friend's name / photo in a chat)
   Message / Video / Voice / Follow / More + Pinned messages /
   Pictures / Videos / Shared links
   ============================================================ */
#dmChatPane .dm-info {
	position: absolute;
	inset: 0;
	z-index: 20;
	display: flex;
	flex-direction: column;
	min-height: 0;
	background: var(--bg);
}
.dmi-top { flex-shrink: 0; padding: 10px 12px 0; }
.dmi-back {
	width: 38px; height: 38px;
	display: flex; align-items: center; justify-content: center;
	border: 1px solid var(--border); border-radius: 12px;
	background: var(--surf2); color: var(--text); cursor: pointer;
}
.dmi-back:hover { background: color-mix(in srgb, var(--accent) 16%, var(--surf2)); }
.dmi-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding-bottom: 24px; }
.dmi-head { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 4px 14px 14px; }
.dmi-avatar { width: 96px; height: 96px; border-radius: 50%; overflow: hidden; margin-bottom: 12px; }
.dmi-avatar .msgr-avatar { width: 96px !important; height: 96px !important; }
.dmi-name { font-size: 21px; font-weight: 700; color: var(--text); max-width: 100%; overflow-wrap: anywhere; }
.dmi-handle { margin-top: 3px; font-size: 14px; color: var(--accent-soft); }
/* @handle + clipboard button */
.dmi-handle-row { display: flex; align-items: center; justify-content: center; margin-top: 3px; max-width: 100%; }
.dmi-handle-row .dmi-handle { margin-top: 0; min-width: 0; overflow-wrap: anywhere; }
.dmi-copy-btn {
	margin-left: 10px; width: 28px; height: 28px; flex-shrink: 0; padding: 0; border: 0; border-radius: 50%;
	display: inline-flex; align-items: center; justify-content: center;
	background: transparent; color: var(--muted); cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.dmi-copy-btn:hover, .dmi-copy-btn:focus-visible { background: var(--surf2); color: var(--text); outline: none; }
/* Shared links tab: the link's own preview image in place of the static link icon */
.dmi-row-ico { overflow: hidden; }
.dmi-link-ico.has-og { background: transparent; }
.dmi-link-ico img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Shared links: the site's own favicon on a light disc so dark / transparent logos stay readable. */
.dmi-link-ico.dmi-fav { background: #fff; }
.dmi-link-ico.dmi-fav img { padding: 8px; box-sizing: border-box; object-fit: contain; }
/* contact panel: photo opens the image viewer, name / @handle open the profile page */
.dmi-clickable { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.dmi-name.dmi-clickable:hover, .dmi-handle.dmi-clickable:hover,
.dmi-name.dmi-clickable:focus-visible, .dmi-handle.dmi-clickable:focus-visible { text-decoration: underline; outline: none; }

.dmi-actions { display: flex; justify-content: center; align-items: flex-start; flex-wrap: wrap; gap: 14px; margin-top: 16px; }
.dmi-act { position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px; width: 62px; }
.dmi-act-btn {
	width: 50px; height: 50px; border-radius: 50%;
	display: flex; align-items: center; justify-content: center;
	border: 0; background: var(--surf2); color: var(--text); cursor: pointer;
	transition: background .15s, transform .1s;
}
.dmi-act-btn:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 22%, var(--surf2)); }
.dmi-act-btn:active:not(:disabled) { transform: scale(.94); }
.dmi-act-btn:disabled { opacity: .45; cursor: default; }
.dmi-act-btn.on { background: color-mix(in srgb, var(--accent) 30%, var(--surf2)); color: var(--accent-soft); }
.dmi-act-label { font-size: 12px; font-weight: 600; color: var(--text); white-space: nowrap; }

.dmi-tabs {
	display: flex; border-bottom: 1px solid var(--border);
	position: sticky; top: 0; z-index: 2; background: var(--bg);
	overflow-x: auto; scrollbar-width: none;
}
.dmi-tabs::-webkit-scrollbar { display: none; }
.dmi-tab {
	flex: 1 0 auto; position: relative; background: none; border: 0;
	padding: 13px 14px 12px; font: inherit; font-size: 14px; font-weight: 700;
	color: var(--muted); cursor: pointer; white-space: nowrap;
}
.dmi-tab:hover, .dmi-tab.active { color: var(--text); }
.dmi-tab.active::after {
	content: ''; position: absolute; left: 14px; right: 14px; bottom: -1px;
	height: 3px; border-radius: 3px; background: var(--accent);
}

.dmi-body { padding: 12px 12px 0; }
.dmi-empty { text-align: center; color: var(--muted); font-size: 14px; padding: 40px 12px; }
.dmi-list { display: flex; flex-direction: column; gap: 2px; }
.dmi-row {
	display: flex; align-items: center; gap: 12px; width: 100%;
	padding: 10px 8px; border: 0; border-radius: 10px; background: transparent;
	color: var(--text); font: inherit; text-align: left; text-decoration: none; cursor: pointer;
}
.dmi-row:hover { background: var(--surf2); }
.dmi-row-ico {
	width: 38px; height: 38px; flex-shrink: 0; border-radius: 50%;
	display: flex; align-items: center; justify-content: center;
	background: var(--surf2); color: var(--accent-soft);
}
.dmi-row-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.dmi-row-title { font-size: 14px; font-weight: 700; }
.dmi-row-text { font-size: 13px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dmi-row-time { font-size: 11.5px; color: var(--muted); flex-shrink: 0; }

.dmi-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 3px; }
.dmi-tile {
	position: relative; display: block; aspect-ratio: 1; padding: 0; border: 0;
	background: var(--surf2); overflow: hidden; cursor: pointer; border-radius: 4px;
}
.dmi-tile img, .dmi-tile video { width: 100%; height: 100%; object-fit: cover; display: block; }
.dmi-play {
	position: absolute; right: 6px; bottom: 6px; width: 26px; height: 26px; border-radius: 50%;
	display: flex; align-items: center; justify-content: center;
	background: rgba(0, 0, 0, .6); color: #fff;
}
@media (min-width: 700px) { .dmi-grid { grid-template-columns: repeat(4, 1fr); } }
/* desktop: the chat pane is static in the right column, so make it the containing block for the contact panel */
@media (min-width: 768px) {
	#viewFriends #dmChatPane.fr-chat-pane { position: relative !important; }
}

/* ---------- Visitor (logged-out) mode -- see assets/js/visitor.js ---------- */
.visitor-fade, html.is-visitor [data-visitor-lock] { opacity: .4; pointer-events: none; filter: grayscale(.3); cursor: not-allowed; }
.header-login-btn { height: 34px; padding: 0 16px; border-radius: 999px; border: 0; background: var(--accent, #7c6cff); color: #fff; font-weight: 700; font-size: 13.5px; cursor: pointer; flex-shrink: 0; font-family: inherit; }
/* The login prompt reuses the shared confirm-bg / confirm-box modal (see visitor.js promptLogin). */
.confirm-bg.visitor-confirm { z-index: 1300; }
.visitor-confirm .confirm-box { width: min(340px, 90vw); }
.visitor-confirm .confirm-btns { flex-wrap: wrap; }
.visitor-banner { margin: 10px 14px; padding: 12px 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--surf2); font-size: 13.5px; line-height: 1.45; display: flex; align-items: center; gap: 10px; justify-content: space-between; flex-wrap: wrap; }
.visitor-banner button { padding: 7px 16px; border-radius: 999px; border: 0; background: var(--accent, #7c6cff); color: #fff; font-weight: 700; cursor: pointer; font-family: inherit; }

.sp-suggest-rowwrap { position: relative; }
.sp-suggest-arrow { position: absolute; top: 50%; transform: translateY(-50%); z-index: 3; width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--border); background: var(--surface); color: var(--text); display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 2px 10px rgba(0,0,0,.35); padding: 0; }
.sp-suggest-arrow.prev { left: 6px; }
.sp-suggest-arrow.next { right: 6px; }
.sp-suggest-arrow:hover { background: var(--surf2); }
/* The global button:active { transform: scale(.96) } replaced translateY(-50%), so the arrow jumped down while pressed
   (it looked like a drag). Keep the centring transform in every state and drop the press animation. */
.sp-suggest-arrow, .sp-suggest-arrow:active, .sp-suggest-arrow:focus { transform: translateY(-50%); transition: background .12s ease; }
.sp-suggest-arrow { cursor: pointer; user-select: none; -webkit-user-select: none; -webkit-user-drag: none; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.sp-suggest-arrow svg { pointer-events: none; -webkit-user-drag: none; }

.header-search-mic { align-self: center; margin-right: 6px; border: 0; background: transparent; color: var(--muted); width: 34px; height: 34px; display: flex; align-items: center; justify-content: center; cursor: pointer; border-radius: 50%; padding: 0; flex-shrink: 0; }
.header-search-mic:hover { color: var(--text); }
.header-search-mic.listening { color: #fff; background: #e5484d; animation: beuMicPulse 1.1s ease-in-out infinite; }
@keyframes beuMicPulse { 0%,100% { box-shadow: 0 0 0 0 rgba(229,72,77,.55); } 50% { box-shadow: 0 0 0 8px rgba(229,72,77,0); } }

/* ===== Login on short screens: nothing gets pushed above the top (v86) =====
   .auth-box-wrap centred the card with align-items:center. When the screen is shorter than the card, centring
   overflows equally above AND below, and the part above the top edge can never be scrolled to (the logo
   then also sat on top of "Welcome"). Now the card is centred with auto margins, which centre it when there
   is room and simply start at the top (scrollable) when there isn't. */
.auth-box-wrap {
	align-items: flex-start;
	min-height: min-content;
}
.auth-box { margin-block: auto; }
@media (max-width: 1024px) {
	/* promo video hidden here: keep the card clear of the top-left logo (24px + ~40px mark) */
	.auth-box-wrap { padding-top: 76px; }
}

/* ===== Shorts timeline, edge fix =====
   The hit strip is a little taller (visible line still sits on the very bottom edge) so it is easy to grab right
   at the screen edge, and the knob / hover preview are clamped inside the track (bsky-video-player.js) instead
   of being clipped by the player at 0:00 and at the end. */
.sh-item .bvp-scrub { height: 18px; }
.sh-item.bvp-touch .bvp-scrub, .sh-item .bvp-touch .bvp-scrub { height: 24px; }
.sh-item .bvp-scrub-hit { min-height: 100%; align-items: flex-end; }

/* ===== Shorts follow the Light / Dark theme =====
   The video itself stays on black (that is the picture), but everything around it -- the page behind the stage on a
   wide screen, the search pill + buttons, search results and the comments sheet -- uses the app theme. */
/* v91: in Light appearance the area around the video stays a dark shade (not white); text over the video stays white. */
html[data-auth-theme="light"] .sh-root { background: #0b0b0f; color: #fff; }
html[data-auth-theme="light"] .sh-ambient { filter: blur(60px) saturate(1.5) brightness(.55); }
html[data-auth-theme="light"] .sh-search-panel { color: var(--text); }
html[data-auth-theme="light"] .sh-icon-btn { background: rgba(255, 255, 255, .78); color: var(--text); box-shadow: 0 1px 6px rgba(0, 0, 0, .18); }
html[data-auth-theme="light"] .sh-icon-btn.on { background: var(--accent); color: #fff; }
html[data-auth-theme="light"] .sh-search { background: rgba(255, 255, 255, .82); border-color: rgba(23, 21, 31, .12); color: var(--muted); box-shadow: 0 1px 6px rgba(0, 0, 0, .14); }
html[data-auth-theme="light"] .sh-search:focus-within { background: #fff; border-color: var(--accent); }
html[data-auth-theme="light"] .sh-search input { color: var(--text); }
html[data-auth-theme="light"] .sh-search input::placeholder { color: var(--muted); }
html[data-auth-theme="light"] .sh-search-clear { background: rgba(23, 21, 31, .12); color: var(--text); }
html[data-auth-theme="light"] .sh-search-panel { background: var(--bg); }
html[data-auth-theme="light"] .sh-card { color: var(--text); }
html[data-auth-theme="light"] .sh-card-thumb { background: var(--surf2); }
html[data-auth-theme="light"] .sh-empty { color: var(--muted); }
html[data-auth-theme="light"] .sh-empty-title { color: var(--text); }
html[data-auth-theme="light"] .sh-comments { background: rgba(0, 0, 0, .35); }
html[data-auth-theme="light"] .sh-comments-panel { background: var(--surface); color: var(--text); }
html[data-auth-theme="light"] .sh-comments-head,
html[data-auth-theme="light"] .sh-comments-form { border-color: var(--border); }
html[data-auth-theme="light"] .sh-x { color: var(--text); }
html[data-auth-theme="light"] .sh-c-empty,
html[data-auth-theme="light"] .sh-c-head { color: var(--muted); }
html[data-auth-theme="light"] .sh-c-head b { color: var(--text); }
html[data-auth-theme="light"] .sh-c-btn { color: var(--muted); }
html[data-auth-theme="light"] .sh-c-btn:hover { color: var(--text); }
html[data-auth-theme="light"] .sh-comments-form input { background: var(--surf2); border-color: var(--border); color: var(--text); }
html[data-auth-theme="light"] .sh-comments-form input:focus { border-color: var(--accent); }
/* the sheets that open over Shorts (more / share / quality ...) already use the theme variables; keep their scrim soft */
html[data-auth-theme="light"] .sh-modal-bg { background: rgba(23, 21, 31, .38); }
html[data-auth-theme="light"] .sh-modal { box-shadow: 0 16px 60px rgba(23, 21, 31, .22); }

/* ===== Publish -> Details: caption preview ===== */
.sh-phone-cc { position: absolute; left: 8px; right: 8px; bottom: 34px; padding: 4px 8px; border-radius: 6px; background: rgba(0, 0, 0, .68); color: #fff; font: 700 12.5px/1.3 var(--sans); text-align: center; pointer-events: none; overflow-wrap: anywhere; }
.sh-phone-cc[hidden] { display: none; }
/* word-by-word (live) captions: the box has a fixed width/left (set from the cue's own settings), text centred inside it -- same as the real player. */
.sh-phone-cc { white-space: pre-line; }
.sh-phone-cc.live { right: auto; box-sizing: border-box; text-align: center; }
/* Shorts: transparent handle laid over the caption so it can be dragged up/down (native captions can't take touches).
   Only present while a caption is on screen; the player positions it (see _syncCcLayer in bsky-video-player.js). */
.bvp-ccdrag { position: absolute; left: 6%; right: 6%; z-index: 5; cursor: grab; touch-action: pan-y; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; border-radius: 10px; }
.bvp-ccdrag[hidden] { display: none; }
.bvp-ccdrag.drag { cursor: grabbing; outline: 0; }
.sh-cc-chip-name { cursor: pointer; border-radius: 999px; }
.sh-cc-chip.viewing { box-shadow: inset 0 0 0 1.5px var(--accent); }
.sh-cc-prev { margin: 4px 0 8px; border: 1px solid var(--border, #2a2a32); border-radius: 12px; background: var(--surface, #16161a); overflow: hidden; }
.sh-cc-prev[hidden] { display: none; }
.sh-cc-prev-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 12px; font-size: 12px; border-bottom: 1px solid var(--border, #2a2a32); }
.sh-cc-prev-head span { color: var(--muted, #8a8a96); font-weight: 700; }
.sh-cc-prev-lines { max-height: 150px; overflow-y: auto; overscroll-behavior: contain; position: relative; }
.sh-cc-line { display: flex; gap: 10px; align-items: baseline; width: 100%; padding: 6px 12px; border: 0; background: none; color: var(--text); font: 600 13px/1.35 var(--sans); text-align: left; cursor: pointer; }
.sh-cc-line i { flex: none; min-width: 34px; font-style: normal; font-size: 11.5px; font-weight: 800; color: var(--muted, #8a8a96); font-variant-numeric: tabular-nums; }
.sh-cc-line:hover { background: var(--surf2, #26262c); }
.sh-cc-line.on { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.sh-cc-prev small { display: block; padding: 6px 12px 8px; font-size: 11.5px; color: var(--muted, #8a8a96); border-top: 1px solid var(--border, #2a2a32); }

/* Visitors: the bell is shown faded + inert (no shimmer), and the empty messages in Friends / Calls sit lower now that the banner is gone */
html.is-visitor #headerMentionsBtn .header-bell-icon-wrap::after { content: none; animation: none; }
.visitor-empty { margin-top: 28px; padding: 28px 20px; }

/* ---- Publish: the button itself is the progress bar ---- */
.sh-publish.uploading, .sh-publish.uploading:disabled { --pct: 0%; opacity: 1; cursor: progress; color: #fff; font-variant-numeric: tabular-nums;
	background: linear-gradient(90deg, var(--accent) var(--pct), color-mix(in srgb, var(--accent) 35%, var(--surf2, #26262c)) var(--pct)); transition: background .2s ease; }
.sh-up-foot .sh-publish.uploading { flex: 1 1 auto; width: 100%; margin-left: 0; }
.sh-publish.uploading:active { transform: none; }

/* ---- Minimize ---- */
.sh-up-head-btns { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
.sh-up .sh-min svg, .sh-up .sh-x svg { display: block; }
.sh-modal-bg.min { background: none; backdrop-filter: none; -webkit-backdrop-filter: none; pointer-events: none; }
.sh-modal-bg.min > .sh-modal { display: none; }
.sh-up-chip { display: none; }
.sh-modal-bg.min > .sh-up-chip { --pct: 0%; pointer-events: auto; position: fixed; left: 50%; transform: translateX(-50%); bottom: calc(78px + env(safe-area-inset-bottom, 0px)); z-index: 1251;
	display: flex; align-items: center; gap: 12px; width: min(360px, calc(100vw - 24px)); padding: 10px 12px 13px 10px; border-radius: 14px; overflow: hidden; position: fixed;
	border: 1px solid color-mix(in srgb, var(--text) 12%, transparent); color: var(--text); font: 600 13px var(--sans); cursor: pointer; text-align: left;
	background: color-mix(in srgb, var(--surface) 94%, transparent); -webkit-backdrop-filter: blur(14px) saturate(1.2); backdrop-filter: blur(14px) saturate(1.2);
	box-shadow: 0 8px 28px rgba(0,0,0,.38), 0 1px 3px rgba(0,0,0,.3); animation: shChipIn .22s ease-out; transition: border-color .15s ease, box-shadow .15s ease; }
@media (min-width: 900px) { .sh-modal-bg.min > .sh-up-chip { left: auto; right: 22px; bottom: 22px; transform: none; animation-name: shChipInD; } }
@keyframes shChipIn { from { opacity: 0; transform: translate(-50%, 10px); } to { opacity: 1; transform: translate(-50%, 0); } }
@keyframes shChipInD { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.sh-up-chip:hover { border-color: color-mix(in srgb, var(--text) 22%, transparent); }
.sh-up-chip:active { filter: brightness(.96); }
.sh-up-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sh-up-thumb { position: relative; flex: 0 0 auto; width: 38px; height: 50px; border-radius: 8px; overflow: hidden; display: grid; place-items: center;
	background: color-mix(in srgb, var(--text) 10%, var(--surface)); color: color-mix(in srgb, var(--text) 55%, transparent); }
.sh-up-thumb-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.sh-up-thumb-img[hidden] { display: none; }
.sh-up-chip-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.sh-up-chip-t { font-weight: 600; font-size: 14px; line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sh-up-chip-s { font-weight: 500; font-size: 12px; line-height: 1.2; color: var(--muted, #8a8a96); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sh-up-chip-open { flex: 0 0 auto; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; color: var(--text);
	background: color-mix(in srgb, var(--text) 9%, transparent); transition: background .15s ease; }
.sh-up-chip:hover .sh-up-chip-open { background: color-mix(in srgb, var(--text) 16%, transparent); }
.sh-up-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: color-mix(in srgb, var(--text) 12%, transparent); overflow: hidden; }
.sh-up-bar > i { display: block; height: 100%; width: var(--pct); background: var(--accent); border-radius: 0 2px 2px 0; transition: width .2s ease; }
.sh-up-chip.indet .sh-up-bar > i { width: 38%; animation: shBarSlide 1.2s ease-in-out infinite; }
@keyframes shBarSlide { from { transform: translateX(-100%); } to { transform: translateX(270%); } }
@media (prefers-reduced-motion: reduce) { .sh-up-chip, .sh-up-chip.indet .sh-up-bar > i { animation: none; } }

/* volume popup: hides shortly after the last change even while hovered (see _shFlashVolPop) */
.sh-vol-wrap.vol-hide .sh-vol-pop { display: none !important; }

/* ---- Bottom nav: Instagram-style "filled when selected" icons (v92) ----
   The active tab's outline icon fills solid with the accent colour; inner details
   (home door, shorts play triangle, settings hub) are cut out in the bar's own
   background so the icon keeps its shape. Pure CSS: no markup change. */
.bottom-nav-btn svg,
.bottom-nav-btn svg * {
	transition: fill .18s ease, stroke .18s ease;
}
.bottom-nav-btn.active svg {
	fill: currentColor;
}
.bottom-nav-btn.active svg * {
	fill: currentColor;
}
/* Home: outline house with an arched door; filled solid (door stays cut out) when selected. */
/* Shorts: the play triangle is cut out of the filled phone. */
#navBtnShorts.active svg path {
	fill: var(--bg);
	stroke: var(--bg);
}
/* Calls: handset fills as one solid shape. */
#navBtnCalls.active svg path {
	fill: currentColor;
}
/* Settings: only the gear body between the outline and the centre ring is filled; the centre stays open
   (it is a real hole -- a second sub-path with fill-rule:evenodd -- so it shows whatever is behind the bar). */
#navBtnSettings.active svg path {
	fill: currentColor;
	fill-rule: evenodd;
}
/* Friends: the second person's arcs are strokes only, so they stay as outlines. */
#navBtnFriends.active svg path:nth-of-type(n+2) {
	fill: none;
}

/* ---- Comments popup (v92): YouTube-style, replaces the full-screen Post page ----
   Desktop: centred card over a dimmed page. Phones: bottom sheet. Theme-aware (Dark / Light). */
.cm-overlay {
	position: fixed;
	inset: 0;
	z-index: 1100;
	display: none;
	align-items: center;
	justify-content: center;
	padding: 16px;
	box-sizing: border-box;
	background: rgba(0, 0, 0, .55);
	-webkit-tap-highlight-color: transparent;
}
.cm-overlay.open { display: flex; animation: fadeIn .15s ease; }

.cm-panel {
	position: relative;
	display: flex;
	flex-direction: column;
	width: min(640px, 100%);
	height: min(78vh, 760px);
	max-height: 100%;
	background: var(--surface);
	color: var(--text);
	border: 1px solid var(--border);
	border-radius: 16px;
	box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
	overflow: hidden;
}

.cm-head {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	padding: 14px 12px 12px 20px;
	border-bottom: 1px solid var(--border);
	flex-shrink: 0;
}
.cm-title { margin: 0; font-size: 20px; font-weight: 800; letter-spacing: 0; }
.cm-count { margin-left: 6px; font-size: 16px; font-weight: 500; color: var(--muted); }
.cm-head-actions { display: flex; align-items: center; gap: 2px; }
.cm-iconbtn {
	display: inline-flex; align-items: center; justify-content: center;
	width: 40px; height: 40px;
	background: none; border: 0; border-radius: 50%;
	color: var(--text); cursor: pointer; padding: 0;
}
.cm-iconbtn:hover { background: var(--surf2); }

.cm-sortmenu {
	position: absolute;
	top: calc(100% - 6px);
	right: 52px;
	z-index: 5;
	min-width: 190px;
	padding: 6px 0;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 12px;
	box-shadow: 0 12px 32px rgba(0, 0, 0, .4);
}
.cm-sortmenu[hidden] { display: none; }
.cm-sortitem {
	display: flex; align-items: center; gap: 8px; width: 100%;
	padding: 10px 16px 10px 10px;
	background: none; border: 0; color: var(--text);
	font: inherit; font-size: 14.5px; text-align: left; cursor: pointer;
}
.cm-sortitem:hover { background: var(--border); }
.cm-sortitem-ck { display: inline-flex; width: 20px; color: var(--accent-soft); }

.cm-list {
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: 6px 16px 12px 16px;
}
.cm-loading { padding: 48px 0; text-align: center; color: var(--muted); font-size: 13px; }
.cm-empty { padding: 56px 12px; text-align: center; color: var(--muted); font-size: 14px; line-height: 1.5; }
.cm-empty-t { color: var(--text); font-size: 17px; font-weight: 800; margin-bottom: 4px; }
.cm-textbtn { margin-top: 12px; background: none; border: 0; color: var(--accent-soft); font: inherit; font-weight: 700; cursor: pointer; }

/* the post itself, only when nothing behind the popup shows it */
.cm-ctx {
	margin: 8px 0 10px;
	padding: 10px 12px;
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 12px;
	font-size: 13.5px;
}
.cm-ctx-head { display: flex; align-items: center; gap: 6px; min-width: 0; color: var(--muted); }
.cm-ctx-name { color: var(--text); font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cm-ctx-handle, .cm-ctx-time { white-space: nowrap; }
.cm-ctx-text { margin-top: 6px; line-height: 1.45; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
.cm-ctx-media { margin-top: 6px; }
.cm-ctx-chip { display: inline-block; margin-right: 6px; padding: 2px 8px; border-radius: 999px; background: var(--border); color: var(--muted); font-size: 12px; font-weight: 700; }

/* a comment: [avatar column | body], replies hang under the body and are tied to the avatar by a thread line */
.cm-thread { position: relative; }
.cm-row { display: flex; gap: 12px; padding-top: 12px; }
.cm-avcol { display: flex; flex-direction: column; align-items: center; width: 40px; flex-shrink: 0; }
.cm-row.cm-sm > .cm-avcol { width: 28px; }
.cm-row > .cm-avcol > .cm-av { width: 40px; height: 40px; font-size: 14px; cursor: pointer; }
.cm-row.cm-sm > .cm-avcol > .cm-av { width: 28px; height: 28px; font-size: 11px; }
.cm-vline { flex: 1; width: 2px; min-height: 8px; margin-top: 6px; background: var(--border); border-radius: 1px; }
/* straight vertical thread line (no curved elbows): a top-level line stops above the reply-control button(s) */
.cm-t0 > .cm-row > .cm-avcol > .cm-vline { margin-bottom: 34px; }
.cm-t0.cm-open:has(> .cm-row .cm-more-replies) > .cm-row > .cm-avcol > .cm-vline { margin-bottom: 68px; }
.cm-main { flex: 1; min-width: 0; }
.cm-body { border-radius: 10px; transition: background .4s ease; }
.cm-meta { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: 13px; line-height: 20px; }
.cm-handle { font-weight: 700; color: var(--text); cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cm-time { color: var(--muted); white-space: nowrap; }
.cm-more {
	margin-left: auto; flex-shrink: 0;
	display: inline-flex; align-items: center; justify-content: center;
	width: 32px; height: 32px; margin-top: -4px; margin-right: -6px;
	background: none; border: 0; border-radius: 50%;
	color: var(--text); cursor: pointer; padding: 0;
	transform: rotate(90deg);
}
.cm-more:hover { background: var(--surf2); }
.cm-text { margin-top: 2px; font-size: 14.5px; line-height: 1.5; overflow-wrap: anywhere; word-break: break-word; }
.cm-text a { color: var(--accent-soft); word-break: break-all; }
.cm-media { margin-top: 8px; max-width: 100%; }
.cm-actions { display: flex; align-items: center; gap: 2px; margin: 4px 0 2px -8px; }
.cm-act {
	display: inline-flex; align-items: center; gap: 6px;
	min-height: 32px; padding: 0 10px;
	background: none; border: 0; border-radius: 16px;
	color: var(--text); font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer;
}
.cm-act:hover { background: var(--surf2); }
.cm-like, .cm-dislike { color: var(--muted); }
.cm-like:hover, .cm-dislike:hover { color: var(--text); }
.cm-like.on { color: var(--accent-soft); }
.cm-dislike.on { color: var(--text); }
.cm-ic-down { transform: rotate(180deg); }
.cm-like-n { min-width: 8px; }
.cm-reply { margin-left: 8px; }
.cm-flash { background: color-mix(in srgb, var(--accent) 18%, transparent); }
/* "liked by the post's author": their avatar with a small heart */
.cm-heart { position: relative; display: inline-flex; width: 24px; height: 24px; margin: 0 2px 0 4px; }
.cm-heart-av {
	display: flex; align-items: center; justify-content: center;
	width: 24px; height: 24px; border-radius: 50%; overflow: hidden;
	background: var(--accent); color: #fff; font-size: 9px; font-weight: 800;
}
.cm-heart-av img { width: 100%; height: 100%; object-fit: cover; }
.cm-heart svg {
	position: absolute; right: -5px; bottom: -4px; width: 14px; height: 14px;
	fill: #ff3d5a; stroke: var(--surface); stroke-width: 2.4; paint-order: stroke;
}

/* reply controls: "N replies v" / "Show more replies v" / "Hide replies ^" */
.cm-rowbtn {
	position: relative;
	display: flex; align-items: center; gap: 6px;
	width: max-content; max-width: 100%;
	height: 32px; margin: 2px 0 0 -8px; padding: 0 12px 0 10px;
	background: none; border: 0; border-radius: 16px;
	color: var(--text); font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer;
}
.cm-rowbtn:hover { background: var(--surf2); }
.cm-rowbtn svg { flex-shrink: 0; }
.cm-flip { display: inline-flex; transform: rotate(180deg); }
.cm-t0:not(.cm-open) .cm-replies,
.cm-t0:not(.cm-open) .cm-more-replies,
.cm-t0:not(.cm-open) .cm-hide { display: none; }
.cm-t0.cm-open > .cm-row .cm-toggle { display: none; }

.cm-replies { margin-left: -8px; }
/* footer */
.cm-foot {
	flex-shrink: 0;
	padding: 10px 14px calc(12px + env(safe-area-inset-bottom, 0px)) 16px;
	border-top: 1px solid var(--border);
	background: var(--surface);
}
.cm-replying {
	display: flex; align-items: center; justify-content: space-between;
	margin: 0 0 8px 52px; padding: 4px 4px 4px 10px;
	background: var(--surf2); border-radius: 10px;
	color: var(--muted); font-size: 12.5px;
}
.cm-replying b { color: var(--text); }
.cm-replying-x { display: inline-flex; background: none; border: 0; color: var(--muted); padding: 4px; border-radius: 50%; cursor: pointer; }
.cm-replying-x:hover { background: var(--border); color: var(--text); }
.cm-compose { display: flex; align-items: flex-end; gap: 12px; }
.cm-compose .cm-av { width: 40px; height: 40px; font-size: 14px; }
.cm-input-wrap { position: relative; flex: 1; min-width: 0; border-bottom: 1px solid var(--border); transition: border-color .15s; }
.cm-input-wrap:focus-within { border-bottom-color: var(--accent-soft); }
.cm-input {
	display: block; width: 100%; box-sizing: border-box;
	min-height: 28px; max-height: none; overflow: hidden; overflow-y: hidden; padding: 6px 0 7px;
	background: none; border: 0; outline: 0; resize: none;
	color: var(--text); font: inherit; font-size: 14.5px; line-height: 1.4;
}
.cm-input::placeholder { color: var(--muted); }
.cm-counter { position: absolute; right: 0; top: -16px; font-size: 11px; color: var(--muted); }
.cm-counter.over { color: var(--err); font-weight: 700; }
.cm-attach { width: 36px; height: 36px; color: var(--muted); }
.cm-send {
	display: inline-flex; align-items: center; justify-content: center;
	width: 38px; height: 38px; padding: 0; flex-shrink: 0;
	background: var(--accent); color: #fff; border: 0; border-radius: 50%; cursor: pointer;
	transition: opacity .15s, transform .1s;
}
.cm-send:hover:not(:disabled) { transform: scale(1.05); }
.cm-send:disabled { opacity: .35; cursor: default; }
.cm-login {
	display: block; width: 100%; padding: 11px 14px;
	background: var(--surf2); color: var(--text); border: 1px solid var(--border); border-radius: 999px;
	font: inherit; font-size: 14px; font-weight: 700; cursor: pointer;
}

/* phones: bottom sheet */
@media (max-width: 640px) {
	.cm-overlay { align-items: flex-end; padding: 0; }
	.cm-panel {
		width: 100%;
		height: 85dvh;
		max-height: 100dvh;
		border-radius: 16px 16px 0 0;
		border-left: 0; border-right: 0; border-bottom: 0;
		animation: cmSheetUp .22s ease;
	}
	.cm-head { padding: 12px 6px 10px 16px; }
	.cm-list { padding: 4px 12px 10px 12px; }
	.cm-title { font-size: 18px; }
}
@keyframes cmSheetUp { from { transform: translateY(40px); opacity: 0; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .cm-panel, .cm-overlay.open { animation: none; } }

/* ============================================================
   Page titles: "Friends", "RECENT CALLS" and "Settings" sit at the same spot on every tab.
   Same left edge (the page column's edge) and the same vertical centre: a 38px title row that starts 4px below
   the content top -- exactly the Settings bar (4px padding + 38px back button). The space BELOW each title row is
   unchanged, so the lists under them do not move.
   ============================================================ */
.fr-head { padding: 4px 0 18px; }
.fr-head .fr-title { min-height: 38px; display: flex; align-items: center; }

/* ---- Friends tab with no friends: no chat pane, the message sits centred on the page ---- */
.fr-view.fr-no-friends .fr-empty-pane { display: none !important; }
.fr-view.fr-no-friends .fr-list-pane { flex: 1 1 auto; padding-right: 0; border-right: 0; }
.fr-view.fr-no-friends .fr-scroll { display: flex; flex-direction: column; }
.fr-view.fr-no-friends #friendsListSection { margin: auto 0; padding-bottom: 60px; }   /* auto margins centre it in the space under the title; the padding lifts it to about the page's middle */
.fr-view.fr-no-friends #friendsList .empty,
.fr-view.fr-no-friends #friendsList .visitor-empty { max-width: 460px; margin: 0 auto; padding: 20px; font-size: 14px; line-height: 1.5; }

/* v102/v109: the visitor note on Friends and on Calls are one style: same box, position, size, weight, colour */
.fr-view.fr-no-friends #friendsListSection:has(.visitor-empty) { margin: 0; padding-bottom: 0; }
.fr-view.fr-no-friends #friendsList .visitor-empty,
#callsHistoryList .visitor-empty {
	max-width: none; margin: 28px 0 0; padding: 28px 20px;
	font: 400 12.5px/1.4 var(--sans, inherit); letter-spacing: normal; text-transform: none;
	color: var(--muted); text-align: center;
}
/* Friends / Recent calls titles: one style (the .fr-head row is shared) */
#viewCalls .fr-head { padding: 4px 0 18px; }
#viewCalls .fr-head .fr-title { min-height: 38px; display: flex; align-items: center; margin: 0; }

/* ---- v109: Shorts speaker + volume pill ----
   (1) The merged "speaker + slider" look only applies while the slider is actually showing. Before, a click made the
       slider auto-hide (.vol-hide) while :hover / .open kept the button squared-off and dark, so the two looked split.
   (2) Light appearance: the pill stays light (it went dark on hover, hiding the dark icon). */
.sh-vol-wrap:not(.vol-hide):hover .sh-icon-btn,
.sh-vol-wrap:not(.vol-hide):has(:focus-visible) .sh-icon-btn,
.sh-vol-wrap:not(.vol-hide).open .sh-icon-btn { border-radius: 20px 20px 0 0; }
.sh-vol-wrap.vol-hide .sh-icon-btn { border-radius: 50%; }
.sh-vol-wrap.vol-hide:hover .sh-icon-btn { background: rgba(30, 30, 36, .62); }
html[data-auth-theme="light"] .sh-vol-wrap:hover .sh-icon-btn,
html[data-auth-theme="light"] .sh-vol-wrap:has(:focus-visible) .sh-icon-btn,
html[data-auth-theme="light"] .sh-vol-wrap.open .sh-icon-btn,
html[data-auth-theme="light"] .sh-vol-wrap.vol-hide:hover .sh-icon-btn { background: rgba(255, 255, 255, .78); color: var(--text); }
html[data-auth-theme="light"] .sh-vol-pop { background: rgba(255, 255, 255, .78); box-shadow: 0 3px 6px rgba(0, 0, 0, .18); }
html[data-auth-theme="light"] .sh-vol-track::before { background: rgba(23, 21, 31, .22); }
html[data-auth-theme="light"] .sh-vol-fill { background: var(--accent); }
html[data-auth-theme="light"] .sh-vol-thumb { background: var(--accent); box-shadow: 0 0 0 2px rgba(255, 255, 255, .9); }

/* ---- v111: Shorts rail caption (CC) button sits above the view count ---- */
.sh-act[hidden] { display: none; }
.sh-act-cc.on { color: var(--accent-soft); }
.sh-item[data-far="1"] { background: #000; }

/* ---- live sync (assets/js/live.js) ---- */
.live-pill{position:fixed;left:50%;top:calc(env(safe-area-inset-top,0px) + 64px);transform:translate(-50%,-12px);z-index:9998;border:0;border-radius:999px;padding:9px 16px;font:600 13px/1 inherit;background:var(--accent,#5850E9);color:#fff;box-shadow:0 6px 20px rgba(0,0,0,.28);opacity:0;pointer-events:none;transition:opacity .2s,transform .2s;cursor:pointer}
.live-pill.show{opacity:1;pointer-events:auto;transform:translate(-50%,0)}
/* the "New short" pill belongs to the Shorts section only -- never show it over Home / Friends / Calls / Settings */
body:not(.shorts-active) #shLivePill{display:none!important}
@keyframes liveIn{from{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:none}}
.sp-post.live-in{animation:liveIn .35s ease-out}
@media (prefers-reduced-motion:reduce){.sp-post.live-in{animation:none}.live-pill{transition:none}}

/* ---- v119: Shorts speaker + volume = ONE pill (like the system volume flyout): speaker on top, slider below ----
   The pill's background now lives on one shared element (.sh-vol-wrap::before) that grows downward, so the speaker and the
   slider can never drift apart -- not on press (the old press-shrink opened a gap), not while dragging. Both children are
   transparent while merged. Merged = hover / keyboard focus / .open / .pressing (finger or mouse button down). */
.sh-vol-wrap { z-index: 26; }
.sh-vol-wrap::before { content: ""; position: absolute; left: 50%; top: 0; width: 40px; height: 40px; margin-left: -20px; border-radius: 20px; z-index: 0;
	background: rgba(30, 30, 36, .62); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); pointer-events: none;
	transition: height .18s cubic-bezier(.2,.8,.3,1), background .15s ease; }
.sh-vol-wrap .sh-icon-btn { position: relative; z-index: 1; }
.sh-vol-wrap .sh-icon-btn:active { transform: none; }
.sh-vol-wrap .sh-icon-btn:active svg { transform: scale(.88); }
.sh-vol-wrap .sh-icon-btn svg { transition: transform .1s ease; }
.sh-vol-wrap:not(.vol-hide):hover::before,
.sh-vol-wrap:not(.vol-hide):has(:focus-visible)::before,
.sh-vol-wrap:not(.vol-hide).open::before,
.sh-vol-wrap.pressing::before { height: 144px; background: rgba(30, 30, 36, .8); }
.sh-vol-wrap:not(.vol-hide):hover .sh-icon-btn,
.sh-vol-wrap:not(.vol-hide):has(:focus-visible) .sh-icon-btn,
.sh-vol-wrap:not(.vol-hide).open .sh-icon-btn,
.sh-vol-wrap.pressing .sh-icon-btn { background: transparent; box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; border-radius: 50%; }
.sh-vol-wrap .sh-vol-pop { background: transparent; box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; z-index: 1; }
.sh-vol-wrap.pressing .sh-vol-pop { display: block !important; }
.sh-vol-wrap.vol-hide:not(.pressing) .sh-icon-btn { background: rgba(30, 30, 36, .62); }
/* slider: thin track, bigger knob, accent fill (as in the reference) */
.sh-vol-track::before { width: 4px; margin-left: -2px; background: rgba(255, 255, 255, .28); }
.sh-vol-fill { background: var(--accent); }
.sh-vol-thumb { width: 14px; height: 14px; margin-left: -7px; background: #fff; box-shadow: 0 1px 4px rgba(0, 0, 0, .45); transition: transform .1s ease; }
.sh-vol-wrap.pressing .sh-vol-thumb { transform: scale(1.18); }
/* light appearance */
html[data-auth-theme="light"] .sh-vol-wrap::before { background: rgba(255, 255, 255, .78); box-shadow: 0 1px 6px rgba(0, 0, 0, .18); }
html[data-auth-theme="light"] .sh-vol-wrap:not(.vol-hide):hover::before,
html[data-auth-theme="light"] .sh-vol-wrap:not(.vol-hide).open::before,
html[data-auth-theme="light"] .sh-vol-wrap.pressing::before { background: rgba(255, 255, 255, .92); }
html[data-auth-theme="light"] .sh-vol-wrap:not(.vol-hide):hover .sh-icon-btn,
html[data-auth-theme="light"] .sh-vol-wrap:not(.vol-hide).open .sh-icon-btn,
html[data-auth-theme="light"] .sh-vol-wrap.pressing .sh-icon-btn { background: transparent; color: var(--text); box-shadow: none; }
html[data-auth-theme="light"] .sh-vol-wrap .sh-vol-pop { background: transparent; box-shadow: none; }
html[data-auth-theme="light"] .sh-vol-thumb { background: var(--accent); box-shadow: 0 0 0 2px rgba(255, 255, 255, .9); }

/* ---- v119: ONE seek bar everywhere. Post videos (.vscrub, see scrubber.js) and the feed/promo player (.beu-ctl-progress)
   now look and behave exactly like the Shorts bar: a thin white line that thickens under the pointer, a round knob and a
   time bubble while you drag. The .bvp-* rules above give the structure; these give the Shorts look. ---- */
.sp-video.vscrub { position: relative; }
.vscrub .vs-ui { position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: flex-end; line-height: 1.2; color: #fff; pointer-events: none; }
.vscrub .vs-ui > * { pointer-events: auto; }
.vscrub .vs-big { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 56px; height: 56px; border-radius: 50%; border: 0; display: grid; place-items: center; color: #fff;
	background: rgba(0, 0, 0, .5); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); cursor: pointer; opacity: 1; transition: opacity .15s ease, transform .15s ease; }
.vscrub.vs-playing .vs-big { opacity: 0; pointer-events: none; transform: translate(-50%, -50%) scale(.85); }
.vscrub .vs-bar { width: 100%; background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,.55)); }
.vscrub .vs-row { display: flex; align-items: center; gap: 4px; padding: 0 6px; height: 34px; opacity: 0; transition: opacity .2s ease; }
.vscrub.vs-show .vs-row, .vscrub:hover .vs-row { opacity: 1; }
.vscrub .vs-flex { flex: 1; }
.vscrub .vs-time { font: 600 12px var(--sans); font-variant-numeric: tabular-nums; opacity: .95; margin-left: 2px; }
.vscrub .vs-btn { width: 34px; height: 34px; border: 0; background: none; color: #fff; display: grid; place-items: center; cursor: pointer; border-radius: 50%; padding: 0; }
.vscrub .vs-btn:hover { background: rgba(255, 255, 255, .14); }
.vscrub .bvp-scrub { padding: 0; height: 18px; }
.vscrub.bvp-touch .bvp-scrub { height: 30px; }
.vscrub .bvp-scrub-hit { align-items: flex-end; padding: 0; }
.vscrub .bvp-track { height: 3px; border-radius: 0; background: rgba(255, 255, 255, .3); }
.vscrub .bvp-scrub.hover .bvp-track, .vscrub .bvp-scrub.active .bvp-track { height: 5px; }
.vscrub .bvp-fill { background: #fff; }
.vscrub .bvp-knob { top: auto; bottom: -6.5px; margin-top: 0; }
.vscrub:fullscreen { display: flex; align-items: center; justify-content: center; max-width: none; border-radius: 0; border: 0; }
.vscrub:fullscreen video { max-height: 100vh; height: 100%; }

/* feed / promo player progress -> the Shorts look */
.beu-ctl-progress { height: 3px; border-radius: 0; background: rgba(255, 255, 255, .3); transition: height .1s ease; touch-action: none; }
.beu-ctl-progress::after { content: ""; position: absolute; left: 0; right: 0; top: -12px; bottom: -12px; }   /* bigger grab area, like the Shorts bar */
.beu-ctl-progress:hover, .beu-ctl-progress:active { height: 5px; }
.beu-ctl-buffered { background: rgba(255, 255, 255, .22); border-radius: 0; }
.beu-ctl-played { background: #fff; border-radius: 0; }
.beu-ctl-knob { width: 14px; height: 14px; background: var(--accent); box-shadow: none; transform: translate(-50%, -50%) scale(0); transition: transform .1s ease; }
.beu-ctl-progress:hover .beu-ctl-knob, .beu-ctl-progress:focus-visible .beu-ctl-knob { transform: translate(-50%, -50%) scale(.6); }
.beu-ctl-progress:active .beu-ctl-knob { transform: translate(-50%, -50%) scale(1); }
.beu-ctl-progress:focus-visible { box-shadow: none; }

/* ---- v120: speaker button shade fix ----
   The shared pill background (.sh-vol-wrap::before) was always drawn, even when the slider was closed, and the closed
   state forced a dark button -- so after a tap the light-theme speaker turned into a dark disc (and the dark/light
   fills stacked). Now the shared background exists ONLY while merged; when closed the button is its normal self
   (white in light theme, dark glass in dark theme), pressed or not. */
.sh-vol-wrap::before { opacity: 0; transition: height .18s cubic-bezier(.2,.8,.3,1), opacity .12s ease, background .15s ease; }
.sh-vol-wrap:not(.vol-hide):hover::before,
.sh-vol-wrap:not(.vol-hide):has(:focus-visible)::before,
.sh-vol-wrap:not(.vol-hide).open::before,
.sh-vol-wrap.pressing::before { opacity: 1; }
.sh-vol-wrap.vol-hide:not(.pressing) .sh-icon-btn,
.sh-vol-wrap.vol-hide:not(.pressing):hover .sh-icon-btn { background: rgba(30, 30, 36, .62); color: #fff; }
html[data-auth-theme="light"] .sh-vol-wrap.vol-hide:not(.pressing) .sh-icon-btn,
html[data-auth-theme="light"] .sh-vol-wrap.vol-hide:not(.pressing):hover .sh-icon-btn { background: rgba(255, 255, 255, .78); color: var(--text); box-shadow: 0 1px 6px rgba(0, 0, 0, .18); }
/* a button that has been clicked keeps :active / focus shading in some browsers -- never darken it */
.sh-vol-wrap .sh-icon-btn:focus:not(:focus-visible), .sh-vol-wrap .sh-icon-btn:active { outline: none; filter: none; }
html[data-auth-theme="light"] .sh-vol-wrap .sh-icon-btn:not(:hover):not(.on) { color: var(--text); }

/* Shorts top bar (speaker / search / plus): slowly fades out when the mouse leaves the video area and
   slowly fades back in when it returns. JS (shorts.js, "top bar auto-hide") toggles .sh-idle on .sh-stage
   for mouse pointers only; touch screens keep the bar visible. It stays visible while the search box is
   focused, the search panel is open or the volume slider is open. */
.sh-topbar {
	transition: opacity .9s ease, transform .9s ease;
}
.sh-stage.sh-idle .sh-topbar {
	opacity: 0;
	transform: translateY(-6px);
}
.sh-stage.sh-idle .sh-topbar > * { pointer-events: none; }
.sh-stage.sh-idle:has(.sh-search:focus-within) .sh-topbar,
.sh-stage.sh-idle:has(.sh-search-panel:not([hidden])) .sh-topbar,
.sh-stage.sh-idle:has(.sh-vol-wrap.open) .sh-topbar,
.sh-stage.sh-idle:has(.sh-search-mic.listening) .sh-topbar {
	opacity: 1;
	transform: none;
}
.sh-stage.sh-idle:has(.sh-search:focus-within) .sh-topbar > *,
.sh-stage.sh-idle:has(.sh-search-panel:not([hidden])) .sh-topbar > *,
.sh-stage.sh-idle:has(.sh-vol-wrap.open) .sh-topbar > *,
.sh-stage.sh-idle:has(.sh-search-mic.listening) .sh-topbar > * { pointer-events: auto; }
@media (prefers-reduced-motion: reduce) {
	.sh-topbar { transition: opacity .2s linear; }
}

/* Post send button: round paper-plane button, same look as the comment send button (.cm-send). */
.btn.primary.sp-submit,
.sp-composer-bar > .sp-submit,
.sp-modal-headslot .sp-submit {
	width: 38px; height: 38px; min-width: 38px; padding: 0; flex-shrink: 0;
	border-radius: 50%; border: 0;
	background: var(--accent); color: #fff;
	display: inline-flex; align-items: center; justify-content: center;
	transition: opacity .15s, transform .1s;
}
.btn.primary.sp-submit:hover:not(:disabled) { transform: scale(1.05); filter: none; }
.btn.primary.sp-submit:disabled { opacity: .35; }

/* Post audience dropdown: native select, same look as the Settings "Public" dropdown (.st-vis / .st-vis-sel).
   No leading icon; the select is sized to its selected label by fitVis() in social.js, and the open list's items get matching side padding. */
.sp-vis-native { flex-shrink: 0; }
.sp-vis-native .sp-vis-ico { display: none; }
.sp-vis-native .st-vis-sel { min-width: 0; max-width: none; field-sizing: fixed; padding: 6px 20px 6px 8px; background-position: right 6px center; background-size: 10px 10px; text-overflow: clip; white-space: nowrap; overflow: hidden; }
.sp-vis-native .st-vis-sel option { padding: 6px 12px; width: auto; }
/* Picking an item hands focus back to the select; that must not restyle the box (accent border) -- only a real hover does. */
.sp-vis-native .st-vis-sel:focus-visible:not(:hover) { border-color: var(--border); }

/* Open list of the audience select (see spBindWidthLockedSelect in social.js): its width is set in JS to the
   select's own width and never grows; labels that don't fit are cut with "..." (full text in the tooltip). */
.sp-sel-pop {
	position: fixed; z-index: 2147483000;
	box-sizing: border-box; max-width: calc(100vw - 8px);
	margin: 0; padding: 4px; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain;
	background: var(--surf2); border: 1px solid var(--border); border-radius: var(--sp-r, 0);
	box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
	font: 600 12.5px var(--sans); color: var(--text);
}
.sp-sel-opt {
	display: block; box-sizing: border-box; width: 100%; min-width: 0;
	padding: 7px 10px; cursor: pointer; user-select: none; border-radius: var(--sp-ri, 0);
	white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sp-sel-opt.on { color: var(--accent); }
.sp-sel-opt.hl { background: color-mix(in srgb, var(--accent) 18%, var(--surf2)); }
.sp-sel-opt.off { opacity: .45; cursor: not-allowed; }

/* Shorts upload: rotate button over the video preview (top right). */
.sh-up-side { position: relative; }
.sh-rot-btn {
	position: absolute; top: 8px; right: 8px; z-index: 3;
	width: 36px; height: 36px; padding: 0; border: 0; border-radius: 50%;
	display: inline-flex; align-items: center; justify-content: center;
	background: rgba(0, 0, 0, .55); color: #fff; backdrop-filter: blur(4px);
	cursor: pointer; transition: transform .15s, background .15s;
}
.sh-rot-btn:hover { background: rgba(0, 0, 0, .75); transform: rotate(45deg); }
.sh-rot-btn:active { transform: rotate(90deg) scale(.94); }
.sh-rot-btn[hidden] { display: none; }

/* Send buttons (post + comment): progress ring hugging the button's edge instead of a "n/300" counter. */
.sp-send-wrap, .cm-send-wrap { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; flex: 0 0 38px; }
.sp-send-ring { position: absolute; left: 50%; top: 50%; width: 46px; height: 46px; margin: -23px 0 0 -23px; pointer-events: none; overflow: visible; }
.sp-send-ring-bg { fill: none; stroke: var(--border); stroke-width: 2.4; }
.sp-send-ring-fg { fill: none; stroke: var(--accent-soft); stroke-width: 2.4; stroke-linecap: round; transition: stroke-dashoffset .12s linear, stroke .15s; }
.sp-send-wrap.warn .sp-send-ring-fg, .cm-send-wrap.warn .sp-send-ring-fg { stroke: var(--warn); }
.sp-send-wrap.over .sp-send-ring-fg, .cm-send-wrap.over .sp-send-ring-fg { stroke: var(--err); }
.sp-send-wrap.busy .sp-send-ring-fg { stroke-dasharray: 34 98; stroke-dashoffset: 0; animation: sp-send-spin .8s linear infinite; transform-origin: 23px 23px; }
@keyframes sp-send-spin { from { transform: rotate(-90deg); } to { transform: rotate(270deg); } }
.sp-composer-bar > .sp-send-wrap { margin: 0 4px 0 6px; }
.sp-modal-headslot .sp-send-wrap { margin-right: 4px; }

/* Quick-reaction bar above a DM message's three-dot button (Telegram-style); positioned inline by openDmMsgMenu() */
.dm-react-bar {
	position: fixed;
	z-index: 1251;
	display: flex;
	align-items: center;
	gap: 2px;
	padding: 5px 6px;
	max-width: calc(100vw - 16px);
	border-radius: 999px;
	background: var(--surface);
	border: 1px solid var(--border);
	box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
	animation: fadeIn .12s ease;
}
.dm-react-btn {
	width: 36px;
	height: 36px;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 21px;
	line-height: 1;
	border: none;
	border-radius: 50%;
	background: transparent;
	cursor: pointer;
	transition: transform .12s, background .12s;
}
.dm-reply-text .dm-snip-ico, .dm-pinned-text .dm-snip-ico, .dm-fwd-preview .dm-snip-ico, .dmi-row-text .dm-snip-ico, #dmReplyText .dm-snip-ico { vertical-align: -2px; margin-right: 5px; flex: none; }
.dm-react-btn:hover { background: var(--surf2); transform: scale(1.18); }
.dm-react-btn.on { background: color-mix(in srgb, var(--accent) 24%, transparent); }

/* "Forwarded from <profile> <username>" line at the top of a forwarded friend-chat message */
#dmChatPane .dm-fwd {
	flex: 1 1 100%;
	min-width: 0;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 4px 6px;
	margin: 0 0 4px;
	font-size: 12.5px;
	line-height: 1.3;
	color: color-mix(in srgb, var(--text) 62%, transparent);
}
#dmChatPane .dm-fwd.clickable { cursor: pointer; }
#dmChatPane .dm-fwd-label { font-style: italic; }
#dmChatPane .dm-fwd-av {
	width: 18px; height: 18px; flex: none;
	border-radius: 50%; overflow: hidden;
	display: inline-flex; align-items: center; justify-content: center;
	background: var(--surf2); font-size: 9px; font-weight: 700; color: var(--text);
}
#dmChatPane .dm-fwd-av img, #dmChatPane .dm-fwd-av .emoji-avatar { width: 100%; height: 100%; object-fit: cover; }
#dmChatPane .dm-fwd-name { font-weight: 700; color: var(--accent-soft, var(--accent)); overflow-wrap: anywhere; }
#dmChatPane .dm-fwd.clickable:hover .dm-fwd-name { text-decoration: underline; }
#dmChatPane .dm-fwd-handle { opacity: .85; overflow-wrap: anywhere; }

/* Post-style link card (thumbnail / title / description / domain) under a link in a friend-chat bubble */
#dmChatPane .club-msg-bubble > .dm-linkcard {
	flex: 1 1 100%;
	width: min(320px, 68vw);
	max-width: 100%;
	margin: 6px 0 2px;
	border-radius: 10px;
}
#dmChatPane .dm-linkcard .sp-lc-title { font-size: 14px; }
#dmChatPane .dm-linkcard .sp-lc-media.portrait { max-height: 380px; }

/* Friend-chat bubbles hug their content: no empty row for the hidden reaction button, width = text + time. */
#dmChatPane .club-msg-bubble { width: fit-content; min-width: 0; max-width: 100%; box-sizing: border-box; }
#dmChatPane .club-msg-reactions:empty { display: none; }
#dmChatPane .club-msg-text, #dmChatPane .club-msg-caption { white-space: pre-wrap; overflow-wrap: anywhere; }

/* Three-dot inside the bubble, in the bottom row before the time (sent + received) */
#dmChatPane .club-msg-time .dm-msg-more-btn {
	position: relative;
	width: 18px;
	height: 16px;
	padding: 0;
	margin: -2px 1px 0 0;
	border-radius: 6px;
	opacity: .6;
}
#dmChatPane .club-msg-time .dm-msg-more-btn::after { content: ""; position: absolute; inset: -8px -6px; }
#dmChatPane .club-msg:hover .dm-msg-more-btn,
#dmChatPane .club-msg-time .dm-msg-more-btn:hover,
#dmChatPane .club-msg-time .dm-msg-more-btn:focus-visible { opacity: 1; background: transparent; }

/* Message box: grows upward line by line (300 chars never needs a scrollbar); any overflow scrolls unseen */
#dmChatPane .dm-input-wrap .club-msg-input {
	max-height: 240px;
	overflow-y: hidden;
	scrollbar-width: none;
	-ms-overflow-style: none;
}
#dmChatPane .dm-input-wrap .club-msg-input::-webkit-scrollbar { display: none; width: 0; height: 0; }

/* Send button: progress ring (n / 300 characters) around it */
.dm-send-wrap { position: relative; width: 46px; height: 46px; flex-shrink: 0; margin-right: 4px; }
.dm-send-ring { position: absolute; left: -4px; top: -4px; width: 54px; height: 54px; pointer-events: none; overflow: visible; }
.dm-ring-bg { fill: none; stroke: var(--border); stroke-width: 2.4; }
.dm-ring-fg { fill: none; stroke: var(--accent-soft, var(--accent)); stroke-width: 2.6; stroke-linecap: round; transition: stroke-dashoffset .12s linear, stroke .15s; }
.dm-send-wrap.warn .dm-ring-fg { stroke: var(--warn, #f5a524); }
.dm-send-wrap.over .dm-ring-fg { stroke: var(--err); }
.dm-send-wrap .dm-send-btn { width: 46px; height: 46px; }
.dm-char-left {
	position: absolute;
	right: -2px;
	top: -14px;
	min-width: 20px;
	padding: 1px 5px;
	border-radius: 999px;
	background: var(--surf2);
	border: 1px solid var(--border);
	font: 700 11px/1.3 var(--mono, monospace);
	color: var(--warn, #f5a524);
	text-align: center;
	pointer-events: none;
}
.dm-send-wrap.over .dm-char-left { color: var(--err); }

/* "Add people" button in the call panel header -- sits at the far top-right, after Minimize */
.call-addmember-top-btn {
	margin-left: 8px;
	margin-right: 2px;
}
.call-panel-header-right {
	display: flex;
	align-items: center;
	gap: 4px;
}

/* DM message ticks: a small pop when a tick changes state live (sent -> delivered -> read) */
#dmChatPane .dm-tick { transition: color .25s ease; }
#dmChatPane .dm-tick-flip { animation: dmTickPop .28s ease-out; }
@keyframes dmTickPop {
	0%   { transform: scale(.55); opacity: .4; }
	60%  { transform: scale(1.18); opacity: 1; }
	100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
	#dmChatPane .dm-tick-flip { animation: none; }
}

/* ---- Telegram-style "send photo" sheet (showImageSendDialog in ui.js) ---- */
.img-send-bg { z-index: 1250; }
.img-send-box {
	width: min(460px, 94vw);
	max-height: 92vh;
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding: 14px;
	background: var(--surface, #17171c);
	border: 1px solid var(--border);
	border-radius: 16px;
	box-shadow: 0 18px 60px rgba(0,0,0,.55);
	animation: dmTickPop .22s ease-out;
}
.img-send-head { display: flex; align-items: center; justify-content: space-between; }
.img-send-title { font-weight: 700; font-size: 15px; color: var(--text); }
.img-send-x { width: 28px; height: 28px; border-radius: 50%; border: 0; background: var(--surf2); color: var(--text); display: flex; align-items: center; justify-content: center; cursor: pointer; }
.img-send-x:hover { background: color-mix(in srgb, var(--accent) 16%, var(--surf2)); }
.img-send-preview {
	display: flex; align-items: center; justify-content: center;
	background: var(--surf2); border-radius: 12px; overflow: hidden;
	min-height: 120px;
}
.img-send-preview img { display: block; max-width: 100%; max-height: min(58vh, 460px); object-fit: contain; }
.img-send-caption {
	width: 100%; box-sizing: border-box; resize: none; overflow-y: auto;
	min-height: 40px; max-height: 120px; padding: 10px 12px;
	border-radius: 12px; border: 1px solid var(--border);
	background: var(--surf2); color: var(--text);
	font: inherit; font-size: 15px; line-height: 1.4; outline: none;
}
.img-send-caption:focus { border-color: var(--accent); }
.img-send-foot { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }

/* Drop target hint while a file is dragged over the DM chat */
.dm-drop-overlay {
	position: fixed; z-index: 1100; display: none; align-items: center; justify-content: center;
	pointer-events: none; box-sizing: border-box;
	background: color-mix(in srgb, var(--accent, #7c6cff) 14%, rgba(13,13,15,.72));
	border: 2px dashed var(--accent, #7c6cff); border-radius: 14px;
	backdrop-filter: blur(2px);
}
.dm-drop-card { display: flex; flex-direction: column; align-items: center; gap: 8px; color: var(--text); font-weight: 700; font-size: 15px; }

/* Image bubbles in a DM: bigger picture, caption underneath, then time + ticks (like Telegram) */
#dmChatPane .club-msg-image { max-width: min(320px, 100%); max-height: 380px; width: auto; border-radius: 8px; }
#dmChatPane .club-msg-caption { font-size: 15px; line-height: 1.4; margin-top: 6px; }

/* ---- Photo albums: the mosaic used by the picker preview AND the chat bubble ---- */
.album-grid { display: flex; flex-direction: column; gap: 2px; width: 100%; }
.album-row { display: flex; gap: 2px; height: var(--album-h1, 210px); }
.album-row.c2 { height: var(--album-h2, 108px); }
.album-row.c3 { height: var(--album-h3, 108px); }
.album-grid.n2 .album-row { height: var(--album-h2n, 160px); }   /* two photos stacked */
.album-grid.n1 .album-row { height: auto; }
.album-grid.n1 .album-cell img { height: auto; max-height: 320px; }
.album-cell { position: relative; flex: 1 1 0; min-width: 0; overflow: hidden; background: var(--surf2); border-radius: 3px; }
.album-cell img, .album-img { display: block; width: 100%; height: 100%; object-fit: cover; cursor: zoom-in; }
.album-row:first-child .album-cell:first-child { border-top-left-radius: 7px; }
.album-row:first-child .album-cell:last-child { border-top-right-radius: 7px; }
.album-row:last-child .album-cell:first-child { border-bottom-left-radius: 7px; }
.album-row:last-child .album-cell:last-child { border-bottom-right-radius: 7px; }
.club-msg-album { width: 320px; max-width: 100%; --album-h1: 210px; --album-h2: 108px; --album-h3: 108px; --album-h2n: 160px; }

/* send sheet: several photos */
.img-send-preview.multi { display: block; max-height: min(52vh, 440px); overflow-y: auto; padding: 0; background: transparent; --album-h1: 220px; --album-h2: 170px; --album-h3: 118px; }
.img-send-preview.multi .album-img, .img-send-preview.multi img { cursor: default; }
.album-x {
	position: absolute; top: 6px; right: 6px; width: 24px; height: 24px; border-radius: 50%;
	border: 0; display: flex; align-items: center; justify-content: center; cursor: pointer;
	background: rgba(0,0,0,.6); color: #fff; opacity: .92;
}
.album-x:hover { background: rgba(220,60,60,.9); opacity: 1; }

/* send sheet: Group items / Send as documents */
.img-send-opts { display: flex; flex-direction: column; gap: 6px; }
.img-send-opt { display: flex; align-items: center; gap: 10px; cursor: pointer; font-size: 14.5px; color: var(--text); user-select: none; }
.img-send-opt input { width: 18px; height: 18px; accent-color: var(--accent, #7c6cff); cursor: pointer; margin: 0; }
.img-send-add { margin-right: auto; }
.img-send-add:disabled { opacity: .45; cursor: not-allowed; }
.img-send-count { font-size: 12px; color: var(--muted); }

/* ---- DM "Jump to bottom" button ---- */
.dm-messages-wrap { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.dm-messages-wrap > .club-messages { flex: 1; min-height: 0; }
.dm-jump-btn {
	position: absolute; right: 14px; bottom: 12px; z-index: 5;
	width: 42px; height: 42px; border-radius: 50%;
	display: flex; align-items: center; justify-content: center;
	background: var(--surf2); color: var(--text);
	border: 1px solid var(--border); box-shadow: 0 4px 16px rgba(0,0,0,.35);
	cursor: pointer; padding: 0;
	opacity: 0; transform: translateY(10px) scale(.88); pointer-events: none;
	transition: opacity .18s ease, transform .18s ease, background .15s ease;
}
.dm-jump-btn.show { opacity: 1; transform: none; pointer-events: auto; }
.dm-jump-btn:hover { background: color-mix(in srgb, var(--accent) 18%, var(--surf2)); }
.dm-jump-btn:active { transform: scale(.94); }
.dm-jump-badge {
	position: absolute; top: -7px; left: 50%; transform: translateX(-50%);
	min-width: 20px; height: 20px; padding: 0 6px; box-sizing: border-box; border-radius: 10px;
	background: var(--accent, #7c6cff); color: #fff;
	font-size: 11.5px; font-weight: 700; line-height: 20px; text-align: center;
}
@media (prefers-reduced-motion: reduce) { .dm-jump-btn { transition: none; } }

/* ===== Friends chat: readable text on YOUR (sent) bubbles in every theme =====
   --dm-out is a dark purple in both themes, but in the light theme --text is near-black, so the message text,
   link, time and ticks were dark-on-dark. Sent bubbles now always use light text. */
#dmChatPane .club-msg.mine .club-msg-bubble { color: #fff; }
#dmChatPane .club-msg.mine .club-msg-text,
#dmChatPane .club-msg.mine .club-msg-caption { color: #fff; }
#dmChatPane .club-msg.mine .club-msg-link { color: #fff; opacity: 1; text-decoration: underline; text-underline-offset: 2px; }
#dmChatPane .club-msg.mine .club-msg-time { color: rgba(255, 255, 255, .78); }
#dmChatPane .club-msg.mine .dm-tick { color: rgba(255, 255, 255, .78); }
#dmChatPane .club-msg.mine .dm-tick-read { color: #53bdeb; }
#dmChatPane .club-msg.mine .dm-msg-more-btn { color: rgba(255, 255, 255, .85); }
#dmChatPane .club-msg.mine .dm-reply-quote { background: rgba(0, 0, 0, .25); }
#dmChatPane .club-msg.mine .dm-reply-name { color: #c9bdff; }
#dmChatPane .club-msg.mine .dm-reply-text { color: rgba(255, 255, 255, .82); }
#dmChatPane .club-msg.mine .dm-fwd { color: rgba(255, 255, 255, .8); }
#dmChatPane .club-msg.mine .dm-fwd-name { color: #d6ccff; }
/* the white link-preview card inside the bubble keeps its own dark text */
#dmChatPane .club-msg.mine .dm-linkcard { color: var(--text); }

/* "characters left" badge on the post + comment send buttons: appears for the last 30 characters (same as the Friends chat) */
.sp-send-wrap .sp-char-left, .cm-send-wrap .sp-char-left {
	position: absolute;
	right: -4px;
	top: -14px;
	z-index: 2;
	min-width: 20px;
	padding: 1px 5px;
	border-radius: 999px;
	background: var(--surf2);
	border: 1px solid var(--border);
	font: 700 11px/1.3 var(--mono, monospace);
	color: var(--warn, #f5a524);
	text-align: center;
	pointer-events: none;
}
.sp-send-wrap.over .sp-char-left, .cm-send-wrap.over .sp-char-left { color: var(--err); }

/* Friends chat: a photo + caption bubble hugs the photo (WhatsApp / Telegram style).
   The photo fills the whole bubble width, the caption sits underneath on the left and the time + ticks at the
   bottom right of the same row -- instead of a wide bubble with the picture pushed to one side. */
#dmChatPane .club-msg-bubble:has(> .club-msg-image) { width: min(341px, 100%); }
#dmChatPane .club-msg-bubble > .club-msg-image {
	width: 100%; max-width: 100%; height: auto; max-height: 380px; object-fit: cover;
	border-radius: 7px; margin-bottom: 4px;
}
#dmChatPane .club-msg-bubble:has(> .club-msg-image) > .club-msg-caption {
	flex: 1 1 auto; min-width: 0; margin-top: 2px; text-align: left; align-self: flex-start;
}

/* Swipe-right-to-reply on DM messages */
#dmMessages .club-msg { position: relative; }
#dmMessages .club-msg.dm-swiping { transition: none; will-change: transform; }
#dmMessages .club-msg:not(.dm-swiping) { transition: transform .18s ease; }
/* horizontal drags on a message belong to the swipe-to-reply handler (vertical scrolling stays with the browser) */
#dmMessages .club-msg { touch-action: pan-y; }
/* the round reply arrow that fades in at the left edge while you drag (JS: _dmSwipeUi) -- one fixed element, so the
   scrolling message list can't clip it */
.dm-swipe-ico {
    position: fixed; left: 0; top: 0; width: 36px; height: 36px; margin: -18px 0 0 -18px;
    border-radius: 50%; display: flex; align-items: center; justify-content: center;
    background: var(--surf2); color: var(--muted); border: 1px solid var(--border);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .28); opacity: 0; pointer-events: none; z-index: 60;
    will-change: transform, opacity; transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.dm-swipe-ico.armed { background: var(--accent); border-color: var(--accent); color: #fff; }

/* Inline image tray above the DM message box (replaces the old "Send an image" pop-up) */
.dm-attach-tray { flex-shrink: 0; padding: 8px 10px 0; background: var(--bg); display: flex; flex-direction: column; gap: 6px; }
/* picker preview = the same mosaic as the chat bubble, each photo with its own X */
/* Tray = exactly the width of .dm-input-wrap: same 10px left inset as the compose row, and the right inset skips the
   Send button column (46px button + 4px margin + 8px gap + the 10px row padding = 68px). */
.dm-attach-tray { padding: 8px 68px 0 10px; }
.dm-tray-album { width: 100%; max-width: 100%; max-height: none; overflow: visible; border-radius: 14px; --album-h1: clamp(160px, 28vh, 300px); --album-h2: clamp(80px, 12vh, 150px); --album-h3: clamp(80px, 12vh, 150px); --album-h2n: clamp(110px, 18vh, 200px); }   /* no inner scrollbar: the tray simply grows with the number of photos (row heights scale with the viewport so 8 photos still leave room for the chat) */
.dm-tray-album .album-grid.n1 .album-cell img { max-height: min(46vh, 420px); }
.dm-tray-album::-webkit-scrollbar { display: none; }
.dm-tray-album .album-img, .dm-tray-album .album-cell img { cursor: default; }
.dm-tray-addmore { align-self: flex-start; border: 1px dashed var(--muted); background: transparent; color: var(--muted); border-radius: 8px; padding: 4px 10px; font-size: 13px; cursor: pointer; }
.dm-tray-addmore:hover { color: var(--text); border-color: var(--text); }
.dm-tray-addmore.full { color: var(--err); border-color: var(--err); font-weight: 700; cursor: default; }
.dm-tray-addmore.full:hover { color: var(--err); border-color: var(--err); }

/* Archived friends: left arrow before the title */
.fr-head .fr-arch-back { margin-right: 6px; flex-shrink: 0; }
.fr-head .fr-title { flex: 1; min-width: 0; }

/* ============================================================
   CALL PANEL SIZES WITH HEADCOUNT -- JS (_callPanelSizeAttrs) sets
   --call-panel-w / --call-row-h / --call-rows from the number of people
   connected, so a 1:1 call is compact and a crowded one opens up.
   Not applied while someone is presenting (that layout keeps full width).
   ============================================================ */
.call-panel.call-panel-sized {
	max-width: var(--call-panel-w, 940px);
}
.call-panel.call-panel-sized .call-split {
	height: min(62vh, max(260px, calc(var(--call-rows, 1) * var(--call-row-h, 150px))));
}
@media (max-width: 700px) {
	.call-panel.call-panel-sized { max-width: 100%; }
}

/* FIX: .call-vgrid (declared later, display:grid + 150px columns) was
   overriding .call-split, so video tiles never shared the space by
   headcount -- one person stayed a 150px sliver. When the grid is in
   split mode, force the flex split back on. */
.call-vgrid.call-split {
	display: flex;
	flex-wrap: wrap;
	grid-template-columns: none;
	gap: var(--call-gap, 10px);
}
.call-vgrid.call-split > .call-vgrid-tile {
	aspect-ratio: auto;
	min-height: 0;
}

/* ============================================================
   UNIFIED SKELETON SYSTEM -- one shimmer, one set of item shapes.
   Every loading placeholder in the app (list rows, feed posts, media
   tiles, sponsor cards, link previews, the PDF stage, the header date
   pill / avatar / bell) now uses THIS sweep: same colours, same 1.6s
   length, same easing, and (via skel.js) the same phase. Appended last
   so it wins over the older per-feature shimmers above.
   ============================================================ */
:root {
	--skel-base: var(--surf2);
	--skel-shine: rgba(255, 255, 255, .07);
	--skel-dur: 1.6s;
	--skel-ease: cubic-bezier(.4, 0, .2, 1);
}
html[data-auth-theme="light"] {
	--skel-base: #e8e8f1;
	--skel-shine: rgba(255, 255, 255, .85);
}
@keyframes skel-sweep {
	0%   { transform: translateX(-100%); }
	100% { transform: translateX(100%); }
}
.skel,
.sponsor-skel-line,
.pdf-loading-skel {
	background: var(--skel-base);
}
.link-preview-skeleton,
.sp-lc-skel {
	position: relative;
	overflow: hidden;
	background: var(--skel-base);
	animation: none;
}
.skel::after,
.sponsor-skel-line::after,
.pdf-loading-skel::after,
.link-preview-skeleton::after,
.sp-lc-skel::after,
.header-avatar-btn:empty::after,
.header-bell-btn.loading .header-bell-icon-wrap::after {
	content: '';
	position: absolute;
	inset: 0;
	transform: translateX(-100%);
	background: linear-gradient(100deg, transparent 20%, var(--skel-shine) 50%, transparent 80%);
	animation: skel-sweep var(--skel-dur) var(--skel-ease) infinite;
	animation-delay: var(--skel-delay, 0ms);
	will-change: transform;
	pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
	.skel::after,
	.sponsor-skel-line::after,
	.pdf-loading-skel::after,
	.link-preview-skeleton::after,
	.sp-lc-skel::after,
	.header-avatar-btn:empty::after,
	.header-bell-btn.loading .header-bell-icon-wrap::after { animation: none; content: none; }
}

/* -- identical item shapes (built by skel.js) -- */
.skel-item {
	box-sizing: border-box;
	display: flex;
	align-items: center;
	gap: 12px;
	width: 100%;
	min-height: 62px;
	padding: 12px 14px;
	background: var(--surface);
	border: 1px solid var(--border);
	border-radius: 10px;
	pointer-events: none;
}
.skel-item-avatar {
	width: 36px;
	height: 36px;
	flex-shrink: 0;
}
.skel-item-col {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 8px;
}
.skel-item .skel-line { height: 11px; border-radius: 6px; }
.skel-item .skel-line-sm { height: 9px; }
.skel-w-name { width: 46%; }
.skel-w-sub  { width: 68%; }
/* feed posts: same avatar + text stack, flush like a real feed row */
.skel-item-post {
	align-items: flex-start;
	min-height: 104px;
	padding: 14px 12px;
	background: transparent;
	border-width: 0 0 1px;
	border-radius: 0;
}
.skel-item-post .skel-item-avatar { width: 40px; height: 40px; }

/* ===== Audio -> video switch: incoming request prompt (call.js, _callVideoRenderPrompt) ===== */
.call-vreq {
	position: fixed;
	top: 18px;
	left: 50%;
	transform: translateX(-50%);
	z-index: 400;                       /* above the call overlay (200) */
	width: min(380px, calc(100vw - 24px));
	box-sizing: border-box;
	padding: 14px 16px;
	border-radius: 14px;
	background: var(--surface);
	color: var(--text);
	border: 1px solid var(--border);
	box-shadow: 0 12px 36px rgba(0, 0, 0, .35);
	animation: fadeIn .15s ease;
}
.call-vreq-title { font-size: 15px; font-weight: 700; line-height: 1.3; overflow-wrap: anywhere; }
.call-vreq-sub { margin-top: 4px; font-size: 13px; color: var(--muted); line-height: 1.4; }
.call-vreq-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }

/* ============================================================
   CALL PANEL -- "who am I calling" state, call-state line, 1:1 video
   (their video = background, mine = small corner preview, tap to swap)
   ============================================================ */
.call-callstate {
	font-size: 12px;
	font-weight: 600;
	color: var(--accent);
	font-family: var(--mono);
	letter-spacing: .2px;
	text-align: center;
}
.call-audio-avatar-tile.pending .call-callstate { font-style: normal; }

/* ringing card inside a video tile: their profile, name, then status */
.call-vgrid-tile.call-vgrid-calling { opacity: 1; }
.call-vcalling {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 8px;
	padding: 12px;
	text-align: center;
}
.call-vcalling-avatar {
	position: relative;
	width: clamp(72px, 22%, 120px);
	aspect-ratio: 1 / 1;
	border-radius: 50%;
	overflow: hidden;
	background: color-mix(in srgb, var(--accent) 18%, var(--surf2));
	border: 2px solid color-mix(in srgb, var(--accent) 45%, transparent);
	animation: callVRingPulse 1.6s ease-in-out infinite;
}
.call-vcalling-avatar .call-vgrid-fallback-img,
.call-vcalling-avatar .call-vgrid-fallback-initials { position: absolute; inset: 0; width: 100%; height: 100%; }
.call-vcalling-name {
	font-size: 16px;
	font-weight: 700;
	color: var(--text);
	max-width: 100%;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
@keyframes callVRingPulse {
	0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 40%, transparent); }
	50% { box-shadow: 0 0 0 10px color-mix(in srgb, var(--accent) 0%, transparent); }
}

/* stacked name + timer label */
.call-vgrid-label-row { display: flex; align-items: center; gap: 5px; min-width: 0; }
.call-vgrid-label.call-vgrid-label-stack {
	flex-direction: column;
	align-items: flex-start;
	gap: 2px;
	border-radius: 12px;
	padding: 5px 10px;
}
.call-vgrid-label-stack .call-callstate { color: #fff; opacity: .9; text-align: left; font-size: 11px; }

/* 1:1 video layout */
.call-vgrid.call-v1on1 {
	display: block;
	position: relative;
	width: 100%;
	height: min(62vh, 440px);
	min-height: 240px;
}
.call-v1on1 > .call-vgrid-tile.main {
	position: absolute;
	inset: 0;
	aspect-ratio: auto;
	border-radius: 14px;
}
.call-v1on1 > .call-vgrid-tile.pip {
	position: absolute;
	right: 12px;
	bottom: 12px;
	width: 28%;
	min-width: 96px;
	max-width: 168px;
	aspect-ratio: 4 / 3;
	z-index: 5;
	cursor: pointer;
	border: 2px solid rgba(255, 255, 255, .55);
	border-radius: 12px;
	box-shadow: 0 6px 18px rgba(0, 0, 0, .45);
	transition: transform .15s ease;
}
.call-v1on1 > .call-vgrid-tile.pip:hover { transform: scale(1.04); }
.call-v1on1 > .call-vgrid-tile.pip .call-vgrid-label { left: 5px; bottom: 5px; padding: 2px 7px; font-size: 10px; }
.call-v1on1 > .call-vgrid-tile.pip .call-vgrid-mute-badge { width: 18px; height: 18px; top: 4px; right: 4px; }
.call-v1on1 > .call-vgrid-tile.pip .call-video-el { z-index: 1; }

/* Friends chat: photo / album bubbles hug the picture -- no outer margin, just a thin 3px frame, the caption wraps straight
   up under it with no extra margin or padding, and the bubble shrinks to fit. */
#dmChatPane .club-msg-bubble:has(> .club-msg-image),
#dmChatPane .club-msg-bubble:has(> .club-msg-album) { padding: 3px; }
#dmChatPane .club-msg-bubble:has(> .club-msg-album) { width: min(326px, 100%); }
#dmChatPane .club-msg-bubble > .club-msg-album { flex: 1 1 100%; width: 100%; max-width: 100%; }
#dmChatPane .club-msg-bubble > .club-msg-image { margin-bottom: 0; border-radius: 7px; }
#dmChatPane .club-msg-bubble:has(> .club-msg-image) > .club-msg-caption,
#dmChatPane .club-msg-bubble:has(> .club-msg-album) > .club-msg-caption {
	flex: 1 1 auto; min-width: 0; margin: 0; padding: 3px 5px 0; text-align: left; align-self: flex-start;
}
#dmChatPane .club-msg-bubble:has(> .club-msg-image) .club-msg-time,
#dmChatPane .club-msg-bubble:has(> .club-msg-album) .club-msg-time { margin-top: 0; padding: 2px 5px 1px 0; }
#dmChatPane .club-msg-bubble:has(> .club-msg-image) > .dm-reply-quote,
#dmChatPane .club-msg-bubble:has(> .club-msg-album) > .dm-reply-quote { margin: 2px 2px 4px; }
#dmChatPane .club-msg-bubble:has(> .club-msg-image) > .dm-fwd,
#dmChatPane .club-msg-bubble:has(> .club-msg-album) > .dm-fwd { padding: 2px 5px 0; }

/* Contact panel: the 3-dot menu that replaced the Follow button opens under its button, right edge aligned. */
.dmi-act-more .dmi-menu { top: 58px; right: -8px; text-align: left; width: max-content; max-width: min(260px, 86vw); }
/* Formatting help dialog (Friends chat 3-dot menu). */
.confirm-box:has(.dm-fmt-help) { width: min(420px, 92vw); max-height: 86vh; overflow-y: auto; }
.dm-fmt-help { text-align: left; font-weight: 400; }
.dm-fmt-help h4 { font-weight: 700; }
.dm-fmt-help h4 { margin: 0 0 6px; font-size: 16px; }
.dm-fmt-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: center; padding: 8px 0; border-top: 1px solid var(--border); font-size: 14px; }
.dm-fmt-syn { font: 12.5px/1.4 var(--mono, monospace); color: var(--muted); overflow-wrap: anywhere; background: none; padding: 0; }
.dm-fmt-out { overflow-wrap: anywhere; }
.dm-fmt-note { margin-top: 8px; font-size: 12.5px; color: var(--muted); }
/* a two-finger swipe over the chat must not trigger the browser's own back / forward gesture */
#dmMessages { overscroll-behavior-x: contain; }

/* ==========================================================================
   Pinned section headers: the title row of Calls and every Settings page
   stays docked under the sticky site header while only the content below
   scrolls (same technique as .sp-tabs: 1px tucked under the header).
   ========================================================================== */
#viewCalls .fr-head,
.st-page .st-bar {
	position: sticky;
	top: calc(var(--header-h, 0px) - 1px);
	z-index: 110;   /* below the site header (120), above the scrolling content */
	background: var(--bg);
	padding-top: 5px;
}
body.alert-banner-open #viewCalls .fr-head,
body.alert-banner-open .st-page .st-bar {
	top: calc(var(--header-h, 0px) + var(--alert-banner-h, 0px) - 1px);
}

/* DM contact panel: the avatar / name / actions / tab strip stay put, only the tab content (.dmi-body) scrolls. */
.dmi-scroll { display: flex; flex-direction: column; overflow: hidden; padding-bottom: 0; }
.dmi-head, .dmi-tabs { flex-shrink: 0; }
.dmi-body { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding-bottom: 24px; }
/* Very short windows (e.g. phone in landscape): not enough room to pin the header, so scroll the whole panel as before. */
@media (max-height: 560px) {
	.dmi-scroll { display: block; overflow-y: auto; padding-bottom: 24px; }
	.dmi-body { overflow: visible; padding-bottom: 0; }
}

/* "<thing> is deleted" placeholders (post card, comment in the comments popup) */
.sp-post-deleted { padding: 16px 18px; border-bottom: 1px solid var(--border); }
.sp-del-note, .cm-del-note {
	display: inline-flex; align-items: center; gap: 7px;
	color: var(--muted); font-size: 13.5px; font-style: italic; font-weight: 600;
}
.cm-del-note { padding: 10px 0 4px; }
.cm-row.cm-deleted .cm-av-del { background: var(--surf2); border: 1px dashed var(--border); cursor: default; }
.sp-quote-missing { font-style: italic; }

/* Comment box: attachment previews sit above the input row */
.cm-previews.sp-previews, .cm-previews.sp-previews:not(:empty) { margin: 0 0 8px 52px; }

/* Page behind any open popup/sheet/lightbox is frozen (see assets/js/scroll-guard.js). */
html.scroll-guard-lock { overflow: hidden; }

/* "Calling..." -- the word is static, the three dots light up one by one (1, 2, 3) and start over */
.call-dots i { font-style: normal; opacity: 0; animation: callDot 1.5s infinite; }
.call-dots i:nth-child(1) { animation-name: callDot1; }
.call-dots i:nth-child(2) { animation-name: callDot2; }
.call-dots i:nth-child(3) { animation-name: callDot3; }
@keyframes callDot1 { 0% { opacity: 0; } 1%, 99% { opacity: 1; } 100% { opacity: 0; } }
@keyframes callDot2 { 0%, 33% { opacity: 0; } 34%, 99% { opacity: 1; } 100% { opacity: 0; } }
@keyframes callDot3 { 0%, 66% { opacity: 0; } 67%, 99% { opacity: 1; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .call-dots i { animation: none; opacity: 1; } }

/* Friend is typing / sending media: three dots bubble under the messages (DM chat) */
.dm-typing { flex-shrink: 0; display: flex; align-items: center; gap: 8px; padding: 2px 14px 6px; background: var(--bg); }
.dm-typing-bubble { display: inline-flex; align-items: center; gap: 4px; padding: 9px 13px; border-radius: 16px; background: var(--surf2); }
.dm-typing-bubble i { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); opacity: .35; animation: dmTypingDot 1.2s infinite ease-in-out; }
.dm-typing-bubble i:nth-child(2) { animation-delay: .2s; }
.dm-typing-bubble i:nth-child(3) { animation-delay: .4s; }
.dm-typing-label { font-size: 12px; color: var(--muted); }
.dm-typing-label:empty { display: none; }
@keyframes dmTypingDot { 0%, 60%, 100% { opacity: .35; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }
@media (prefers-reduced-motion: reduce) { .dm-typing-bubble i { animation: none; opacity: .7; } }

/* Friend chat: the footer row (reactions on the LEFT, three-dot + time + ticks on the RIGHT) always sits on its own
   line under the message, whether the text is one line or many. */
#dmChatPane .club-msg-bubble > .dm-msg-foot {
	flex: 1 1 100%;
	width: 100%;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	min-width: 0;
	margin-top: 2px;
}
#dmChatPane .dm-msg-foot > .club-msg-reactions {
	flex: 0 1 auto;
	min-width: 0;
	margin: 4px 0 0 0;
	justify-content: flex-start;
}
#dmChatPane .dm-msg-foot > .club-msg-time {
	flex: 0 0 auto;
	margin: 3px 0 0 auto;
}
/* photo bubbles: keep the footer inside the photo padding */
#dmChatPane .club-msg-bubble:has(> .club-msg-image) > .dm-msg-foot,
#dmChatPane .club-msg-bubble:has(> .club-msg-album) > .dm-msg-foot { padding: 0 5px 1px; box-sizing: border-box; }
#dmChatPane .club-msg-bubble:has(> .club-msg-image) .dm-msg-foot > .club-msg-time,
#dmChatPane .club-msg-bubble:has(> .club-msg-album) .dm-msg-foot > .club-msg-time { padding: 2px 0 1px; }

/* "frRowMenu": the floating friend-chat menu (see _frRowMenuOpenFor() in clubs-and-dms.js). */
.fr-menu.fr-row-menu {
	position: fixed; top: 0; left: 0; z-index: 10000;
	/* .fr-menu sets right:0 for the header dropdowns; on this fixed menu it would stretch across the screen. */
	right: auto;
	width: max-content;
	min-width: 190px;
	max-width: calc(100vw - 16px);
	max-height: calc(100vh - 16px);
	overflow-y: auto;
	padding: 6px;
}
.fr-row-menu .dm-menu-item { gap: 12px; padding: 9px 12px; font-size: 14px; white-space: nowrap; }
.fr-row-menu .dm-menu-item svg { width: 18px; height: 18px; }

/* ==========================================================================
   Friends chat bubble layout fixes
   1) Text always reads left -> right. The bubble used to be a wrapping flex row with justify-content:flex-end, and the
      footer (3-dot + time + ticks) is a full-width row, so a one-line message was pushed to the RIGHT edge of a bubble
      that was as wide as "text + footer". The bubble is now a plain column: it hugs max(text, footer), the text starts
      at the left, and the footer (time / ticks) sits on the right of its own row.
   2) Reacting never resizes the bubble. Reaction pills used to sit in the footer row, so adding one grew the footer
      (taller row, wider bubble on short messages). They now hang off the bottom-left edge of the bubble (bottom-right
      on my own messages) and take no space inside it; the message row only gets a little room below it.
   ========================================================================== */
#dmChatPane .club-msg-bubble {
	flex-direction: column;
	flex-wrap: nowrap;
	align-items: stretch;
	justify-content: flex-start;
}
#dmChatPane .club-msg-text,
#dmChatPane .club-msg-caption { text-align: left; align-self: stretch; }

#dmChatPane .dm-msg-foot > .club-msg-reactions {
	position: absolute;        /* the bubble is position:relative (.club-msg-bubble) */
	left: 8px;
	right: auto;
	bottom: -17px;
	margin: 0;
	width: max-content;
	max-width: min(320px, 78vw);
	flex-wrap: nowrap;
	gap: 4px;
	z-index: 2;
}
#dmChatPane .club-msg.mine .dm-msg-foot > .club-msg-reactions { left: auto; right: 8px; }
#dmChatPane .dm-msg-foot > .club-msg-reactions .club-msg-reaction-pill {
	height: 22px;
	padding: 0 7px;
	line-height: 1;
	box-sizing: border-box;
	background: var(--surf2);
	box-shadow: 0 1px 3px rgba(0, 0, 0, .4);
}
/* room under a bubble that carries reactions, so the hanging pills never overlap the next message */
#dmChatPane .club-msg:has(.dm-msg-foot > .club-msg-reactions) { margin-bottom: 18px; }

/* ============================================================
   CALL WINDOW -- full screen by default + no re-animation on re-render
   ============================================================ */
/* The panel's slide-in only plays when the call window first opens (JS sets data-entered ~400ms later).
   Every join / leave / camera / swap re-render rebuilds the panel; without this the animation replayed each time
   and the whole window flashed. */
.call-overlay[data-entered] .call-panel { animation: none; }

/* Plain video call: the window takes the whole screen; the split screen below divides it by headcount. */
.call-overlay.call-overlay-full {
	padding: 0;
	align-items: stretch;
	backdrop-filter: none;
	background: var(--surface);
}
.call-overlay.call-overlay-full .call-panel.call-video-panel {
	width: 100%;
	max-width: none;
	height: 100%;
	box-sizing: border-box;
	border: 0;
	border-radius: 0;
	gap: 12px;
	padding: max(14px, env(safe-area-inset-top)) max(18px, env(safe-area-inset-right)) max(12px, env(safe-area-inset-bottom)) max(18px, env(safe-area-inset-left));
}
.call-overlay.call-overlay-full .call-video-panel .call-audio-body {
	flex: 1 1 auto;
	min-height: 0;
	align-items: stretch;
}
.call-overlay.call-overlay-full .call-video-panel .call-audio-main {
	flex: 1 1 auto;
	min-height: 0;
}
/* the tiles share all the room that's left (2 -> side by side, 3 -> 2+1, 4 -> 2x2, ... see _callSplitAttrs) */
.call-overlay.call-overlay-full .call-video-panel .call-split,
.call-overlay.call-overlay-full .call-video-panel .call-v1on1 {
	flex: 1 1 auto;
	height: auto;
	min-height: 0;
}
.call-overlay.call-overlay-full .call-video-panel .call-pending-line { margin-top: 0; }
/* someone presenting: the shared screen takes the room, the people sit in a strip under it */
.call-overlay.call-overlay-full .call-video-panel .call-presenter-area {
	flex: 1 1 auto;
	min-height: 0;
	display: flex;
	flex-direction: column;
}
.call-overlay.call-overlay-full .call-video-panel .call-presenter-preview {
	flex: 1 1 auto;
	min-height: 0;
	max-height: none;
	aspect-ratio: auto;
}
.call-overlay.call-overlay-full .call-video-panel .call-vgrid-with-presenter {
	flex: 0 0 auto;
	grid-template-columns: repeat(auto-fill, minmax(120px, 170px));
}

/* ============================================================
   CAMERA EFFECTS sheet (call More menu -> "Camera effects")
   ============================================================ */
.call-fx-panel {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 230;           /* above the call window (200) */
	display: flex;
	justify-content: center;
	pointer-events: none;
}
/* Same look as the camera's lens carousel (.cam-lensbar): round lenses on a dark fade, the active one centred with a white ring */
.call-fx-sheet {
	pointer-events: auto;
	position: relative;
	width: 100%;
	padding: 44px 0 calc(10px + env(safe-area-inset-bottom, 0px));
	background: linear-gradient(to top, rgba(0,0,0,.82) 0%, rgba(0,0,0,.5) 55%, rgba(0,0,0,0) 100%);
	color: #fff;
	animation: up .18s ease;
}
.call-fx-close {
	position: absolute;
	top: 8px;
	right: 12px;
	width: 30px;
	height: 30px;
	border: none;
	border-radius: 50%;
	background: rgba(0,0,0,.45);
	color: #fff;
	cursor: pointer;
	font-size: 13px;
}
.call-fx-row {
	display: flex;
	align-items: flex-start;
	gap: 4px;
	overflow-x: auto;
	overflow-y: hidden;
	padding: 16px calc(50% - 38px) 4px;
	scroll-snap-type: x proximity;
	scrollbar-width: none;
	-webkit-overflow-scrolling: touch;
	-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 15%, #000 85%, transparent 100%);
	mask-image: linear-gradient(90deg, transparent 0, #000 15%, #000 85%, transparent 100%);
}
.call-fx-row::-webkit-scrollbar { display: none; }
.call-fx-item { flex: none; width: 76px; display: flex; flex-direction: column; align-items: center; scroll-snap-align: center; }
.call-fx-chip {
	position: relative;
	flex: none;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 56px;
	height: 56px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	overflow: hidden;
	background: rgba(255,255,255,.14);
	opacity: .82;
	transform: scale(.86);
	box-shadow: 0 0 0 2px rgba(255,255,255,.35), 0 4px 12px rgba(0,0,0,.35);
	cursor: pointer;
	transition: transform .22s cubic-bezier(.3,1.4,.5,1), opacity .2s, box-shadow .2s, background .2s;
}
.call-fx-chip canvas {
	width: 100%;
	height: 100%;
	display: block;
	border-radius: 50%;
	object-fit: cover;
	background: #000;
	transform: scaleX(-1);   /* thumbnails look like my own (mirrored) preview */
	pointer-events: none;
}
.call-fx-chip.active { opacity: 1; transform: scale(1.22); background: rgba(255,255,255,.2); box-shadow: 0 0 0 3px rgba(0,0,0,.45), 0 0 0 6px #fff, 0 6px 18px rgba(0,0,0,.5); }
.call-fx-chip.active:active { transform: scale(1.1); }
.call-fx-none { background: rgba(255,255,255,.1); }
.call-fx-none span { width: 42px; height: 42px; border-radius: 50%; background: #fff; box-shadow: inset 0 0 0 3px rgba(0,0,0,.08); }
.call-fx-none.active { background: transparent; }
.call-fx-name {
	margin-top: 18px;
	padding: 3px 9px;
	max-width: 100%;
	border-radius: 999px;
	font: 600 10.5px/1.25 var(--sans, sans-serif);
	color: rgba(255,255,255,.74);
	text-shadow: 0 1px 3px rgba(0,0,0,.7);
	text-align: center;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	pointer-events: none;
	transition: background .2s, color .2s;
}
.call-fx-chip.active + .call-fx-name { background: rgba(255,255,255,.95); color: #111; text-shadow: none; font-weight: 700; }
.call-fx-new {
	position: absolute;
	top: 4px;
	right: 4px;
	z-index: 1;
	padding: 1px 5px;
	border-radius: 999px;
	background: linear-gradient(135deg, #7c4dff, #c24dff);
	color: #fff;
	font-size: 8px;
	font-style: normal;
	font-weight: 800;
	letter-spacing: .04em;
	line-height: 1.5;
	pointer-events: none;
}
.call-presenter-badge-wrap[hidden] { display: none; }


/* ---- Chat background picker + wallpaper (see "Chat background" in clubs-and-dms.js) ---- */
#dmChatPane .club-messages.dm-has-wallpaper { background-color: transparent; }

/* ============================================================
   Sponsor a Hashtag / Search: the "Sponsored" message above hashtag posts and search results
   ============================================================ */
.sp-kw-ad { margin: 10px 12px; padding: 12px 14px; border-radius: 14px; background: var(--surf2, rgba(255,255,255,.04)); box-shadow: inset 0 0 0 1px var(--border); text-align: left; }
.sp-kw-ad-top { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.sp-kw-ad-by { font-size: 12px; color: var(--muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sp-kw-ad-text { margin-top: 2px; font-size: 14px; line-height: 1.4; color: var(--muted); overflow-wrap: anywhere; white-space: pre-wrap; }
.sp-kw-ad-img { margin-top: 10px; width: 100%; aspect-ratio: 16 / 9; border-radius: 10px; background-size: cover; background-position: center; background-color: var(--surf2); }
.sp-kw-ad-cta { margin-top: 10px; }
.sp-kw-ad.compact { margin: 4px 0 6px; padding: 10px 12px; }
.sp-kw-ad.compact .sp-kw-ad-img { aspect-ratio: 3 / 1; }
.hs-ad-row { cursor: pointer; border-radius: 10px; }
.hs-ad-row.hs-active .sp-kw-ad { box-shadow: inset 0 0 0 2px var(--accent); }
.hs-tag.hs-tag-ad { display: inline-flex; align-items: center; gap: 6px; }
.hs-tag-ad-mark { font-size: 9.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; padding: 1px 6px; border-radius: 999px; background: rgba(232, 147, 74, .18); color: var(--a2, #e8934a); }
.sponsor-kw-wrap { display: flex; align-items: center; gap: 6px; }
.sponsor-kw-wrap input { flex: 1; min-width: 0; }
.sponsor-kw-hash { font-weight: 800; font-size: 16px; color: var(--muted); }

/* Friends chat image viewer: shares the post lightbox's close button / dots / arrows (.sp-lightbox-close, .sp-lb-*) */
.image-viewer-overlay .sp-lightbox-close { display: flex; align-items: center; justify-content: center; position: fixed; top: calc(env(safe-area-inset-top, 0px) + 12px); right: 14px; width: 38px; height: 38px; padding: 0; }
.image-viewer-overlay .sp-lb-nav { position: fixed; }
.image-viewer-overlay .sp-lb-dots { position: fixed; }

/* ---- Results / Notices / Holidays: the "Back | Title | actions" bar stays put under the site header while the
   list (or PDF page) scrolls beneath it, instead of scrolling away with the content. Same trick as .sp-tabs:
   --header-h is kept live by ui.js; tucked 1px under the (higher z-index) header so no hairline shows between. */
.result-topbar, .notice-topbar, .holiday-topbar {
	position: sticky;
	top: calc(var(--header-h, 0px) + var(--alert-banner-h, 0px) - 1px);
	z-index: 110;           /* below the site header (120), above the content scrolling under it */
	background: var(--bg);
	margin-bottom: 0;
}
/* the 14px gap that used to sit under the bar lives on the content now, so scrolled rows never peek through it */
#resultListSection .result-topbar + *,
#viewNotices .notice-topbar + *,
#viewHolidays .holiday-topbar + * { margin-top: 14px; }

/* Syllabus / PYQs / Results / Notices / Holidays: same header as Settings -- round back arrow with the title beside it on the left
   (no "Back" word, no centred title). Any action icons on the right (open board, refresh) are pushed to the far end. */
.app-view .vtop {
	padding: 5px 0 10px;
	gap: 6px;
	border-bottom: 0;
	backdrop-filter: none;
	-webkit-backdrop-filter: none;
}
.vtop-back {
	width: 38px;
	height: 38px;
	flex: none;
	border: 0;
	border-radius: 50%;
	background: none;
	color: var(--text);
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.vtop-back:hover { background: var(--surf2, var(--surface)); }
.vtop-title { margin: 0; font-size: 22px; font-weight: 800; letter-spacing: -.2px; line-height: 1.2; color: var(--text); }
.vtop .vtop-title + * { margin-left: auto; }


/* ============================================================
   Nearby Vendors / Rooms / Transports / Sell-Buy full-screen sections: keep their content in the SAME centred
   column as the rest of the app (Home, Settings ...) instead of stretching edge to edge. Mirrors .app's width
   (min(75%,1100px), widening to 90% / 95% / 100% on narrower screens) and its side padding. The overlay itself
   stays full-screen (background + scrolling); only the header / search / chips / list are inset to the column.
   ============================================================ */
#vnOverlay, #rmOverlay, #trOverlay, #sbOverlay {
	--ov-col: min(75%, 1100px);
	--ov-pad: clamp(10px, 3vw, 20px);
}
@media (max-width: 1100px) { #vnOverlay, #rmOverlay, #trOverlay, #sbOverlay { --ov-col: 90%; } }
@media (max-width: 900px)  { #vnOverlay, #rmOverlay, #trOverlay, #sbOverlay { --ov-col: 95%; } }
@media (max-width: 600px)  { #vnOverlay, #rmOverlay, #trOverlay, #sbOverlay { --ov-col: 100%; } }
:is(#vnOverlay, #rmOverlay, #trOverlay, #sbOverlay) > :is(.vn-top, .vn-bar, .vn-chips, .vn-list) {
	padding-left: calc((100% - var(--ov-col)) / 2 + var(--ov-pad));
	padding-right: calc((100% - var(--ov-col)) / 2 + var(--ov-pad));
}
/* Sort pill ("Nearest" ...): start it where the screen title text starts (right of the back arrow) instead of under the
   arrow, so it lines up with Nearby Vendors / Transports / Rooms / Sell-Buy. Back button = 22px icon + 2x4px padding = 30px, + 12px gap. */
:is(#vnOverlay, #rmOverlay, #trOverlay, #sbOverlay) > .vn-bar {
	padding-left: calc((100% - var(--ov-col)) / 2 + var(--ov-pad) + 30px + 12px);
}

/* ============================================================
   Nearby screens use the MAIN header search bar (see assets/js/nearby-search.js). The overlays start under the header
   (--nb-top), so keep the header above its own backdrop. The header looks and behaves exactly like Home's.
   ============================================================ */
body.nb-search-on header { z-index: 9995; }
body.nb-search-on .header-search-results { display: none !important; }

/* ---- Responsive hardening (v6.5): nothing may push the page wider than the screen, on any device ---- */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
html, body { max-width: 100%; overflow-wrap: break-word; }
img, video, canvas, iframe, embed, object { max-width: 100%; }
img, video { height: auto; }
.app-view, .app-view > * { min-width: 0; }
pre { max-width: 100%; overflow-x: auto; }
.confirm-box, .modal-bg > *, .modal-overlay > *, .sp-modal-bg > *, .sh-modal-bg > * { max-width: calc(100vw - 16px); max-height: calc(100vh - 16px); max-height: calc(100dvh - 16px); box-sizing: border-box; }
@media (max-width: 700px) {
	.app-view table { display: block; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
}


/* Dialogs keep their own width cap. The rule above (.modal-bg > *) only guards against overflowing the screen, but being
   later in the file it also beat .modal's max-width, so e.g. the Sponsor dialog stretched across the whole window. */
.modal-bg > .modal { max-width: min(500px, calc(100vw - 16px)); }
#mSponsor .modal { max-width: min(520px, calc(100vw - 16px)); }

/* Sponsor modal: keep the "Sponsor beuclub" title + close button pinned; only the content below scrolls. */
#mSponsor .modal {
	padding-top: 0;
}
#mSponsor .modal-title-row {
	position: sticky;
	top: 0;
	z-index: 5;
	background: var(--surface);
	margin-bottom: 0;
	padding: 14px 0 10px;
}
/* Shorter Sponsor menu: tighter header, cards and gaps (it used to run most of the screen). */
#mSponsor .modal { max-height: 82vh; padding-bottom: 14px; }
#mSponsor .sponsor-pick-cards { gap: 6px; margin-bottom: 8px; }
#mSponsor .sponsor-card { padding: 8px 12px; border-radius: 10px; }
#mSponsor .sponsor-card-title { font-size: 13.5px; margin-bottom: 1px; }
#mSponsor .sponsor-card-blurb { font-size: 11.5px; line-height: 1.3; margin-bottom: 0; }
#mSponsor .sponsor-card-price { font-size: 12.5px; }
#mSponsor .sponsor-card-skeleton { min-height: 56px; }
#mSponsor .sponsor-mine-list { margin-top: 2px; max-height: 160px; }

.sponsor-skel-blurb2 { width: 56%; }
.sponsor-card-skeleton { min-height: 96px; }

/* Shorts publish sheet: name + #hashtag suggestions */
.sh-suggest { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-radius: 12px; border: 1px dashed var(--border, #2a2a32); background: var(--surf2, #26262c); }
.sh-suggest[hidden] { display: none; }
.sh-suggest-head { font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--muted, #8a8a96); }
.sh-suggest-name { text-align: left; display: flex; flex-direction: column; gap: 2px; padding: 9px 12px; border-radius: 10px; border: 1px solid var(--border, #2a2a32); background: transparent; color: inherit; font: inherit; font-size: 14px; font-weight: 700; cursor: pointer; }
.sh-suggest-name small { font-size: 11px; font-weight: 600; color: var(--accent, #7c6cff); }
.sh-suggest-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.sh-suggest-tag { padding: 6px 11px; border-radius: 999px; border: 1px solid var(--border, #2a2a32); background: transparent; color: inherit; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.sh-suggest-tag.on { background: var(--accent, #7c6cff); border-color: var(--accent, #7c6cff); color: #fff; }


/* Rotating search-bar hints: bottom -> centre -> up (see global-search.js) */
.header-search-field.hs-ph-host { position: relative; }
.hs-ph { position: absolute; top: 0; bottom: 0; display: none; align-items: center; overflow: hidden; pointer-events: none; color: var(--muted); font-size: 14px; }
.hs-ph-on .hs-ph { display: flex; }
.hs-ph-on .header-search-input::placeholder { color: transparent; }
.hs-ph-t { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; opacity: 0; }
.hs-ph-t.go { animation: hsPhRise 2.8s cubic-bezier(.3, .7, .3, 1) both; }
.hs-ph-t.static { animation: none; opacity: 1; transform: none; }
@keyframes hsPhRise {
	0%   { transform: translateY(110%); opacity: 0; }      /* below the box */
	16%  { transform: translateY(0);    opacity: 1; }      /* settles in the centre */
	82%  { transform: translateY(0);    opacity: 1; }      /* rests */
	100% { transform: translateY(-110%); opacity: 0; }     /* lifts out the top */
}


/* ---- "look here" outline: a shared link just opened this short / post (beuHighlight in helpers.js) ---- */
.beu-hl { outline: 3px solid transparent; outline-offset: 2px; border-radius: 12px; animation: beuHlOutline 4.5s ease-out both; }
@keyframes beuHlOutline {
    0%   { outline-color: rgba(124,108,255,0);  box-shadow: 0 0 0 0 rgba(124,108,255,0); }
    10%  { outline-color: #7c6cff;              box-shadow: 0 0 0 9px rgba(124,108,255,.30); }
    28%  { box-shadow: 0 0 0 2px rgba(124,108,255,.14); }
    46%  { box-shadow: 0 0 0 9px rgba(124,108,255,.30); }
    64%  { box-shadow: 0 0 0 2px rgba(124,108,255,.14); }
    80%  { outline-color: #7c6cff; }
    100% { outline-color: rgba(124,108,255,0);  box-shadow: 0 0 0 0 rgba(124,108,255,0); }
}
/* a short fills the whole screen and clips its edges, so its outline is drawn inside, above the video */
.sh-item.beu-hl { outline: 0; border-radius: 0; animation: none; }
.sh-item.beu-hl::after {
    content: ''; position: absolute; inset: 0; z-index: 30; pointer-events: none;
    border: 4px solid #7c6cff; border-radius: 14px;
    box-shadow: inset 0 0 28px rgba(124,108,255,.65), 0 0 18px rgba(124,108,255,.55);
    animation: beuHlShort 4.5s ease-out both;
}
@keyframes beuHlShort {
    0%   { opacity: 0; }
    8%   { opacity: 1; }
    30%  { opacity: .45; }
    48%  { opacity: 1; }
    66%  { opacity: .45; }
    80%  { opacity: 1; }
    100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
    .beu-hl, .sh-item.beu-hl::after { animation-duration: 4.5s; animation-timing-function: steps(1, end); }
}


/* ---- in-page camera (camera-capture.js): computers only; phones open their own camera app ---- */
.cam-ov { position: fixed; inset: 0; z-index: 100000; background: rgba(0,0,0,.72); display: flex; align-items: center; justify-content: center; padding: 16px; }
.cam-box { position: relative; width: min(560px, 100%); max-height: 100%; background: #000; border-radius: 16px; overflow: hidden; display: flex; flex-direction: column; box-shadow: 0 20px 60px rgba(0,0,0,.6); }
.cam-v { display: block; width: 100%; max-height: 70vh; min-height: 220px; background: #111; object-fit: cover; }
.cam-x { position: absolute; top: 10px; right: 10px; z-index: 2; width: 36px; height: 36px; border-radius: 50%; border: 0; background: rgba(0,0,0,.55); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.cam-bar { display: flex; align-items: center; justify-content: space-between; padding: 14px 22px 18px; }
.cam-side { width: 44px; }
.cam-flip { width: 44px; height: 44px; border-radius: 50%; border: 0; background: rgba(255,255,255,.14); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.cam-shot { width: 68px; height: 68px; border-radius: 50%; border: 4px solid #fff; background: transparent; padding: 0; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.cam-shot span { width: 50px; height: 50px; border-radius: 50%; background: #fff; transition: transform .12s; }
.cam-shot:active span { transform: scale(.88); }

/* ---- Camera lenses (assets/js/camera-lenses.js): canvas preview + round lens carousel; the big centre lens is the shutter ---- */
.cam-vh { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.cam-cv { display: block; width: 100%; max-height: 70vh; min-height: 220px; background: #111; object-fit: cover; }
.cam-flip-top { position: absolute; top: 56px; right: 10px; z-index: 2; width: 36px; height: 36px; background: rgba(0,0,0,.55); }
.cam-flip-top svg { width: 19px; height: 19px; }
.cam-lensbar { padding: 6px 0 14px; background: #000; }
.cam-lens-row { display: flex; align-items: center; gap: 16px; overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; padding: 14px calc(50% - 28px) 12px; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.cam-lens-row::-webkit-scrollbar { display: none; }
.cam-lens { flex: none; width: 56px; height: 56px; border-radius: 50%; border: 0; padding: 0; scroll-snap-align: center; background: rgba(255,255,255,.16); display: grid; place-items: center; cursor: pointer; transition: transform .18s ease, box-shadow .18s ease, background .18s; -webkit-tap-highlight-color: transparent; }
.cam-lens i { font-style: normal; font-size: 27px; line-height: 1; }
.cam-lens { overflow: hidden; }
.cam-lens img { width: 100%; height: 100%; display: block; border-radius: 50%; object-fit: cover; pointer-events: none; -webkit-user-drag: none; user-select: none; }
.cam-lens.on { transform: scale(1.3); box-shadow: 0 0 0 3px #fff; background: rgba(255,255,255,.26); }
.cam-lens.on:active { transform: scale(1.18); }
.cam-lens-none span { width: 40px; height: 40px; border-radius: 50%; background: #fff; }
.cam-lens-none { background: transparent; box-shadow: 0 0 0 3px rgba(255,255,255,.7); }
.cam-lens-none.on { box-shadow: 0 0 0 3px #fff; background: transparent; }
/* lens bar (v2): floats over the bottom of the picture on a soft fade; round lenses with their names, the centred one is the shutter */
.cam-lensbar { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; padding: 30px 0 calc(8px + env(safe-area-inset-bottom, 0px)); background: linear-gradient(to top, rgba(0,0,0,.8) 0%, rgba(0,0,0,.48) 55%, rgba(0,0,0,0) 100%); pointer-events: none; }
.cam-lens-row { pointer-events: auto; align-items: flex-start; gap: 4px; padding: 16px calc(50% - 38px) 4px; -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 15%, #000 85%, transparent 100%); mask-image: linear-gradient(90deg, transparent 0, #000 15%, #000 85%, transparent 100%); }
.cam-lens-item { flex: none; width: 76px; display: flex; flex-direction: column; align-items: center; scroll-snap-align: center; }
.cam-lens-item .cam-lens { scroll-snap-align: none; width: 56px; height: 56px; background: rgba(255,255,255,.14); opacity: .82; transform: scale(.86); box-shadow: 0 0 0 2px rgba(255,255,255,.35), 0 4px 12px rgba(0,0,0,.35); transition: transform .22s cubic-bezier(.3,1.4,.5,1), opacity .2s, box-shadow .2s, background .2s; }
.cam-lens-item .cam-lens.on { opacity: 1; transform: scale(1.22); background: rgba(255,255,255,.2); box-shadow: 0 0 0 3px rgba(0,0,0,.45), 0 0 0 6px #fff, 0 6px 18px rgba(0,0,0,.5); }
.cam-lens-item .cam-lens.on:active { transform: scale(1.1); }
.cam-lens-item .cam-lens-none { background: rgba(255,255,255,.1); }
.cam-lens-item .cam-lens-none.on { background: transparent; }
.cam-lens-none span { width: 42px; height: 42px; box-shadow: inset 0 0 0 3px rgba(0,0,0,.08); }
.cam-lens-name { margin-top: 18px; padding: 3px 9px; max-width: 100%; border-radius: 999px; font: 600 10.5px/1.25 var(--sans, sans-serif); color: rgba(255,255,255,.74); text-shadow: 0 1px 3px rgba(0,0,0,.7); text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none; transition: background .2s, color .2s; }
.cam-lens.on + .cam-lens-name { background: rgba(255,255,255,.95); color: #111; text-shadow: none; font-weight: 700; }
.cam-lens-hint { position: absolute; left: 50%; top: 46%; transform: translateX(-50%); z-index: 2; padding: 6px 14px; border-radius: 999px; background: rgba(0,0,0,.6); color: #fff; font: 600 13px/1.3 var(--sans, sans-serif); text-align: center; pointer-events: none; }
.cam-flash { position: absolute; inset: 0; z-index: 3; background: #fff; opacity: 0; pointer-events: none; }
.cam-flash.go { animation: camFlash .28s ease-out; }
@keyframes camFlash { 0% { opacity: .85; } 100% { opacity: 0; } }
@media (max-width: 640px) {
	.cam-ov:has(.cam-cv) { padding: 0; }
	.cam-ov:has(.cam-cv) .cam-box { width: 100%; height: 100%; max-height: none; border-radius: 0; }
	.cam-ov:has(.cam-cv) .cam-cv { flex: 1; max-height: none; min-height: 0; height: 0; }
}

/* ---- Camera lenses on large screens: bigger camera window + bigger round lens carousel (laptop / desktop / 4K) ---- */
@media (min-width: 1024px) {
	.cam-ov:has(.cam-cv) { padding: 24px; }
	.cam-ov:has(.cam-cv) .cam-box { width: min(760px, 100%); border-radius: 22px; }
	.cam-ov:has(.cam-cv) .cam-cv { max-height: 78vh; }
	.cam-x, .cam-flip-top { width: 44px; height: 44px; }
	.cam-flip-top { top: 66px; }
	.cam-flip-top svg { width: 23px; height: 23px; }
	.cam-lensbar { padding-top: 44px; padding-bottom: 14px; }
	.cam-lens-row { gap: 8px; padding: 22px calc(50% - 53px) 6px; }
	.cam-lens-item { width: 106px; }
	.cam-lens-item .cam-lens { width: 78px; height: 78px; }
	.cam-lens i { font-size: 38px; }
	.cam-lens-none span { width: 58px; height: 58px; }
	.cam-lens-item .cam-lens.on { box-shadow: 0 0 0 4px rgba(0,0,0,.45), 0 0 0 8px #fff, 0 8px 24px rgba(0,0,0,.5); }
	.cam-lens-name { margin-top: 26px; padding: 4px 12px; font-size: 13px; }
	.cam-lens-hint { font-size: 15px; padding: 8px 18px; }
}
@media (min-width: 1600px) {
	.cam-ov:has(.cam-cv) .cam-box { width: min(980px, 100%); border-radius: 26px; }
	.cam-ov:has(.cam-cv) .cam-cv { max-height: 80vh; }
	.cam-x, .cam-flip-top { width: 50px; height: 50px; }
	.cam-flip-top { top: 74px; }
	.cam-lensbar { padding-top: 56px; padding-bottom: 18px; }
	.cam-lens-row { gap: 10px; padding: 28px calc(50% - 66px) 8px; }
	.cam-lens-item { width: 132px; }
	.cam-lens-item .cam-lens { width: 100px; height: 100px; }
	.cam-lens i { font-size: 48px; }
	.cam-lens-none span { width: 74px; height: 74px; }
	.cam-lens-item .cam-lens.on { box-shadow: 0 0 0 5px rgba(0,0,0,.45), 0 0 0 10px #fff, 0 10px 28px rgba(0,0,0,.5); }
	.cam-lens-name { margin-top: 34px; padding: 5px 14px; font-size: 15px; }
	.cam-lens-hint { font-size: 17px; padding: 10px 22px; }
}
@media (min-width: 2400px) {
	.cam-ov:has(.cam-cv) .cam-box { width: min(1280px, 100%); border-radius: 30px; }
	.cam-lens-row { gap: 12px; padding: 34px calc(50% - 82px) 10px; }
	.cam-lens-item { width: 164px; }
	.cam-lens-item .cam-lens { width: 128px; height: 128px; }
	.cam-lens i { font-size: 62px; }
	.cam-lens-none span { width: 96px; height: 96px; }
	.cam-lens-name { margin-top: 42px; font-size: 18px; }
}

/* ---- Google ads (assets/js/gads.js): drawn as a real-looking post card in the feeds and a real-looking short between Shorts ---- */
.sp-gad .gad { min-height: 100px; margin-top: 6px; }
.sp-gad.gad-hidden { display: none; }
.gad-unit { width: 100%; overflow: hidden; border-radius: 12px; }
.gad-unit .adsbygoogle[data-ad-status="unfilled"] { display: none !important; }
.gad-av { display: grid; place-items: center; font-weight: 800; font-size: 13px; background: var(--accent, #6c5ce7); color: #fff; }
.gad-count { font-size: 12.5px; opacity: .75; min-height: 0; }
.sp-gad .gad-count:empty { display: none; }
.sp-gad.gad-locked .gad-count { color: var(--accent, #6c5ce7); opacity: 1; font-weight: 600; }
.sh-item.sh-gad { background: #000; }
.sh-gad-media { display: grid; place-items: center; padding: 0 12px; }
.sh-gad-media .gad { width: min(100%, 520px); min-height: 250px; background: rgba(255,255,255,.05); border-radius: 12px; overflow: hidden; }
.sh-item.sh-gad .gad-count { font-weight: 600; }

/* Video recorder (beuRecordVideo in camera-capture.js): red record button, stop square while recording, timer */
.cam-recbtn span { background: #e5484d; }
.cam-recbtn.on span { border-radius: 8px; transform: scale(.62); }
.cam-rec-time { position: absolute; top: 14px; left: 14px; z-index: 2; padding: 3px 10px; border-radius: 999px; background: rgba(0,0,0,.55); color: #fff; font: 600 13px/1.4 var(--sans, sans-serif); }
.cam-rec-time::before { content: ''; display: inline-block; width: 8px; height: 8px; margin-right: 6px; border-radius: 50%; background: #e5484d; }

/* ---- Video clips in a friend chat (video button in the message box) ---- */
#dmChatPane .club-msg-bubble > .club-msg-video { flex: 1 1 100%; }
#dmChatPane .club-msg-video {
	display: block; width: 100%; max-width: min(320px, 100%); max-height: 380px; border-radius: 8px;
	background: #000; object-fit: contain;
}
#dmChatPane .club-msg-bubble:has(> .club-msg-video) { width: min(341px, 100%); }
@media (max-width: 420px) {
	#dmChatPane .dm-input-wrap .club-attach-btn { width: 34px; }
}

/* ---- Blocked friend: the chat stays, a notice sits under the header and messages are not sent ---- */
.dm-blocked-note {
	flex-shrink: 0; text-align: center; font-size: 12px; padding: 6px 10px;
	color: var(--danger, #d93025); background: var(--surface); border-bottom: 1px solid var(--border);
}
.dm-blocked-undo { background: none; border: 0; padding: 0 2px; font: inherit; font-weight: 600; color: var(--accent); cursor: pointer; text-decoration: underline; }

/* ============================================================
   DM photo tray + caption box on SHORT windows.
   The tray (up to 46vh of photo) and the caption box (up to 240px) both used to refuse to shrink, so on a window
   shorter than header + tray + caption + nav the compose row was pushed under the bottom nav and the caption box
   got cut off. Now the tray is the part that gives way (it shrinks and scrolls inside itself), the caption box is
   capped relative to the window height (it scrolls inside once capped), and the compose row never leaves the screen.
   ============================================================ */
#dmChatPane .dm-attach-tray {
	flex: 0 1 auto;
	min-height: 96px;
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-width: thin;
}
#dmChatPane .dm-tray-addmore { flex-shrink: 0; }
#dmChatPane .dm-input-wrap .club-msg-input { max-height: clamp(88px, 26vh, 240px); }
#dmChatPane .dm-compose { flex-shrink: 0; }

/* "Message limit reached" -> Get Verified prompt (see _dmLimitUpsell() in clubs-and-dms.js) */
.dm-upsell-box .dm-upsell-title { font-weight: 700; font-size: 16px; margin-bottom: 6px; }
.dm-upsell-box p { margin: 0; line-height: 1.45; color: var(--text-dim, var(--muted)); }
.dm-upsell-box p b { color: var(--text); }

/* While a list shows only skeleton placeholders the page must not scroll or rubber-band (see "skeleton lock" in
   scroll-guard.js): no overscroll bounce, no pull-to-refresh chaining, no scroll anchoring jump when the real items land. */
html.skel-lock, html.skel-lock body { overscroll-behavior-y: none; }
html.skel-lock * { overflow-anchor: none; }

/* FRIENDS split view: one continuous horizontal hairline. The chat header's bottom line used to stop at the vertical divider
   and meet nothing on the list side, and the strips under it (forwarding note / blocked note / pinned bar) each carried their
   own line. Now the list header is the same height as the chat header with the same bottom line (running through the divider),
   and the strips under the chat header never stack a second line on top of another. */
@media (min-width: 768px) {
	#viewFriends .fr-head {
		box-sizing: border-box;
		min-height: 61px;
		padding: 8px 6px;
		margin: 0 -8px 8px 0;          /* run under the list pane's 8px right padding, up to the divider */
		border-bottom: 1px solid var(--border);
	}
	#viewFriends #dmChatPane .dm-head { box-sizing: border-box; min-height: 61px; }
}
#dmChatPane .dm-noforward-note + .dm-blocked-note,
#dmChatPane .dm-blocked-note + .dm-pinned-bar { border-top: 0; }
#dmChatPane .dm-blocked-note { border-bottom: 1px solid var(--border); }

.dm-undecryptable{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}
.dm-undecryptable .dm-lock-icon{flex:0 0 auto;opacity:.8}
.dm-retry-btn:disabled{opacity:.6;cursor:default}

/* Search inside a DM chat (see dmSearchOpen() in clubs-and-dms.js) */

/* ============================================================
   CALL GRID (audio + video): a real CSS grid, not a wrapping flex row.
   1 -> full | 2 -> side by side (stacked on phones) | 3 -> one big tile on the left spanning both rows + two stacked on
   the right | 4 -> 2x2 | 5 -> 3 + 2 | 6 -> 3x2 | 7-9 -> 3 per row | 10+ -> 4 per row.
   A short last row stays LEFT-aligned with an empty cell (it no longer stretches across). JS sets --call-cols / --call-rows
   and data-n (see _callSplitAttrs in call.js). Appended last so it wins over the older flex rules above.
   ============================================================ */
.call-split,
.call-vgrid.call-split {
	display: grid;
	grid-template-columns: repeat(var(--call-cols, 1), minmax(0, 1fr));
	grid-template-rows: repeat(var(--call-rows, 1), minmax(0, 1fr));
	gap: var(--call-gap, 10px);
	align-content: stretch;
}
.call-split > *,
.call-vgrid.call-split > .call-vgrid-tile {
	flex: none;
	width: auto;
	min-width: 0;
	min-height: 0;
}
/* 3 people: big tile on the left (2 rows tall), two stacked on the right */
.call-split[data-n="3"] {
	grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
	grid-template-rows: repeat(2, minmax(0, 1fr));
}
.call-split[data-n="3"] > :first-child { grid-row: 1 / span 2; }
/* 2 people on a phone: stack them */
@media (max-width: 560px) {
	.call-split[data-n="2"] {
		grid-template-columns: minmax(0, 1fr);
		grid-template-rows: repeat(2, minmax(0, 1fr));
	}
}

/* ============================================================
   VIDEO CALL GRID LOOK (matches the reference): equal dark tiles, centred round avatar / initial while the camera is off,
   small name label bottom-left on the tile (no pill), live camera fills the tile. Layouts: 3 -> big left + 2 stacked,
   4 -> 2x2, 5 -> 3+2 (left-aligned), 6 -> 3x2 (see CALL GRID above).
   ============================================================ */
.call-vgrid.call-split { gap: 8px; }
.call-vgrid.call-split > .call-vgrid-tile {
	background: var(--surf2);
	border: 1px solid var(--border);
	border-radius: 12px;
}
.call-vgrid.call-split > .call-vgrid-tile .call-vgrid-fallback {
	display: flex;
	align-items: center;
	justify-content: center;
}
.call-vgrid.call-split > .call-vgrid-tile .call-vgrid-fallback-img,
.call-vgrid.call-split > .call-vgrid-tile .call-vgrid-fallback-initials {
	position: static;
	inset: auto;
	width: clamp(48px, 26%, 112px);
	height: auto;
	aspect-ratio: 1;
	border-radius: 50%;
	object-fit: cover;
	font-size: clamp(18px, 3.2vw, 34px);
}
.call-vgrid.call-split[data-n="3"] > .call-vgrid-tile:first-child .call-vgrid-fallback-img,
.call-vgrid.call-split[data-n="3"] > .call-vgrid-tile:first-child .call-vgrid-fallback-initials { width: clamp(64px, 30%, 140px); }
.call-vgrid.call-split > .call-vgrid-tile .call-vgrid-label {
	left: 10px;
	bottom: 8px;
	padding: 0;
	background: none;
	font-size: 11px;
	font-weight: 500;
	text-shadow: 0 1px 3px rgba(0, 0, 0, .7);
}
.call-vgrid.call-split.dense > .call-vgrid-tile .call-vgrid-label { font-size: 10px; }


/* ---- Chat search runs in the MAIN header search bar (global-search.js hsEnterScope) ---- */
.header-search-field.hs-scoped .hs-ph { display: none !important; }
.header-search-field.hs-scoped .header-search-input::placeholder { color: var(--muted) !important; }
.hs-scope-chip { cursor: pointer; flex: 0 1 auto; align-self: center; display: inline-flex; align-items: center; gap: 5px; max-width: 42%; margin-right: 6px; padding: 3px 4px 3px 9px; border-radius: 999px; background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent-soft); font-size: 12px; font-weight: 700; }
.hs-scope-chip { touch-action: manipulation; -webkit-tap-highlight-color: transparent; user-select: none; -webkit-user-select: none; }
.hs-scope-chip svg { flex: none; }
.hs-scope-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hs-scope-x { pointer-events: none; flex: none; width: 18px; height: 18px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: inherit; font-size: 15px; line-height: 1; cursor: pointer; }
.hs-scope-x:hover { background: rgba(128,128,150,.28); }
.hs-row.hs-msg { align-items: flex-start; }
.hs-msg-ico { color: var(--muted); }
.hs-msg-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.hs-msg-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.hs-msg-top .hs-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hs-msg-date { flex: none; font-size: 11px; color: var(--muted); }
.hs-msg-text { font-size: 13px; color: var(--muted); line-height: 1.35; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; word-break: break-word; }
mark.hs-hit { background: color-mix(in srgb, var(--accent) 38%, transparent); color: var(--text); border-radius: 3px; padding: 0 1px; }
/* padlock in the pinned strip / reply quotes (replaces the lock emoji) */
.dm-snip-ico.dm-lock-icon { opacity: .85; }


/* ---- Call split screen v2: phone layouts + tiles that scale with their share of the screen ---- */
/* tile contents follow the tile's own size (container units), so a short tile never squashes the avatar into an oval */
.call-split-tile { container-type: size; }
.call-split-tile .call-audio-avatar-ring {
	flex: 0 0 auto;
	width: clamp(36px, min(38cqw, 46cqh), 120px);
	height: auto;
	aspect-ratio: 1;
}
.call-split-tile .call-audio-avatar-fallback { font-size: clamp(16px, 11cqmin, 40px); }
.call-split-tile .call-audio-name { font-size: clamp(11px, 6.5cqmin, 15px); }
@media (max-width: 560px) {
	/* 3 people: one wide tile on top, two side by side below (the desktop "big left + two stacked" is too narrow here) */
	.call-split[data-n="3"] { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr)); }
	.call-split[data-n="3"] > :first-child { grid-row: auto; grid-column: 1 / span 2; }
	/* a stacked pair needs two rows of height, not one */
	.call-panel.call-panel-sized .call-split[data-n="2"] { height: min(62vh, max(300px, calc(2 * var(--call-row-h, 150px)))); }
}

/* DM compose bar: the sticker icon sits close to the "Write a message..." text (the button was 40px wide + the input had 10px of
   left padding, which left a ~20px gap) */
#dmChatPane .dm-input-wrap #dmStickerBtn { width: 32px; }
#dmChatPane .dm-input-wrap #dmStickerBtn + .club-msg-input { padding-left: 2px; }

.hs-scope-chip:hover { background: color-mix(in srgb, var(--accent) 32%, transparent); }
.hs-scope-chip:hover .hs-scope-x { background: rgba(128,128,150,.28); }

/* Locked DM messages: no text, no Retry -- a quiet shimmer while the app unlocks them, hidden if they stay locked */
.dm-undecryptable.dm-unlocking { display: block; min-width: 120px; height: 18px; padding: 0; }
.dm-unlock-shimmer { display: block; width: 100%; height: 100%; border-radius: 8px; background: linear-gradient(90deg, rgba(128,128,150,.12) 25%, rgba(128,128,150,.28) 50%, rgba(128,128,150,.12) 75%); background-size: 200% 100%; animation: dmUnlockShimmer 1.3s linear infinite; }
@keyframes dmUnlockShimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
#dmMessages.dm-locked-hidden .club-msg:has(.dm-unlocking) { display: none; }

/* ============================================================
   CALL UI v3 -- cleaner call card. Scoped to .call-audio-panel so nothing else is touched.
   No dot before the signal bars; one rounded card; one ping chip; calm tiles; roomy controls.
   ============================================================ */
.call-audio-panel {
	gap: 20px;
	padding: 20px 20px 22px;
	border-radius: 24px;
	border-color: color-mix(in srgb, var(--border) 70%, transparent);
	box-shadow: 0 24px 60px -12px rgba(0, 0, 0, .55);
}
.call-audio-panel .call-audio-header { align-items: center; padding: 0 2px; }
.call-audio-panel .call-audio-title { font-size: 18px; font-weight: 700; letter-spacing: -.1px; line-height: 1.2; }
.call-audio-panel .call-audio-subtitle { font-size: 11.5px; gap: 5px; margin-top: 4px; }
.call-audio-panel .call-audio-subtitle svg { opacity: .65; }

/* header: ping + signal (+ timer in group calls) live in one chip, the two actions are quiet round buttons */
.call-audio-panel .call-audio-meta { gap: 8px; }
.call-meta-chip {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	height: 30px;
	padding: 0 11px;
	border-radius: 999px;
	background: var(--surf2);
	color: var(--muted);
}
.call-meta-chip .call-ping { font-size: 12px; font-variant-numeric: tabular-nums; color: var(--text); opacity: .85; }
.call-audio-panel .call-min-btn {
	width: 30px;
	height: 30px;
	background: transparent;
	color: var(--muted);
	transition: background .15s ease, color .15s ease;
}
.call-audio-panel .call-min-btn:hover { background: var(--surf2); color: var(--text); }

/* tiles: soft surface, no hard outline; content stacks on one centre line */
.call-audio-panel .call-split { --call-gap: 12px; }
.call-audio-panel .call-split-tile {
	gap: 6px;
	padding: 14px 10px;
	border: 0;
	border-radius: 18px;
	background: color-mix(in srgb, var(--surf2) 78%, var(--surface));
	justify-content: center;
	align-items: center;
}
.call-audio-panel .call-split-tile .call-audio-avatar-ring {
	margin-bottom: 6px;
	box-shadow: 0 0 0 1px color-mix(in srgb, var(--text) 10%, transparent);
}
.call-audio-panel .call-audio-avatar-tile.speaking .call-audio-avatar-ring {
	box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent), 0 0 22px 2px color-mix(in srgb, var(--accent) 40%, transparent);
}
.call-audio-panel .call-split-tile .call-audio-name {
	display: flex;
	align-items: baseline;
	justify-content: center;
	max-width: 100%;
	font-weight: 600;
	letter-spacing: .1px;
}
.call-audio-panel .call-you { margin-left: 5px; font-size: .86em; font-weight: 400; opacity: .55; }

/* status line: signal bars + word, nothing else (the dot is gone) */
.call-audio-panel .call-audio-status {
	gap: 6px;
	height: 18px;
	font-size: 12px;
	font-weight: 500;
	color: var(--muted);
	transition: color .15s ease;
}
.call-audio-panel .call-audio-status.speaking { color: var(--accent-soft); font-weight: 600; }
.call-audio-panel .call-audio-status-icon svg { display: block; width: 14px; height: 14px; }

/* call-state line under the name (Calling... / Connecting... / timer / Relay unreachable) */
.call-audio-panel .call-callstate {
	min-height: 18px;
	line-height: 18px;
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0;
}
.call-audio-panel .call-callstate-spacer { visibility: hidden; }
.call-audio-panel .call-callstate[data-st="Relay unreachable"],
.call-audio-panel .call-callstate[data-st="Declined"] { color: var(--warn); }

/* controls: bigger touch targets, borderless, labels underneath, End stands out */
.call-audio-panel .call-controls { gap: 18px; padding-top: 2px; }
.call-audio-panel .call-audio-ctrl-wrap { gap: 8px; min-width: 56px; }
.call-audio-panel .call-ctrl-btn {
	width: 56px;
	height: 56px;
	border: 0;
	background: var(--surf2);
	box-shadow: none;
}
.call-audio-panel .call-ctrl-btn:hover { transform: none; background: color-mix(in srgb, var(--text) 10%, var(--surf2)); }
.call-audio-panel .call-ctrl-btn:active { transform: scale(.95); }
.call-audio-panel .call-ctrl-btn.active { background: color-mix(in srgb, var(--accent) 22%, var(--surf2)); color: var(--accent); }
.call-audio-panel .call-ctrl-btn.end { background: var(--err); color: #fff; }
.call-audio-panel .call-ctrl-btn.end:hover { background: color-mix(in srgb, var(--err) 88%, #000); }
.call-audio-panel .call-ctrl-btn:focus-visible,
.call-audio-panel .call-min-btn:focus-visible { outline: 2px solid var(--accent-soft); outline-offset: 2px; }
.call-audio-panel .call-ctrl-label { font-size: 11.5px; font-weight: 500; }

/* nothing inside a tile may be squeezed out of shape by a short tile */
.call-audio-panel .call-split-tile > * { flex-shrink: 0; }
.call-audio-panel .call-audio-title-block { min-width: 0; }
.call-audio-panel .call-audio-title { white-space: nowrap; }
.call-audio-panel .call-audio-subtitle { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

@media (max-width: 560px) {
	.call-audio-panel { padding: 16px 14px 18px; border-radius: 20px; }
	.call-audio-panel .call-audio-title { font-size: 17px; }
	.call-meta-chip { padding: 0 9px; }
	/* stacked 1:1: each tile gets enough height for avatar + name + state + status */
	.call-panel.call-panel-sized.call-audio-panel .call-split[data-n="2"] { height: min(72vh, 360px); }
	.call-audio-panel .call-split-tile { padding: 10px 8px; gap: 4px; }
	.call-audio-panel .call-split-tile .call-audio-avatar-ring { width: clamp(36px, min(38cqw, 40cqh), 64px); margin-bottom: 4px; }
	.call-audio-panel .call-controls { gap: 12px; }
	.call-audio-panel .call-ctrl-btn { width: 52px; height: 52px; }
	.call-audio-panel .call-audio-ctrl-wrap { min-width: 52px; }
}
@media (prefers-reduced-motion: reduce) {
	.call-audio-panel * { transition: none !important; }
}


/* ===== Shorts: shinier picture (v132) =====
   A light colour/contrast lift on the video itself (cheap GPU filter) makes compressed clips look crisper and more vivid;
   the ambient halo behind the stage is brighter and more saturated; a soft gloss falls from the top of the stage. */
.sh-item .bvp-video {
	filter: contrast(1.08) saturate(1.2) brightness(1.04);
	image-rendering: -webkit-optimize-contrast;
}
.sh-item .bvp.bvp-fullscreen .bvp-video { filter: contrast(1.06) saturate(1.15) brightness(1.03); }
.sh-ambient { filter: blur(56px) saturate(2) brightness(.9); }
.sh-item .bvp-fig::after {
	content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
	background: linear-gradient(180deg, rgba(255,255,255,.07) 0%, rgba(255,255,255,0) 28%);
	mix-blend-mode: soft-light;
}
@media (prefers-reduced-data: reduce) {
	.sh-item .bvp-video { filter: none; }
	.sh-item .bvp-fig::after { display: none; }
}


/* ============================================================
   White icons (v134) -- every UI icon is white in the DARK theme.
   Light theme (html[data-auth-theme="light"]) is untouched: its icons stay dark.
   Only icons drawn with currentColor are recoloured (nav, buttons, menus, like / repost / reply / share, header icons ...).
   Kept as they are: Home quick-link tile art (.quicklink-icon), stickers + lenses, brand logo, social-login logos,
   About-page artwork, and icons that carry their own fixed colours (fill/stroke="#ffd84d" etc.).
   ============================================================ */
html:not([data-auth-theme="light"]) svg:not(.quicklink-icon svg):not(.brand-mark svg):not(.social-btn svg):not([class*="about-"]):not([class*="sticker"]):not([class*="stk"]):not([class*="lens"]):not(.keep-color) {
	color: #fff !important;
}
/* icons coloured through explicit stroke rules */
html:not([data-auth-theme="light"]) .pdf-tbtn svg,
html:not([data-auth-theme="light"]) .pdf-tbtn-accent svg,
html:not([data-auth-theme="light"]) .no-events-icon,
html:not([data-auth-theme="light"]) .sess-chip svg {
	stroke: #fff !important;
}
/* liked / reposted state: keep it recognisable now that the colour is white (the filled shape is the signal) */
html:not([data-auth-theme="light"]) .sp-act-like.on svg,
html:not([data-auth-theme="light"]) .sh-act-like.on svg,
html:not([data-auth-theme="light"]) .sp-act-repost.on svg,
html:not([data-auth-theme="light"]) .comment-like-btn.liked svg {
	color: #fff !important;
}

/* Contact-panel lists (Pinned messages / Links / ...): press feedback matches the pinned strip -- only the label (title + text) tinies
   and returns; the round icon, the time and the row itself stay put. Beats the global [data-onclick]:active scale(.96). */
.dmi-row, .dmi-row:active { transform: none; transition: background .12s ease; }
.dmi-row-body { transform-origin: left center; transition: transform .12s ease; }
.dmi-row:active .dmi-row-body { transform: scale(0.96); }

/* Dark theme: the unselected bottom-nav labels follow the icons and are white too (the selected tab keeps the accent colour). */
html:not([data-auth-theme="light"]) .bottom-nav-btn:not(.active),
html:not([data-auth-theme="light"]) .bottom-nav-btn:not(.active) span {
	color: #fff;
}

/* Dark theme: call-control labels (Mute / Speaker / More / End ...) use the same colour as their icons instead of the muted grey. */
html:not([data-auth-theme="light"]) .call-ctrl-label {
	color: var(--text);
}

/* ---- Camera lenses (v139): BIGGER camera window (width and height), SMALLER round lens icons ---- */
.cam-ov:has(.cam-cv) { padding: 16px; }
.cam-ov:has(.cam-cv) .cam-box { width: min(880px, 100%); }
.cam-ov:has(.cam-cv) .cam-cv { aspect-ratio: 4 / 3; height: auto; min-height: 220px; max-height: 84vh; }
.cam-lensbar { padding-top: 38px; }
.cam-lens-row { gap: 4px; padding: 14px calc(50% - 32px) 4px; }
.cam-lens-item { width: 64px; }
.cam-lens-item .cam-lens { width: 46px; height: 46px; }
.cam-lens i { font-size: 22px; }
.cam-lens-none span { width: 34px; height: 34px; }
.cam-lens-name { margin-top: 15px; padding: 2px 8px; font-size: 9.5px; }
@media (max-width: 640px) {
	.cam-ov:has(.cam-cv) { padding: 0; }
	.cam-ov:has(.cam-cv) .cam-box { width: 100%; }
	.cam-ov:has(.cam-cv) .cam-cv { aspect-ratio: auto; height: 0; min-height: 0; max-height: none; }
}
@media (min-width: 1024px) {
	.cam-ov:has(.cam-cv) { padding: 24px; }
	.cam-ov:has(.cam-cv) .cam-box { width: min(1040px, 100%); }
	.cam-ov:has(.cam-cv) .cam-cv { max-height: 86vh; }
	.cam-lensbar { padding-top: 40px; padding-bottom: 12px; }
	.cam-lens-row { gap: 6px; padding: 18px calc(50% - 40px) 4px; }
	.cam-lens-item { width: 80px; }
	.cam-lens-item .cam-lens { width: 58px; height: 58px; }
	.cam-lens i { font-size: 28px; }
	.cam-lens-none span { width: 44px; height: 44px; }
	.cam-lens-item .cam-lens.on { box-shadow: 0 0 0 3px rgba(0,0,0,.45), 0 0 0 6px #fff, 0 8px 22px rgba(0,0,0,.5); }
	.cam-lens-name { margin-top: 20px; padding: 3px 10px; font-size: 11.5px; }
}
@media (min-width: 1600px) {
	.cam-ov:has(.cam-cv) .cam-box { width: min(1280px, 100%); }
	.cam-ov:has(.cam-cv) .cam-cv { max-height: 86vh; }
	.cam-lensbar { padding-top: 46px; padding-bottom: 14px; }
	.cam-lens-row { gap: 8px; padding: 22px calc(50% - 48px) 6px; }
	.cam-lens-item { width: 96px; }
	.cam-lens-item .cam-lens { width: 70px; height: 70px; }
	.cam-lens i { font-size: 34px; }
	.cam-lens-none span { width: 52px; height: 52px; }
	.cam-lens-name { margin-top: 24px; padding: 4px 12px; font-size: 13px; }
}
@media (min-width: 2400px) {
	.cam-ov:has(.cam-cv) .cam-box { width: min(1600px, 100%); }
	.cam-lens-row { gap: 10px; padding: 28px calc(50% - 60px) 8px; }
	.cam-lens-item { width: 120px; }
	.cam-lens-item .cam-lens { width: 88px; height: 88px; }
	.cam-lens i { font-size: 42px; }
	.cam-lens-none span { width: 66px; height: 66px; }
	.cam-lens-name { margin-top: 30px; font-size: 15px; }
}

/* Shorts: while our eased slide runs the browser's scroll-snap must not pull the feed around. */
.sh-scroller.sh-sliding { scroll-snap-type: none; }


/* ===== Shorts: richer colour (v141) =====
   1) the picture itself: stronger saturation / contrast and a small lift, so dark or washed-out clips come out vivid;
   2) the ambient glow (built from the live video pixels, see shorts.js _shAmbientTick) is brighter, far more saturated and now spreads
      across the whole side margins instead of a thin halo, so the colours of the clip flow out of the picture.
   Tune with the three variables below (1 = no change). */
.sh-stage { --sh-vivid-sat: 1.32; --sh-vivid-contrast: 1.1; --sh-vivid-light: 1.08; }
.sh-item .bvp-video {
	filter: contrast(var(--sh-vivid-contrast)) saturate(var(--sh-vivid-sat)) brightness(var(--sh-vivid-light));
}
.sh-item .bvp.bvp-fullscreen .bvp-video { filter: contrast(1.07) saturate(1.2) brightness(1.04); }
.sh-ambient {
	width: min(100%, 700px);   /* v154: narrower side glow (was 1000px) */
	filter: blur(56px) brightness(1.45) saturate(2.8);
	transform: translateX(-50%) scale(1.04);
}
@media (prefers-reduced-data: reduce) {
	.sh-item .bvp-video { filter: none; }
}


/* ===== Selected icon labels + bottom-nav label (v142) =====
   1) DARK theme: when an icon button is switched on (liked / reposted / CC on / hand raised / ...) its LABEL (the count or the word next
      to the icon) no longer turns --accent / --accent-soft; it keeps the colour it has when the button is off. The icon itself is
      already white in the dark theme, so the filled shape remains the "selected" signal. Hover colours are untouched.
   2) BOTTOM NAV (both themes): the selected tab's label only gets heavier (900) -- its colour is NOT changed (it stays exactly what an
      unselected label is: white in the dark theme, --muted in the light theme). The icon still fills in, as before. */
html:not([data-auth-theme="light"]) .sp-act-like.on,
html:not([data-auth-theme="light"]) .sp-act-repost.on,
html:not([data-auth-theme="light"]) .comment-like-btn.liked,
html:not([data-auth-theme="light"]) .comment-like-btn.has-likes,
html:not([data-auth-theme="light"]) .cm-like.on {
	color: var(--muted);
}
html:not([data-auth-theme="light"]) .sh-act-like.on,
html:not([data-auth-theme="light"]) .sh-act-cc.on {
	color: #fff;
}
html:not([data-auth-theme="light"]) .dmi-act-btn.on {
	color: var(--text);
}
html:not([data-auth-theme="light"]) .live-hand-raise-btn.active {
	color: inherit;
}

/* bottom nav: label = same colour selected or not, just bolder when selected (the unseen-count badge keeps its own look) */
.bottom-nav-btn.active > span:not(.nav-unseen-badge) {
	color: var(--muted);
	font-weight: 900;
}
html:not([data-auth-theme="light"]) .bottom-nav-btn.active > span:not(.nav-unseen-badge) {
	color: #fff;
}


/* ===== Shorts: logo, mouse grab, empty-search caret (v143) =====
   1) beuclub logo (medium, 38px) in the top-left corner of the Shorts stage, with a margin. It is not part of the top bar, so it stays
      put when the bar fades out; the bar's first button (speaker) is pushed right to make room for it.
   2) Mouse grab: the Shorts section shows a grab hand; while dragging it is a closed hand and nothing is selected (see shorts.js "Mouse grab").
   3) Search box: with no keyword the text caret used to sit on the left edge of the placeholder, over the first letter, and its blink made
      it look like a glitch. The caret is hidden while the box is empty (it appears as soon as something is typed); a slightly brighter
      outline shows that the box is focused. */
.sh-logo {
	position: absolute;
	z-index: 21;
	top: calc(env(safe-area-inset-top, 0px) + 11px);
	left: 10px;
	width: 38px;
	height: 38px;
	border-radius: 50%;
	object-fit: contain;
	pointer-events: none;
	user-select: none;
	-webkit-user-drag: none;
	filter: drop-shadow(0 1px 4px rgba(0, 0, 0, .45));
}
/* v156: speaker button is the left-most item of the bar (no room reserved for the logo, so the search bar gets longer);
   the logo sits right below the speaker button, centred under it. */
.sh-logo { top: calc(env(safe-area-inset-top, 0px) + 10px + 40px + 8px); left: 11px; }

@media (hover: hover) and (pointer: fine) {
	.sh-root { cursor: grab; }
	.sh-root .sh-comments, .sh-root #shortsSearchPanel, .sh-root .sp-modal { cursor: auto; }
	.sh-root.sh-grabbing, .sh-root.sh-grabbing * { cursor: grabbing !important; }
}
.sh-root.sh-grabbing { user-select: none; -webkit-user-select: none; }

/* v167: the blinking text caret (the vertical line) is hidden while a search box is EMPTY (nothing typed: the placeholder is showing) and appears again as soon as something is typed. */
.sh-search input { caret-color: #fff; }
html[data-auth-theme="light"] .sh-search input { caret-color: var(--text); }
.sh-search:focus-within:has(input:placeholder-shown) { border-color: rgba(255, 255, 255, .34); }
html[data-auth-theme="light"] .sh-search:focus-within:has(input:placeholder-shown) { border-color: rgba(23, 21, 31, .34); }

/* v145: a caption hint with no text (e.g. after a language was picked) takes no space */
.sh-cc-note:empty { display: none; }


/* v146 -- Spoken language shows the detected language by name; several languages can be spoken in one video */
.sh-cc-auto[style*="display: none"] + .sh-cc-multi { display: none; }
.sh-cc-multi { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 6px; }
.sh-cc-multi:not(:has(.sh-cc-chip)):has(select:disabled) { opacity: .6; }
.sh-cc-chips { display: contents; }
.sh-cc-chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 4px 3px 11px; border-radius: 999px; border: 1px solid var(--accent, #7c6cf0); color: var(--accent, #7c6cf0); font-size: 12.5px; font-weight: 700; }
.sh-cc-chip button { border: 0; background: transparent; color: inherit; font: inherit; font-size: 15px; line-height: 1; width: 20px; height: 20px; border-radius: 50%; cursor: pointer; padding: 0; }
.sh-cc-chip button:hover, .sh-cc-chip button:focus-visible { background: var(--accent, #7c6cf0); color: #fff; outline: none; }
#shAutoCcLangAdd { flex: 0 1 auto; max-width: 100%; padding: 5px 8px; border-radius: 999px; border: 1px dashed var(--border, #2a2a32); background: transparent; color: var(--muted, #8a8a96); font-family: var(--sans); font-size: 12.5px; font-weight: 600; }
#shAutoCcLangAdd:not(:disabled):hover { color: inherit; border-color: var(--accent, #7c6cf0); }


/* v146 -- upload dropzone: artwork above "Drag a video here" with the upload badge on its bottom-right corner; lines either side of "or" */
.sh-dz-art { position: relative; display: block; width: 150px; height: 131px; margin: 0 0 6px;
	background: url("../img/upload-art-dark.png") center / contain no-repeat; }
html[data-auth-theme="light"] .sh-dz-art { background-image: url("../img/upload-art.png"); }
.sh-dz-art .sh-dz-ic { position: absolute; right: -6px; bottom: -4px; width: 46px; height: 46px; margin: 0;
	background: #efedff; color: var(--accent, #7c6cf0); box-shadow: 0 4px 14px rgba(0, 0, 0, .35), 0 0 0 3px color-mix(in srgb, var(--surf2, #26262c) 70%, transparent); }
.sh-dz-art .sh-dz-ic svg { width: 22px; height: 22px; }
.sh-dz-or { display: flex; align-items: center; gap: 12px; width: min(240px, 80%); }
.sh-dz-or i { flex: 1 1 0; height: 1px; background: var(--border, #2a2a32); }
html:not([data-auth-theme="light"]) .sh-dz-or i { background: rgba(255, 255, 255, .16); }

/* ==========================================================================================================
   v148 -- one look for Back / Cancel / Close / Minimize (the markup carries x-cancel / x-ico / x-back)
   * x-cancel : every "Cancel" button. Same pill as the standard small button (.btn.sm), in every dialog, sheet and form.
   * x-ico    : every icon-only Close (X), Minimize (chevron down) and cancel-edit (X) control. Same 30x30 box, same
                border / fill / radius as x-cancel, same 16px glyph -- so Cancel and Minimize are visually one family.
   * x-back   : the arrow-only Back button used in the multi-step forms (the arrow itself is the one 22px / 2.4 stroke arrow).
   !important is deliberate: these controls live in many components that each style their own button.
   Photo / video overlays (camera shutter close, lightbox, image viewer, mini-player) keep their dark translucent
   disc because they sit on top of media.
   ========================================================================================================== */
.x-cancel, .btn.x-cancel, .btn.sm.x-cancel {
    display: inline-flex !important; align-items: center !important; justify-content: center !important; gap: 6px !important;
    box-sizing: border-box !important; min-width: 0 !important; width: auto !important; height: auto !important;
    padding: 5px 10px !important; margin-top: 0; border-radius: 6px !important;
    border: 1px solid var(--border) !important; background: var(--surf2) !important; color: var(--text) !important;
    font-family: var(--sans) !important; font-size: 12px !important; font-weight: 600 !important; line-height: 1.3 !important;
    letter-spacing: 0 !important; text-transform: none !important; text-align: center !important; box-shadow: none !important;
    cursor: pointer; -webkit-tap-highlight-color: transparent; touch-action: manipulation;
}
.x-cancel:hover, .btn.x-cancel:hover { background: color-mix(in srgb, var(--surf2) 78%, var(--text) 22%) !important; color: var(--text) !important; }
.x-cancel:active { transform: scale(.97); }
/* Cancel inside the action sheets (Social / Shorts): still the same button, just centred under the list */
.sp-sheet-item.x-cancel, .sh-sheet-item.x-cancel { display: flex !important; margin: 10px auto 0 !important; }
.sp-modal-head .sp-modal-cancel.x-cancel { flex: 0 0 auto; }

.x-ico, .btn.x-ico, .btn.sm.x-ico {
    display: inline-flex !important; align-items: center !important; justify-content: center !important;
    box-sizing: border-box !important; flex: 0 0 auto; width: 30px !important; height: 30px !important; min-width: 30px !important;
    padding: 0 !important; margin: 0; border-radius: 6px !important;
    border: 1px solid var(--border) !important; background: var(--surf2) !important; color: var(--text) !important;
    font-size: 0 !important; line-height: 0 !important; box-shadow: none !important;
    cursor: pointer; -webkit-tap-highlight-color: transparent; touch-action: manipulation;
}
.x-ico:hover, .btn.x-ico:hover { background: color-mix(in srgb, var(--surf2) 78%, var(--text) 22%) !important; color: var(--text) !important; }
.x-ico svg { display: block; width: 16px !important; height: 16px !important; margin: 0 !important; stroke-width: 2.2; pointer-events: none; }
.sh-up .x-ico, .sh-modal .x-ico { margin: 0 !important; }   /* the Shorts studio header used to nudge its X with a negative margin */

.btn.x-back { padding: 5px 10px; min-width: 40px; }
.btn.x-back svg, .btn.ghost.sm svg[width="22"] { display: block; }
.auth-switch-btn svg { vertical-align: -4px; margin-right: 4px; }

/* v149 -- sticker button in the Comments popup (posts) and the Shorts comments sheet; sticker comments render as animated stickers */
.cm-sticker-btn, .sh-sticker-btn {
	flex: 0 0 38px; width: 38px; height: 38px; padding: 0; border: 0; border-radius: 50%;
	display: inline-flex; align-items: center; justify-content: center;
	background: transparent; color: var(--text, #fff); cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.cm-sticker-btn:hover, .sh-sticker-btn:hover { background: var(--surf2, rgba(255, 255, 255, .08)); }
.sh-sticker-btn { color: #fff; }
html[data-auth-theme="light"] .sh-sticker-btn { color: var(--text); }
.cm-text .cm-sticker, .cm-sticker { width: 112px; max-width: 40vw; margin: 2px 0; }
.sh-c-body .sh-c-sticker { width: 96px; max-width: 36vw; margin: 2px 0; }

/* v151 -- Home banner clip slides: the muted <video> fills the slide behind the gradient + copy */
.banner-slide video.banner-clip { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; background: #08070d; }



/* ===== Dark theme: a label is the same colour as its icon (v154) =====
   The icons are white in the dark theme (v134). Their labels (counts under / beside the icon -- reply, repost, like, views, comment
   likes -- and the words on icon buttons / menu rows) used to stay muted grey or turned accent on hover / when switched on.
   Rule: in the DARK theme, an icon button, link or row that has an icon drawn directly inside it, and the label that follows that icon,
   are white too (also on hover and when selected -- the filled icon is the signal). Not touched: the LIGHT theme (dark icons, dark labels),
   danger / error / warning items (they keep red / amber), badges and dots (own colour), sub-text that is not the label, and any
   icon opted out with .keep-color (v134 exclusions: Home tile art, stickers, lenses, brand logo, social logos, About artwork). */
html:not([data-auth-theme="light"]) :is(button, a, [role="button"], [data-onclick], .sp-act):has(> svg:not(.keep-color):not([class*="sticker"]):not([class*="stk"]):not([class*="lens"])):not([class*="danger"]):not([class*="err"]):not([class*="delete"]):not([class*="warn"]):not([class*="badge"]):not(.keep-color):not(.brand):not(.social-btn) {
	color: #fff !important;
}
/* the label right after the icon (its own element, e.g. the count <span>) */
html:not([data-auth-theme="light"]) :is(button, a, [role="button"], [data-onclick], .sp-act):not([class*="danger"]):not([class*="err"]):not([class*="delete"]):not([class*="warn"]):not([class*="badge"]):not(.keep-color):not(.brand):not(.social-btn) > svg:not(.keep-color) + :is(span, b, strong, em, small, label):not([class*="badge"]):not([class*="dot"]):not([class*="unseen"]) {
	color: #fff !important;
}

/* Syllabus branch cards: each branch shows its own picture as the card background (assets/img/branches/<branch id>.webp).
   A dark gradient sits on top so the badge and name stay readable. The picture is set per card through --bi. */
.syllabus-branch-card.has-bg {
	position: relative;
	overflow: hidden;
	min-height: 104px;
	justify-content: center;
	background-color: var(--surf2);
	background-image: linear-gradient(180deg, rgba(8,8,12,.30) 0%, rgba(8,8,12,.80) 100%), var(--bi);
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}
.syllabus-branch-card.has-bg:hover { border-color: color-mix(in srgb, var(--accent) 60%, var(--border)); }
.syllabus-branch-card.has-bg .syllabus-branch-icon {
	border: 2px solid rgba(255,255,255,.55);
	box-shadow: 0 2px 10px rgba(0,0,0,.45);
}
.syllabus-branch-card.has-bg .syllabus-branch-label {
	color: #fff;
	font-weight: 700;
	text-shadow: 0 1px 6px rgba(0,0,0,.85);
}

/* ===== Shorts ambient glow: a halo around the video, not the whole screen (v168) =====
   The glow is the video (520px wide stage) plus about 160px of colour on each side -- 840px in total -- and never wider than the view
   screen. Its outer edge fades out softly (mask) so there is no hard line, and the rest of the screen stays dark. The stage still covers
   the middle, so only the two side bands show the glow. On a screen narrower than that the width is simply the screen. */
.sh-ambient {
	width: min(100%, 840px);
	transform: translateX(-50%) scale(1, 1);
	-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 14%, #000 86%, transparent 100%);
	mask-image: linear-gradient(90deg, transparent 0, #000 14%, #000 86%, transparent 100%);
}

/* ===== Search boxes: no blinking caret while empty (v167) =====
   An empty, focused search box used to blink a vertical line in front of its placeholder text. The caret is now transparent while the
   placeholder is showing (nothing typed) and returns the moment there is text. Typing, selecting and the voice button are unaffected. */
.sh-search input:placeholder-shown,
html[data-auth-theme="light"] .sh-search input:placeholder-shown,
.header-search-input:placeholder-shown {
	caret-color: transparent;
}

/* ===== Sponsor form: back arrow on the LEFT of the title (v167) =====
   Same look as the Syllabus page's back arrow (.vtop-back): a plain arrow, no box. The close (X) button stays on the right. */
.modal-title-left {
	display: flex;
	align-items: center;
	gap: 6px;
	min-width: 0;
	margin-left: -8px;   /* optical: the arrow's round hover area sits flush with the modal's left padding */
}
.modal-title-left .modal-title { margin-bottom: 0; min-width: 0; }
.modal-back {
	width: 36px;
	height: 36px;
	flex: none;
	border: 0;
	border-radius: 50%;
	background: none;
	color: var(--text);
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}
.modal-back:hover { background: var(--surf2, var(--surface)); }
.modal-back svg { width: 20px; height: 20px; }

/* ===== v158: course-level tabs (B.Tech / B.Arch / M.Tech / MBA) stay put while the content below them scrolls =====
   Syllabus, PYQs and Results: #syllabusLevelTabs, #pyqLevelTabs and #resultTabsWrap (all .result-tabs). They dock right under the
   "Back | Title" bar, which itself docks under the site header (same 1px-tuck trick, so no hairline shows between). Only what sits
   below the tabs scrolls. --vtop-h is the height of that Back | Title bar (5px + 38px back button + 10px padding = 53px). */
:root { --vtop-h: 53px; --vtabs-h: 36px; }
#viewSyllabus .result-tabs,
#viewPyq .result-tabs,
#viewResults .result-tabs {
	position: sticky;
	top: calc(var(--header-h, 0px) + var(--alert-banner-h, 0px) + var(--vtop-h) - 2px);
	z-index: 105;            /* under the Back | Title bar (110) and the site header (120), above the scrolling content */
	margin-bottom: 0;
	background: var(--surface);
	box-shadow: 0 -10px 0 0 var(--surface);   /* hides rows peeking through the gap above the tabs */
}
/* the 12px gap that sat under the tabs lives on the content now, so scrolled rows never show through it */
#viewSyllabus .result-tabs + *,
#viewPyq .result-tabs + *,
#viewResults .result-tabs + * { margin-top: 12px; }
/* the PYQ search toolbar used to dock right under the header; it now docks under the tabs */
#viewPyq .pyq-toolbar {
	top: calc(var(--header-h, 60px) + var(--alert-banner-h, 0px) + var(--vtop-h) + var(--vtabs-h) + 8px);
}
