/* ==========================================================================
   mxrc.club — gemeinsames Stylesheet (Mainpage, Imagehost, Fehlerseiten)
   /memes/ bringt sein eigenes CSS mit und laedt diese Datei nicht.
   ========================================================================== */

@font-face {
	font-family: "Brandon Light";
	src: url("../media/brandon-light.woff2") format("woff2");
	font-display: swap;
}

:root {
	--accent: #a855f7;
	--accent-deep: #7b0ec9;
	--bg: #000;
	--text: #fff;
	--muted: #9a90ac;
	--line: rgba(255, 255, 255, .12);
	--glass: rgba(12, 5, 20, .55);

	--ease: cubic-bezier(.2, .7, .3, 1);
	--pad: clamp(1.25rem, 4vw, 3rem);

	/* Die Zahlen sind der Hotspot — der Punkt im Bild, der tatsaechlich klickt.
	   Die .cur-Datei bringt ihn selbst mit (8/5, in der Fingerspitze), aber
	   nicht jeder Browser liest ihn aus; ohne Angabe landet der Klickpunkt dann
	   in der Ecke. Das Schluesselwort am Ende ist Pflicht: eine cursor-Regel
	   ohne Fallback ist ungueltig und wird komplett verworfen.

	   Der Pfad ist absolut, obwohl er von hier aus relativ kuerzer waere: eine
	   relative URL in einer Custom Property wird nicht gegen die Datei
	   aufgeloest, in der sie *steht*, sondern gegen die, in der das `var()`
	   benutzt wird. In i/assets/host.css zeigte "../media/" damit auf
	   /i/media/cursor.cur — 404, und die Knoepfe der Galerie hatten wieder den
	   Standardzeiger.

	   Liegt lokal statt als Hotlink auf r2.fakecrime.bio — fremde Domain, und
	   die eigene CSP erlaubt keine externen Bildquellen. */
	--cursor: url("/assets/media/cursor.cur") 8 5, auto;

	/* Brandon Light ist eine Anzeigeschrift ohne feste Laufweite. Alles
	   maschinell Erzeugte — Upload-IDs, Groessen, der Besucherzaehler — liest
	   sich darin schlecht, 1/l und 0/O laufen ineinander. */
	--mono: ui-monospace, "Cascadia Mono", "Segoe UI Mono", Consolas, monospace;
}

*,
*::before,
*::after { margin: 0; padding: 0; box-sizing: border-box; }

html {
	-webkit-text-size-adjust: 100%;
	scrollbar-color: var(--accent-deep) transparent;
}

body {
	min-height: 100dvh;
	background: var(--bg);
	color: var(--text);
	font-family: "Brandon Light", system-ui, -apple-system, "Segoe UI", sans-serif;
	-webkit-tap-highlight-color: transparent;
	-webkit-font-smoothing: antialiased;
}

/* `cursor` vererbt sich zwar, aber das reicht hier nicht: die Browser setzen in
   ihrem eigenen Stylesheet `a:any-link { cursor: auto }`, und eine Regel — auch
   eine aus dem UA-Stylesheet — schlaegt einen geerbten Wert immer. Ohne die
   zweite Zeile haetten also ausgerechnet die Links wieder die Standard-Hand.

   Textfelder bleiben absichtlich beim Balken: im Admin-Login muss man sehen,
   wo der Text hinlaeuft. Deshalb steht hier `input, textarea` nicht mit drin. */
html { cursor: var(--cursor); }

a,
button,
label,
summary,
[role="button"] { cursor: var(--cursor); }

:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 3px;
	border-radius: 4px;
}

/* Gehört zum Schutz-Layer, steht aber hier statt in protect.js: ein per JS
   injiziertes <style> würde style-src 'unsafe-inline' in der CSP erzwingen. */
img, video, canvas {
	user-select: none;
	-webkit-user-select: none;
	-webkit-user-drag: none;
	-webkit-touch-callout: none;
}

/* Der Icon-Sprite liefert nur <symbol>-Definitionen und darf keinen Platz belegen */
.sprite { position: absolute; width: 0; height: 0; }

.sr-only {
	position: absolute;
	width: 1px; height: 1px;
	padding: 0; margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* ==========================================================================
   Eintrittsschirm

   Sichtbar per Default, damit ohne JS nichts blinkt und der :target-Fallback
   greift. Die Klasse `is-loading` setzt erst das JS — ohne JS steht direkt
   "click to enter" da, und der Klick auf #gate blendet den Schirm per CSS aus.
   ========================================================================== */

.gate {
	position: fixed;
	inset: 0;
	z-index: 50;

	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: clamp(.9rem, 2.4vh, 1.5rem);
	padding: var(--pad);

	background: #05010a;
	color: var(--text);
	text-decoration: none;
	cursor: var(--cursor);

	transition: opacity .7s var(--ease), visibility .7s var(--ease);
}

/* Ausblenden: `is-open` setzt das JS nach dem Klick, `:target` ist der
   JS-lose Weg über den Fragment-Link. */
.gate.is-open,
.gate:target {
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
}

/* [hidden] allein verliert gegen `display:flex` oben */
.gate[hidden] { display: none; }

.gate-line {
	width: min(82vw, 30rem);
	height: 1px;
	background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .45), transparent);
}

.gate-text {
	font-size: clamp(.95rem, 3.2vw, 1.65rem);
	font-weight: 400;
	letter-spacing: .18em;
	text-transform: uppercase;
	text-align: center;
	text-wrap: balance;

	/* Der Glow aus der Vorlage: enger weisser Kern, weiter lila Hof */
	text-shadow:
		0 0 12px rgba(255, 255, 255, .55),
		0 0 42px rgba(168, 85, 247, .40);
}

.gate-loading { display: none; }
.gate.is-loading .gate-loading { display: inline; }
.gate.is-loading .gate-enter { display: none; }

/* Der Balken zeigt echten Ladefortschritt und existiert deshalb nur, solange
   auch geladen wird — kein Fake-Fortschritt, der auf 90 % stehen bleibt. */
.gate-bar {
	width: min(82vw, 30rem);
	height: 2px;
	background: rgba(255, 255, 255, .10);
	border-radius: 2px;
	overflow: hidden;
	opacity: 0;
	transition: opacity .3s var(--ease);
}

.gate.is-loading .gate-bar { opacity: 1; }

.gate-fill {
	display: block;
	width: 0;
	height: 100%;
	background: linear-gradient(90deg, var(--accent-deep), var(--accent));
	box-shadow: 0 0 10px rgba(168, 85, 247, .8);
	transition: width .25s var(--ease);
}

.gate:hover .gate-text,
.gate:focus-visible .gate-text {
	text-shadow:
		0 0 14px rgba(255, 255, 255, .85),
		0 0 56px rgba(168, 85, 247, .60);
}

/* ==========================================================================
   Hintergrund
   ========================================================================== */

.bg {
	position: fixed;
	inset: 0;
	z-index: 0;
	background: #0a0410 url("../media/poster.jpg") center / cover no-repeat;
}

.bg video {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

/* Vignette + Verlauf: hält Logo und Icons lesbar, egal was im Video passiert */
.bg::after {
	content: "";
	position: absolute;
	inset: 0;
	background:
		radial-gradient(125% 95% at 50% 45%, transparent 22%, rgba(0, 0, 0, .62) 100%),
		linear-gradient(180deg, rgba(0, 0, 0, .40) 0%, transparent 26%, transparent 66%, rgba(0, 0, 0, .62) 100%);
}

/* ==========================================================================
   Bühne — Logo mit Wellenform
   ========================================================================== */

.stage {
	position: relative;
	z-index: 1;
	min-height: 100dvh;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: clamp(1rem, 2.4vh, 2rem);
	padding: var(--pad);
	/* Der Zuschlag unten haelt den Inhalt von der Player-Saeule frei. Die steht
	   `fixed` und belegt deshalb keinen Platz im Fluss — ohne Reserve legt sie
	   sich einfach ueber die Icons.

	   Nachgemessen bei 720px Fensterhoehe: Titelzeile 15px + 9px Abstand +
	   Knopf 35px + 9px + Zaehlerzeile 11px = 79px, dazu der eigene Abstand der
	   Saeule zum Fensterrand von 1.4rem — zusammen rund 101px.

	   Reserviert sind trotzdem 8.5rem = 136px. Grund ist das
	   `justify-content: center` weiter oben: der Inhalt sitzt in der Mitte des
	   Kastens, und wenn der Kasten unten schrumpft, wandert seine Mitte nur um
	   die *halbe* Differenz nach oben. Von jedem zusaetzlichen Pixel Polsterung
	   kommt also nur ein halbes beim Abstand an — die Reserve muss knapp
	   doppelt so gross sein wie die Saeule hoch ist.

	   Vorher standen hier 4.5rem. Das reichte, solange die Saeule zwei Zeilen
	   hatte; mit dem Besucherzaehler ist eine dritte dazugekommen, und die
	   Titelzeile lag in der Icon-Reihe. */
	padding-block-end: max(var(--pad), calc(env(safe-area-inset-bottom) + 8.5rem));
}

/* Die Signatur-Optik: invertiert und overlay-geblendet über dem Video */
.mark {
	/* Der dritte Wert ist der wichtige. Die Signatur war bis hierher nur an
	   `vmin` gebunden, und `vmin` ist im Querformat die Fensterhoehe — das
	   Wortbild wuchs also genau dann mit, wenn der Platz knapp wurde. Solange
	   darunter nur die Icon-Reihe stand, ging das trotzdem auf; mit dem
	   Wahlspruch kommen rund 210px dazu, und bei 1280x820 stand die Buehne
	   119px ueber der Fensterhoehe.

	   Das Logo ist 1920x1080, also wird aus einer erlaubten Hoehe die Breite
	   mal 16/9. 32vh ist nachgerechnet: bei 820px Fensterhoehe bleiben nach
	   Polsterung (184px), Wahlspruch (135px), Icons (62px), Ortszeile (19px)
	   und drei Abstaenden (59px) noch 361px fuer die Signatur — 32vh ergibt
	   352px. Bei 1000px Hoehe waeren 528px frei, belegt werden 430px.

	   Der Preis ist ehrlich: auf einem grossen Monitor ist das Wortbild jetzt
	   kleiner als vorher. Die Alternative waere gewesen, an Signatur, Wellen,
	   Abstaenden und Polsterung gleichzeitig ein paar Pixel abzuknapsen — vier
	   Zahlen, die auf jedem Geraet anders aufgehen. Eine Groesse, die sich am
	   tatsaechlich vorhandenen Platz orientiert, haelt laenger. */
	width: min(88vmin, 1040px, calc(30vh * 16 / 9));
	display: flex;
	flex-direction: column;
	align-items: center;
	mix-blend-mode: overlay;
	filter: invert(100%);
	pointer-events: none;
}

.mark canvas {
	width: 100%;
	/* Zwei Wellenformen zu je 9vmin waren auf einem 1000px hohen Fenster 180px
	   — mehr als der Wahlspruch samt Linie und Motto zusammen. 5.5vmin haelt
	   sie sichtbar, ohne der Buehne die Hoehe wegzunehmen. */
	height: clamp(22px, 5.5vmin, 104px);
	display: block;
}

.mark img {
	width: 100%;
	height: auto;
	display: block;
	user-select: none;
	-webkit-user-drag: none;
}

/* ==========================================================================
   Wahlspruch
   ========================================================================== */

.creed {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: clamp(.55rem, 1.5vh, .95rem);
	text-align: center;

	/* Der Block liegt ueber dem Video und ist reine Anzeige. Ohne das faengt
	   er Klicks ab, die eigentlich dem Hintergrund gelten. */
	pointer-events: none;

	/* Fuer den Scrim darunter. `isolation` sperrt das `z-index: -1` des
	   Pseudoelements in diesen Block ein — ohne den eigenen Stapelkontext
	   koennte es unter die Buehne rutschen und waere gar nicht mehr zu sehen. */
	position: relative;
	isolation: isolate;
}

/* Der Scrim.

   Der Wahlspruch trug bisher nur einen text-shadow. Ein Schatten setzt aber
   einen duennen dunklen Saum um die Buchstaben, und das hilft genau so lange,
   wie der helle Bildbereich kleiner ist als der Saum. Im Video faehrt bei
   Sekunde 6 der Scheinwerfer quer durch die erste Zeile — eine helle Flaeche,
   die breiter ist als jeder Schatten. Nachgesehen: die Zeile war dort faktisch
   unlesbar, der Schatten hat nichts ausgerichtet.

   Deshalb eine Flaeche statt eines Saums. Ein Kasten mit Kante waere hier
   falsch, die Seite hat sonst nirgends einen — also ein Verlauf, der zu den
   Raendern hin auf null laeuft. `ellipse 66% 62%` haelt die Mitte flach
   dunkel und blendet erst aussen aus; eine Standard-Ellipse ist genau im
   Zentrum am dunkelsten, und dann bekommt die oberste Zeile, die am Rand
   sitzt, zu wenig ab — nachgemessen, genau die Zeile blieb hell.

   Die 0.82 sind nicht frei gewaehlt: mit 0.62 schien der Scheinwerfer wieder
   durch.

   Die vielen Stuetzstellen sind der Grund, warum man den Scrim nicht sieht.
   Mit drei Stopps lief die Deckkraft von 42 % auf 0 innerhalb der letzten
   22 % — als Kurve ein Knick, und ein Knick in der Deckkraft ist im Bild eine
   sichtbare Kante quer ueber den Block. Die Stufen hier bilden ein Smoothstep
   nach: der Abstand zwischen zwei Werten wird nach aussen hin kleiner, damit
   der Uebergang nicht nur stetig ist, sondern auch seine Steigung.

   Dazu -5rem statt -2.6rem oben und unten. Weniger Platz heisst steilerer
   Ausklang, und dann nuetzen auch die Stopps nichts. Nachgemessen: der
   groessere Kasten kostet weder waagerecht noch senkrecht Ueberlauf, weil er
   absolut positioniert ist und mitten in der Buehne liegt.

   Zur Breite: `calc(-1 * var(--pad))` und nicht ein fester rem-Wert. Der
   Block ist zentriert, seine Breite plus die Buehnenpolsterung ergibt
   hoechstens die Fensterbreite — der Scrim kann also rechnerisch nie
   ueberstehen. Mit -3.5rem tat er das bei 393px um 9px und erzeugte einen
   waagerechten Scrollbalken. */
.creed::before {
	content: "";
	position: absolute;
	z-index: -1;
	inset: -5rem calc(-1 * var(--pad));
	pointer-events: none;
	background: radial-gradient(ellipse 78% 70% at 50% 50%,
		rgba(0, 0, 0, .82) 0%,
		rgba(0, 0, 0, .78) 22%,
		rgba(0, 0, 0, .66) 40%,
		rgba(0, 0, 0, .46) 56%,
		rgba(0, 0, 0, .27) 70%,
		rgba(0, 0, 0, .13) 82%,
		rgba(0, 0, 0, .05) 91%,
		transparent 100%);
}

/* Der Schatten ist kein Effekt, sondern noetig — der Text steht auf bewegtem
   Video und muss auch ueber einer hellen Kirschbluete lesbar bleiben. */
.creed-lines {
	font-size: clamp(.9rem, 2.5vw, 1.12rem);
	line-height: 1.6;
	letter-spacing: .02em;
	color: rgba(255, 255, 255, .92);
	text-shadow: 0 1px 14px rgba(0, 0, 0, .95);
}

/* Die Pointe des Dreisatzes. Die ersten beiden Zeilen stellen fest, die
   dritte fordert etwas — deshalb traegt nur sie die Akzentfarbe. Faerbte man
   alle drei, waere es wieder ein gleichfoermiger Block. */
.creed-hit {
	color: #d8b4fe;
	text-shadow:
		0 0 22px rgba(168, 85, 247, .65),
		0 1px 12px rgba(0, 0, 0, .95);
}

/* Dieselbe Linie wie am Eintrittsschirm, nur schmaler — die Seite soll eine
   Handschrift haben und nicht zwei. */
.creed-rule {
	/* Eine Linie steht ueber dem Wahlspruch, eine darunter — sie rahmen ihn
	   ein, statt ihn nur vom Motto zu trennen. Deshalb duerfen sie breiter
	   sein als der Spruch selbst: 20rem gegen die rund 17rem der laengsten
	   Zeile. Vorher waren es 11rem, also deutlich schmaler als der Text, was
	   eher nach abgeschnitten aussah als nach Rahmen.

	   2px statt 1px: auf einem Bildschirm mit doppelter Pixeldichte wird aus
	   1px CSS eine halbe Geraetezeile, und ein Verlauf, der zu den Enden hin
	   ohnehin ausblendet, verschwindet dann ueber hellen Bildstellen ganz. */
	width: min(76vw, 20rem);
	height: 2px;
	background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .7), transparent);

	/* Am Eintrittsschirm liegt dieselbe Linie auf Schwarz und braucht nichts
	   weiter. Hier liegt sie auf bewegtem Video und war ueber einer hellen
	   Bluete schlicht weg. drop-shadow statt box-shadow: box-shadow zeichnet
	   den Rahmen des Kastens nach, drop-shadow die Deckkraft des Verlaufs —
	   die Linie soll an den Enden mit ausblenden. */
	filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .95));
}

.creed-motto {
	font-size: clamp(.8rem, 2.2vw, 1.02rem);
	letter-spacing: .34em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, .92);
	text-shadow:
		0 0 20px rgba(168, 85, 247, .5),
		0 1px 12px rgba(0, 0, 0, .9);

	/* letter-spacing haengt auch hinter dem letzten Zeichen. Ohne Ausgleich
	   sitzt eine zentrierte Zeile um diesen halben Betrag zu weit links —
	   sichtbar, sobald der Abstand so gross ist wie hier. */
	text-indent: .34em;
}

/* Ortszeile unter den Icons. Bewusst kleiner und blasser als der Wahlspruch —
   sie ist Fussnote, nicht Aussage. */
.place {
	display: flex;
	align-items: center;
	gap: .45em;
	font-size: clamp(.68rem, 1.8vw, .82rem);
	letter-spacing: .12em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, .55);
	text-shadow: 0 1px 12px rgba(0, 0, 0, .95);
	pointer-events: none;
}

.place svg {
	width: 1.15em;
	height: 1.15em;
	fill: var(--accent);
	filter: drop-shadow(0 0 8px rgba(168, 85, 247, .6));
}

.place-sep { color: rgba(255, 255, 255, .28); }

/* Der Zeitzonenversatz ist eine Zahl und wird sich zweimal im Jahr aendern.
   In der Anzeigeschrift springt die Zeile dabei in der Breite, in einer
   Festbreitenschrift nicht. */
.place-tz { font-family: var(--mono); letter-spacing: .06em; }

/* ==========================================================================
   Icon-Navigation
   ========================================================================== */

/* Laesst man Flexbox frei umbrechen, entsteht je nach Fensterbreite 9 + 1 oder
   8 + 2 — eine volle Zeile und ein Rest. Deshalb steht die Anzahl pro Zeile
   explizit in `--per-row`, und die Maximalbreite wird daraus ausgerechnet statt
   als fertiger rem-Wert geraten. Aendern sich Icon-Groesse oder Luecke, rechnet
   das Limit mit — genau das hat den Sprung von neun auf zehn Icons ueberlebt.

   Flexbox und nicht Grid, obwohl Grid die Spaltenzahl direkt kann: bei
   `repeat(5, auto)` klebt eine unvollstaendige letzte Zeile linksbuendig in den
   ersten Spalten. Flexbox zentriert jede Zeile fuer sich. */
.links {
	--size: clamp(48px, 8.5vmin, 62px);
	--gap: clamp(.6rem, 1.8vw, 1.05rem);
	--per-row: 11;

	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--gap);
	max-width: calc(var(--per-row) * var(--size) + (var(--per-row) - 1) * var(--gap));
	margin-inline: auto;
}

.links a {
	position: relative;
	width: var(--size);
	height: var(--size);
	display: grid;
	place-items: center;
	color: rgba(255, 255, 255, .88);
	background: var(--glass);
	border: 1px solid var(--line);
	border-radius: 50%;
	backdrop-filter: blur(16px) saturate(1.25);
	-webkit-backdrop-filter: blur(16px) saturate(1.25);
	box-shadow: 0 10px 30px -12px rgba(0, 0, 0, .9);
	transition:
		transform .22s var(--ease),
		border-color .22s var(--ease),
		background .22s var(--ease),
		color .22s var(--ease),
		box-shadow .22s var(--ease);
}

.links a svg {
	width: 45%;
	height: 45%;
	fill: currentColor;
}

.links a:hover,
.links a:focus-visible {
	/* Seit die Icons in mehreren Zeilen stehen koennen, ragt der Tooltip der
	   oberen Zeile in die untere. Ohne z-index malen die spaeteren Icons
	   darueber, weil sie alle `position: relative` und `z-index: auto` haben
	   und die Reihenfolge im DOM entscheidet. */
	z-index: 2;
	transform: translateY(-4px);
	color: #fff;
	border-color: rgba(168, 85, 247, .65);
	background: rgba(123, 14, 201, .32);
	box-shadow: 0 14px 34px -10px rgba(123, 14, 201, .75);
}

.links a:active { transform: translateY(-1px); }

/* Abzeichen fuer den Fall, dass zwei Buttons dieselbe Marke tragen. Aktuell
   nur das "@" am Discord-Profil, damit es nicht mit dem Server-Invite
   daneben verwechselt wird. */
.links a[data-badge]::before {
	content: attr(data-badge);
	position: absolute;
	right: -1px;
	bottom: -1px;
	min-width: 1.55em;
	padding: .1em .35em;
	font-size: .58rem;
	font-weight: 700;
	letter-spacing: .04em;
	line-height: 1.5;
	text-align: center;
	color: #fff;
	background: var(--accent-deep);
	border: 1px solid rgba(0, 0, 0, .55);
	border-radius: 999px;
}

/* Tooltip — nur wo es einen echten Zeiger gibt; auf Touch ist es nutzlos */
@media (hover: hover) {
	.links a::after {
		content: attr(data-label);
		position: absolute;
		top: calc(100% + .6rem);
		left: 50%;
		translate: -50% 4px;
		padding: .3em .7em;
		font-size: .74rem;
		letter-spacing: .12em;
		text-transform: uppercase;
		white-space: nowrap;
		color: #e8dcff;
		background: rgba(10, 4, 18, .88);
		border: 1px solid var(--line);
		border-radius: 7px;
		opacity: 0;
		pointer-events: none;
		transition: opacity .18s var(--ease), translate .18s var(--ease);
	}

	.links a:hover::after,
	.links a:focus-visible::after {
		opacity: 1;
		translate: -50% 0;
	}
}

/* ==========================================================================
   Sound-Toggle
   ========================================================================== */

/* Der Player traegt jetzt die Position, der Button sitzt nur noch darin —
   sonst muessten Titelzeile und Schalter zwei getrennte fixe Positionen
   pflegen und beim Ein- und Ausblenden der Zeile wuerde der Button springen. */
.player {
	position: fixed;
	left: 50%;
	bottom: max(1.4rem, calc(env(safe-area-inset-bottom) + .6rem));
	translate: -50% 0;
	z-index: 3;

	display: flex;
	flex-direction: column;
	align-items: center;
	gap: .55rem;
}

/* Titelzeile.

   Eigener Font-Stack, nicht der Seiten-Font: "Brandon Light" ist eine reine
   Latin-Schrift, das kyrillische "Кино" faellt dort zeichenweise auf die
   Systemschrift zurueck. Halb Brandon, halb Segoe in einer Zeile sieht aus
   wie ein Fehler — dann lieber die ganze Zeile in einer Schrift. */
.track {
	font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
	font-size: clamp(.66rem, 1.5vmin, .76rem);
	letter-spacing: .16em;
	text-transform: uppercase;
	text-align: center;
	text-wrap: balance;
	color: rgba(255, 255, 255, .62);
	text-shadow: 0 2px 14px rgba(0, 0, 0, .85);

	display: flex;
	align-items: baseline;
	gap: .5em;

	opacity: 0;
	translate: 0 .4rem;
	transition: opacity .5s var(--ease), translate .5s var(--ease);
}

/* [hidden] wuerde von `display:flex` ueberstimmt */
.track[hidden] { display: none; }

.track.is-on {
	opacity: 1;
	translate: 0 0;
}

.track-artist { color: rgba(255, 255, 255, .9); }

/* Trenner als Pseudoelement, damit er nicht im Markup steht und beim Aendern
   des Titels mitgepflegt werden muss. Vorgelesen wird er trotzdem: `content`
   landet im Accessibility-Tree als StaticText ("MACAN — Кино"). Das ist hier
   in Ordnung, es liest sich wie die Zeile aussieht. */
.track-title::before {
	content: "—";
	margin-inline-end: .5em;
	color: rgba(255, 255, 255, .35);
}

.sound {
	display: inline-flex;
	align-items: center;
	gap: .6em;
	padding: .62em 1.2em;
	font: inherit;
	font-size: clamp(.72rem, 1.7vmin, .84rem);
	letter-spacing: .18em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, .82);
	background: var(--glass);
	border: 1px solid var(--line);
	border-radius: 999px;
	backdrop-filter: blur(16px);
	-webkit-backdrop-filter: blur(16px);
	cursor: var(--cursor);
	transition: border-color .2s var(--ease), background .2s var(--ease), color .2s var(--ease);
}

.sound:hover { color: #fff; border-color: rgba(168, 85, 247, .6); }

.sound .dot {
	width: .5em;
	height: .5em;
	border-radius: 50%;
	background: var(--accent-deep);
	box-shadow: 0 0 12px var(--accent-deep);
	transition: background .2s, box-shadow .2s;
}

.sound[aria-pressed="true"] .dot {
	background: #37e07a;
	box-shadow: 0 0 12px #37e07a;
}

/* Im Datensparmodus gibt es keine Musik, dann ist der Button sinnlos */
.sound[hidden] { display: none; }

/* Besucherzaehler, unter dem Button. Bewusst leiser als die Titelzeile: eine
   Zahl, die niemand gefragt hat, soll nicht mit dem Knopf um Aufmerksamkeit
   streiten. Gleiche Falle wie bei .track — `hidden` verliert gegen das
   `display: flex` von .player, es braucht die eigene Regel. */
.views {
	font-family: var(--mono);
	font-size: clamp(.6rem, 1.3vmin, .68rem);
	letter-spacing: .18em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, .38);
	text-shadow: 0 2px 14px rgba(0, 0, 0, .85);

	opacity: 0;
	transition: opacity .6s var(--ease);
}

.views[hidden] { display: none; }

.views.is-on { opacity: 1; }

.views-count { color: rgba(255, 255, 255, .68); }

/* ==========================================================================
   Fehlerseiten

   /memes/ steht bewusst nicht mehr hier: die Seite ist "The Annoying Site"
   im Original und bringt ihr komplettes CSS selbst mit. Die frueheren
   Galerie-Regeln (.page, .grid, .tile, .lightbox) sind mit ihr entfallen.
   ========================================================================== */

.back {
	margin-inline-start: auto;
	display: inline-flex;
	align-items: center;
	gap: .5em;
	padding: .55em 1.05em;
	color: rgba(255, 255, 255, .8);
	background: var(--glass);
	border: 1px solid var(--line);
	border-radius: 999px;
	text-decoration: none;
	font-size: .82rem;
	letter-spacing: .14em;
	text-transform: uppercase;
	transition: color .2s var(--ease), border-color .2s var(--ease), background .2s var(--ease);
}

.back:hover { color: #fff; border-color: rgba(168, 85, 247, .6); background: rgba(123, 14, 201, .25); }

/* Pfeil als SVG statt &larr; — Brandon Light hat den Glyph nicht und
   der Fallback sah daneben aus */
.back svg {
	width: 1em;
	height: 1em;
	fill: currentColor;
	flex: none;
}

/* Unterseiten laufen ohne Video — flacher Verlauf statt Standbild */
.bg--flat {
	background: radial-gradient(88% 66% at 50% 0%, #330956 0%, #12031c 55%, #000 100%);
}

.bg--flat::after { background: none; }

/* Zentrierte Fehlerseite */
.center {
	/* .bg ist fixed mit z-index:0 und läge sonst über dem Text */
	position: relative;
	z-index: 1;
	min-height: 100dvh;
	display: grid;
	place-items: center;
	text-align: center;
	padding: var(--pad);
}

.center .code {
	font-size: clamp(5rem, 22vmin, 11rem);
	line-height: .9;
	letter-spacing: .04em;
	background: linear-gradient(180deg, #fff 30%, rgba(168, 85, 247, .35));
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}

.center h1 {
	margin-block-start: .8rem;
	font-size: clamp(1.1rem, 3.4vw, 1.6rem);
	font-weight: 400;
	letter-spacing: .22em;
	text-transform: uppercase;
}

.center p {
	margin-block-start: .7rem;
	color: var(--muted);
	font-size: .95rem;
	letter-spacing: .05em;
}

.center .back { margin: 2rem auto 0; }

/* ==========================================================================
   Wann Video und Musik NICHT laufen

   Nur noch im Datensparmodus. `html.save-data` setzt main.js, wenn der Browser
   `saveData` meldet oder eine 2G-Verbindung; dann bleibt das Standbild aus
   `.bg` stehen (poster.jpg) und die JS-Seite haengt zusaetzlich die `src` ab,
   weil `display:none` einen laufenden Download nicht stoppt. Dieselbe Klasse
   haelt die Musik zurueck und blendet den Sound-Button aus — dort gibt es kein
   CSS-Gegenstueck, ein <audio> hat keine Darstellung.

   Zwei pauschale Sperren standen hier vorher und sind beide raus:

   `max-width: 768px` — hat das Hauptelement der Seite auf jedem Handy
   abgeschaltet, auch im WLAN. 2,7 MB sind zwei Fotos.

   `prefers-reduced-motion` — unter Windows setzt schon der allgemeine
   Schalter "Animationseffekte" dieses Flag. Ergebnis war: Ton lief, Bild
   nicht, und niemand konnte sich erklaeren warum. Die Praeferenz wirkt weiter
   auf die UI-Animationen unten; das Hintergrundvideo ist der Inhalt, nicht
   Deko.
   ========================================================================== */

.save-data .bg video { display: none; }

@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: .01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: .01ms !important;
	}
}

/* Eine Reihe aus elf Icons misst 11 x 62px + 10 x 16.8px = 850px. Daneben
   liegen zweimal --pad, das bei diesen Breiten bei 4vw steht — es bleiben also
   0.92 x Fensterbreite fuer die Zeile. Bei 960px sind das 883px, die Reihe
   passt; bei 920px nur noch 846px und es kippt zu 10 + 1.

   Darunter 6 + 5. Elf laesst sich nicht gleichmaessig aufteilen, also ist die
   Frage nur, wo der Rest hinsoll — zwei Zeilen mit einem Icon Unterschied sehen
   ruhiger aus als drei Zeilen. */
@media (max-width: 960px) {
	.links { --per-row: 6; }
}

/* Hier stand eine Abfrage `(orientation: landscape) and (max-height: 960px)`,
   die die Signatur auf 74vmin und die Wellen auf 7vmin gesetzt hat — der
   Versuch, flache Fenster (1366x768, ein Browser im halben Bildschirm) mit
   einem zweiten Satz Zahlen abzufangen.

   Sie ist raus, weil der `calc(32vh * 16 / 9)`-Term bei `.mark` dasselbe
   Problem an der Wurzel loest und in beiden Ausrichtungen greift. Zwei
   Regelsaetze fuer dieselbe Frage haetten sich ausserdem gegenseitig
   ausgehebelt: eine Media Query ersetzt den kompletten `min()`-Ausdruck, die
   Hoehenbindung waere dort also wirkungslos gewesen. */

/* Handy im Querformat, 844x390 und aehnlich. Hier hilft Verkleinern nicht mehr
   weiter — es sind nur noch 220px Platz, und davon gehen allein 111px an die
   Icons, die bei dieser Breite zwei Zeilen brauchen. Das war schon vor dem
   Besucherzaehler so; mit der alten zweizeiligen Saeule lagen die Icons
   ebenfalls unter dem Player, nur 20px statt 70px tief.

   Statt weiter an Zahlen zu drehen, die bei jedem Geraet anders aufgehen, faellt
   hier die Ursache weg: die Saeule steht nicht mehr `fixed` am unteren Rand,
   sondern im normalen Fluss unter den Icons, und der Schirm hoert auf, sich auf
   eine Fensterhoehe zwingen zu wollen. Damit kann sich nichts mehr ueberlagern
   — im schlimmsten Fall scrollt die Seite, und das ist auf einem quer gehaltenen
   Handy die richtige Antwort.

   Die Signatur wird trotzdem kleiner, damit die Seite ohne Scrollen auskommt,
   solange es irgend geht: 50vmin und 4vmin ergeben bei 390px Fensterhoehe 141px
   statt 218px. */
@media (orientation: landscape) and (max-height: 560px) {
	.mark { width: min(50vmin, 1040px); }
	.mark canvas { height: clamp(20px, 4vmin, 104px); }

	/* Die drei Zeilen samt Linie kosten hier rund 90px — bei 390px
	   Fensterhoehe mehr, als die Signatur nach dem Verkleinern noch belegt.
	   Der Wahlspruch selbst bleibt, das Beiwerk faellt weg. */
	.creed-lines,
	.creed-rule { display: none; }

	/* Die Abfrage `max-width: 960px` weiter oben bricht die Icons auf 6 + 5 um.
	   Das ist fuer schmale Fenster gedacht — ein quer gehaltenes Handy ist aber
	   844px breit und nur flach. Dort kostet der Umbruch eine zweite Zeile
	   (111px statt 48px) an genau der Stelle, an der die Hoehe fehlt, obwohl
	   elf Icons zu 48px plus zehn Luecken zu 15px nur 680px brauchen. */
	.links { --per-row: 11; }

	.stage {
		min-height: 0;
		justify-content: flex-start;
		padding-block: var(--pad);
	}

	.player {
		position: static;
		translate: none;
		margin-inline: auto;
		margin-block-end: max(var(--pad), calc(env(safe-area-inset-bottom) + .6rem));
	}
}

@media (max-width: 480px) {
	.mark { width: 86vw; }

	/* 4 + 4 + 3. Feste Werte statt der clamp()-Formeln: unter 480px liefern
	   beide ohnehin nur noch ihren unteren Anschlag. 48px ist die uebliche
	   Untergrenze fuer ein Tippziel, tiefer geht es nicht.

	   Sechs pro Zeile wuerden hier nicht mehr passen: 6 x 48 + 5 x 8 = 328px,
	   und auf einem 320px breiten Geraet bleiben nach --pad (dort am Minimum von
	   20px je Seite) nur 280px. Vier passen mit 4 x 48 + 3 x 8 = 216px locker. */
	.links {
		--size: 48px;
		--gap: .5rem;
		--per-row: 4;
	}
}
