Die Oberfläche nach Plex' Vorbild — Hintergrund, Schriftzug, Besetzung

Nach den Bildern deiner Plex-Installation umgebaut. Übernommen ist der
Aufbau, nicht die Farbe: Plex ist bernsteingelb, Flaro bleibt Orchidee.

**Kopfleiste oben** mit Marke, Suche und Konto. Die Suche gehört dorthin und
nicht in jede Mediathek: wer einen Titel sucht, weiss oft gar nicht, in
welcher er liegt. Sie geht quer durch alles.

**Seitenleiste mit Icons** — echte SVG, keine Zeichen aus einer Schriftart.
Ein ▶ aus Unicode sieht auf jedem System anders aus, lässt sich nicht
einfärben und springt in der Grundlinie. Die aktive Zeile bekommt einen
schmalen Balken links statt eines leuchtenden Blocks; das liest sich in
einer langen Liste ruhiger.

**Die Kachel steht jetzt an einer Stelle** statt an fünfen. Sie trägt den
Haken für Gesehenes in der Ecke — wie bei Plex, ein Blick über das Raster
genügt —, die Zahl offener Folgen an derselben Stelle, den Fortschrittsbalken
unten und beim Überfahren einen Abspielknopf. Folgen sind dreizeilig: Serie,
Folgentitel, „S14 · F22".

**Reihen haben Pfeile.** Zwanzig Kacheln passen nicht in den Bildschirm, und
quer zu scrollen geht mit dem Rad nur mit Umschalttaste. Die Überschrift
nennt die Mediathek beim Namen: „Zuletzt in Dokumentationen hinzugefügt".

**Mediatheken haben drei Reiter** — Empfehlungen, Mediathek, Kategorien.
Darunter eine Filterzeile mit Genre, Sortierung und der Anzahl. Die
Kategorien sind farbige Kacheln; die Farbe kommt aus dem Namen, nicht aus
einer festen Liste, die jedes Genre kennen müsste, das TMDB je erfindet.

**Die Detailseite** hat jetzt den unscharfen Hintergrund über die ganze
Seite, den **Schriftzug des Films** statt der Systemschrift, eine Zeile mit
Jahr, Genres, Laufzeit, Altersfreigabe und Bewertung, runde Knöpfe für
gesehen/Favorit/weglegen, die Medienangaben als Tabelle — Datei, Video, Ton,
Untertitel — und zuletzt Besetzung & Stab mit runden Porträts.

**Dazu am Server:** ein Name. Ohne ihn zeigten die Clients den Hostnamen,
und die App schrieb „localhost" über die Anmeldung. Vorgabe ist der
Rechnername des Containers.

---

**Der Fehler, den das erste Bild zeigte:** die Lupe in der Suchleiste war
dreihundert Bildpunkte gross und lag über der halben Seite. Ein rohes
`<svg>` ohne Breite füllt seinen Container; die Regel galt nur für `.icon`.
Jetzt für jedes.

368 Tests grün. Am Container nachgesehen: „Der Untergang" mit eigenem
Schriftzug, FSK 12, Bewertung 7,9, Genres Drama/Historie/Kriegsfilm, beide
Tonspuren in der Tabelle, 22 Porträts darunter.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-04 01:04:27 +02:00
co-authored by Claude Opus 5
parent ede4069cd4
commit 8c395ac051
15 changed files with 1428 additions and 693 deletions
+363 -182
View File
@@ -1,292 +1,473 @@
/* Flaro — Weboberfläche
/* Flaro — Weboberfläche.
*
* Der Aufbau folgt Plex: Seitenleiste links, Inhalt als waagerechte Reihen,
* Detailseite mit grossem unscharfem Hintergrund. Die Farben nicht — Orchid
* stimmt mit der iOS-App überein, und Plex' Orange ist deren Marke.
*/
* Der Aufbau folgt Plex: Kopfleiste oben, Seitenleiste links mit Icons,
* waagerechte Reihen auf der Startseite, Plakate im Verhältnis 2:3 mit Haken
* und Fortschrittsbalken, eine Detailseite mit unscharfem Hintergrund und
* dem Schriftzug des Films.
*
* **Die Farbe nicht.** Plex ist bernsteingelb, Flaro ist Orchidee. Das
* Vorbild ist die Anordnung, nicht das Aussehen. */
:root {
--accent: #C04AC9;
--accent-dim: #8E3595;
--accent-soft: rgba(192, 74, 201, .16);
--canvas: #0B0B0D;
--surface: #141417;
--elevated: #1D1D21;
--placeholder: #26262B;
--text: #F5F5F7;
--text-dim: #A1A1A8;
--text-faint: #6E6E76;
--separator: rgba(255, 255, 255, .10);
--elevated: #1D1D22;
--raised: #26262C;
--placeholder: #202026;
--line: rgba(255, 255, 255, .08);
--r-card: 6px;
--r-control: 8px;
--r-panel: 14px;
--text: #F2F2F5;
--text-dim: #B4B4BE;
--text-muted: #8A8A95;
--text-faint: #65656F;
--destructive: #E5484D;
--s-tight: 4px; --s-snug: 8px; --s-regular: 12px;
--s-loose: 16px; --s-section: 28px;
--s-loose: 16px; --s-section: 24px; --s-page: 32px;
--r-control: 8px; --r-card: 6px; --r-pill: 999px;
--card-w: 150px;
--sidebar-w: 240px;
--topbar-h: 56px;
--font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
color-scheme: dark;
}
* { box-sizing: border-box; }
html, body {
margin: 0; padding: 0; height: 100%;
background: var(--canvas); color: var(--text);
font-family: var(--font); font-size: 14px; line-height: 1.45;
html, body { height: 100%; }
body {
margin: 0;
background: var(--canvas);
color: var(--text);
font: 400 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
-webkit-font-smoothing: antialiased;
}
button, input, select { font: inherit; color: inherit; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
.hidden { display: none !important; }
/* ---------- Gerüst ---------- */
/* **Jedes SVG bekommt eine Grösse, nicht nur die in `.icon`.** Ein rohes
`<svg>` ohne Breite füllt sonst seinen Container — und die Lupe in der
Suchleiste war dreihundert Bildpunkte gross und lag über der Seite. */
svg { width: 20px; height: 20px; display: block; flex: none; }
.icon { width: 20px; height: 20px; display: block; flex: none; }
#app { display: flex; min-height: 100%; }
/* ---------- Kopfleiste ---------- */
#chrome {
width: var(--sidebar-w); flex: 0 0 var(--sidebar-w);
background: var(--surface);
border-right: 1px solid var(--separator);
padding: var(--s-loose) 0;
display: flex; flex-direction: column; gap: var(--s-tight);
position: sticky; top: 0; height: 100vh; overflow-y: auto;
#topbar {
position: fixed; inset: 0 0 auto 0; height: var(--topbar-h); z-index: 30;
display: flex; align-items: center; gap: var(--s-loose);
padding: 0 var(--s-loose);
background: #0E0E11;
border-bottom: 1px solid var(--line);
}
#topbar .marke {
font-size: 20px; font-weight: 700; letter-spacing: -.02em;
color: var(--accent); user-select: none;
}
#topbar .suchfeld {
flex: 1; max-width: 420px;
display: flex; align-items: center; gap: var(--s-snug);
background: var(--elevated); border: 1px solid transparent;
border-radius: var(--r-pill); padding: 0 var(--s-regular); height: 34px;
color: var(--text-muted);
}
#topbar .suchfeld:focus-within { border-color: var(--accent); color: var(--text); }
#topbar .suchfeld input {
flex: 1; min-width: 0; background: none; border: 0; outline: none; color: var(--text);
}
#topbar .rechts { margin-left: auto; display: flex; align-items: center; gap: var(--s-regular); }
#topbar .konto {
display: flex; align-items: center; gap: var(--s-snug);
background: none; border: 0; color: var(--text-dim); padding: 0;
}
#topbar .konto .kreis {
width: 28px; height: 28px; border-radius: 50%;
background: var(--accent); color: #fff;
display: grid; place-items: center; font-size: 13px; font-weight: 600;
}
.marke {
font-size: 20px; font-weight: 700; letter-spacing: -.02em;
padding: 0 var(--s-loose) var(--s-loose);
color: var(--accent);
/* ---------- Grundgerüst ---------- */
#app { display: flex; min-height: 100%; padding-top: var(--topbar-h); }
#chrome {
position: fixed; top: var(--topbar-h); bottom: 0; left: 0;
width: var(--sidebar-w); overflow-y: auto;
background: #0E0E11;
border-right: 1px solid var(--line);
padding: var(--s-loose) 0;
display: flex; flex-direction: column; gap: 2px;
}
.nav-gruppe {
font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
color: var(--text-faint); padding: var(--s-loose) var(--s-loose) var(--s-tight);
font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
color: var(--text-faint); padding: var(--s-loose) var(--s-loose) var(--s-snug);
}
#chrome button {
display: block; width: calc(100% - var(--s-regular)); margin: 0 var(--s-snug);
text-align: left; background: none; border: 0; cursor: pointer;
padding: var(--s-snug) var(--s-regular); border-radius: var(--r-control);
color: var(--text-dim); font-size: 14px;
display: flex; align-items: center; gap: var(--s-regular);
background: none; border: 0; text-align: left;
padding: 9px var(--s-loose); color: var(--text-dim);
border-left: 3px solid transparent;
}
/* Jede Mediathek eine Zeile: Name links, Zahl rechts. */
#chrome button { display: flex; align-items: center; gap: var(--s-tight); }
#chrome button > span:first-child {
#chrome button > span.name {
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#chrome button .icon { flex: none; color: var(--text-muted); }
#chrome button:hover { color: var(--text); background: rgba(255, 255, 255, .04); }
#chrome button:hover .icon { color: var(--text-dim); }
/* **Der Balken links, nicht ein gefüllter Knopf.** Bei Plex markiert ein
schmaler farbiger Strich die aktive Zeile; das liest sich in einer langen
Liste ruhiger als ein leuchtender Block. */
#chrome button.check {
color: var(--accent); border-left-color: var(--accent);
background: linear-gradient(90deg, var(--accent-soft), transparent 60%);
}
#chrome button.check .icon { color: var(--accent); }
#chrome button .zahl {
flex: none; font-size: 11px; font-variant-numeric: tabular-nums;
color: var(--text-muted); background: var(--elevated);
padding: 1px 6px; border-radius: 8px;
color: var(--text-faint);
}
#chrome button.check .zahl { background: rgba(255, 255, 255, .22); color: #fff; }
#chrome button:hover { background: var(--elevated); color: var(--text); }
#chrome button.check { background: var(--accent-dim); color: #fff; }
#chrome button.check .zahl { color: var(--accent); }
.nav-fuss { margin-top: auto; padding-top: var(--s-loose); }
#content { flex: 1; min-width: 0; padding: var(--s-section); }
#content {
flex: 1; min-width: 0;
margin-left: var(--sidebar-w);
padding: var(--s-section) var(--s-page) var(--s-page);
}
/* ---------- Anmeldung ---------- */
#login {
display: flex; align-items: center; justify-content: center;
min-height: 100vh; padding: var(--s-loose);
min-height: 100vh; display: grid; place-items: center; padding: var(--s-section);
background: var(--canvas);
}
.login-card {
width: 100%; max-width: 360px; background: var(--surface);
border: 1px solid var(--separator); border-radius: var(--r-panel);
padding: var(--s-section); display: flex; flex-direction: column; gap: var(--s-regular);
width: 100%; max-width: 360px;
display: flex; flex-direction: column; gap: var(--s-regular);
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-control); padding: var(--s-section);
}
.login-card h1 { margin: 0; font-size: 26px; letter-spacing: -.02em; }
.login-card h1 { margin: 0; font-size: 26px; letter-spacing: -.02em; color: var(--accent); }
/* ---------- Bedienelemente ---------- */
/* ---------- Kopf einer Mediathek ---------- */
.field {
width: 100%; background: var(--elevated); border: 1px solid var(--separator);
border-radius: var(--r-control); padding: 10px var(--s-regular); outline: none;
.seitenkopf {
display: flex; align-items: flex-start; gap: var(--s-section);
margin-bottom: var(--s-section);
}
.field:focus { border-color: var(--accent); }
.btn {
background: var(--elevated); border: 1px solid var(--separator);
border-radius: var(--r-control); padding: 9px var(--s-loose);
cursor: pointer; white-space: nowrap;
.seitenkopf .wer { min-width: 0; }
.seitenkopf h1 { margin: 0; font-size: 22px; font-weight: 700; letter-spacing: -.02em; }
.seitenkopf .quelle {
font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--text-faint);
}
.btn:hover:not(:disabled) { background: #2A2A30; }
.btn:disabled { opacity: .5; cursor: default; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
.btn-primary:hover:not(:disabled) { background: #CB5BD3; }
.btn-row { display: flex; gap: var(--s-snug); flex-wrap: wrap; align-items: center; }
.seg { display: inline-flex; background: var(--elevated); border-radius: var(--r-control); padding: 2px; }
.seg button {
background: none; border: 0; cursor: pointer; color: var(--text-dim);
padding: 6px var(--s-loose); border-radius: 6px;
/* Reiter in der Mitte, aktiver unterstrichen — wie bei Plex. */
.reiter { display: flex; gap: var(--s-section); margin: 0 auto; }
.reiter button {
background: none; border: 0; padding: 4px 0 8px;
color: var(--text-muted); font-weight: 500;
border-bottom: 2px solid transparent;
}
.seg button.check { background: var(--accent); color: #fff; }
.reiter button:hover { color: var(--text); }
.reiter button.check { color: var(--accent); border-bottom-color: var(--accent); }
.seitentitel { margin: 0; font-size: 24px; letter-spacing: -.02em; }
.row { display: flex; gap: var(--s-regular); align-items: center; margin-bottom: var(--s-section); }
.row .field { max-width: 280px; margin-left: auto; }
/* Die Sortierung steht neben der Suche und braucht weniger Platz. */
.row .field.schmal { max-width: 190px; margin-left: auto; }
.row .field.schmal + .field { margin-left: 0; }
/* Filterzeile darunter. */
.filterzeile {
display: flex; align-items: center; gap: var(--s-regular);
margin-bottom: var(--s-section); flex-wrap: wrap;
}
.filterzeile .anzahl {
background: var(--elevated); border-radius: var(--r-card);
padding: 2px 10px; font-size: 13px; color: var(--text-dim);
font-variant-numeric: tabular-nums;
}
.filterzeile select, .filterzeile input {
background: var(--elevated); border: 1px solid transparent;
border-radius: var(--r-control); padding: 6px 10px; color: var(--text); outline: none;
}
.filterzeile select:focus, .filterzeile input:focus { border-color: var(--accent); }
.filterzeile .suche { margin-left: auto; min-width: 220px; }
/* ---------- Karten und Raster ---------- */
/* ---------- Kacheln ---------- */
.grid {
.raster {
display: grid; gap: var(--s-loose);
grid-template-columns: repeat(auto-fill, minmax(var(--card-w), 1fr));
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.card {
background: none; border: 0; padding: 0; cursor: pointer; text-align: left;
.kachel {
display: flex; flex-direction: column; gap: var(--s-snug);
background: none; border: 0; padding: 0; text-align: left; min-width: 0;
}
.card:hover .poster { transform: translateY(-3px); box-shadow: 0 10px 24px rgba(0,0,0,.5); }
.poster {
aspect-ratio: 2 / 3; width: 100%; background: var(--placeholder);
border-radius: var(--r-card); overflow: hidden; position: relative;
.plakat {
position: relative; width: 100%; aspect-ratio: 2 / 3;
background: var(--placeholder);
border-radius: var(--r-card); overflow: hidden;
transition: transform .15s ease, box-shadow .15s ease;
}
.poster img { width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .2s; }
.poster.geladen img { opacity: 1; }
.plakat img {
width: 100%; height: 100%; object-fit: cover;
opacity: 0; transition: opacity .2s;
}
.plakat.geladen img { opacity: 1; }
/* Der Fortschrittsbalken auf der Kachel. Flaro zeigt ihn ab der ersten
Sekunde — es gibt keinen Anteil, der zu klein wäre, um ihn zu merken. */
.kachel:hover .plakat {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 0, 0, .55);
}
/* Der Abspielknopf erscheint beim Überfahren, mittig. */
.schweber {
position: absolute; inset: 0; display: grid; place-items: center;
background: rgba(0, 0, 0, .45);
opacity: 0; transition: opacity .15s ease;
}
.schweber svg { width: 44px; height: 44px; color: #fff; filter: drop-shadow(0 2px 8px rgba(0,0,0,.6)); }
.kachel:hover .schweber { opacity: 1; }
/* **Haken und Zahl sitzen in derselben Ecke**, weil sie dieselbe Frage
beantworten: ist hier noch etwas offen? */
.ecke {
position: absolute; top: 0; right: 0; z-index: 2;
min-width: 26px; height: 26px; padding: 0 6px;
display: grid; place-items: center;
font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums;
border-bottom-left-radius: var(--r-card);
}
.ecke.haken { background: rgba(0, 0, 0, .72); color: #fff; }
.ecke.haken svg { width: 15px; height: 15px; }
.ecke.zahl { background: var(--accent); color: #fff; }
/* Ab der ersten Sekunde sichtbar — darum die Mindestbreite. */
.fortschritt {
position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
position: absolute; left: 0; right: 0; bottom: 0; height: 3px; z-index: 2;
background: rgba(255, 255, 255, .22);
}
/* Eine Mindestbreite, damit auch eine einzige Minute sichtbar ist — bei
2:36 Laufzeit wären das sonst anderthalb Bildpunkte. */
.fortschritt > div { height: 100%; min-width: 4px; background: var(--accent); }
/* Die Plakette mit den offenen Folgen, wie bei Plex in der Ecke. */
.offen {
position: absolute; top: 6px; right: 6px;
min-width: 18px; padding: 1px 5px; border-radius: 9px;
background: var(--accent); color: #fff;
font-size: 11px; font-weight: 600; line-height: 16px; text-align: center;
.kacheltext { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.kacheltext strong {
font-weight: 500; font-size: 13px;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rowline { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.rowline strong { font-weight: 500; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ---------- Waagerechte Reihen, wie bei Plex ---------- */
.reihe { margin-bottom: var(--s-section); }
.reihe h2 {
font-size: 17px; font-weight: 600; margin: 0;
letter-spacing: -.01em;
.kacheltext span {
font-size: 12px; color: var(--text-muted);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kacheltext .dritte { color: var(--text-faint); }
/* ---------- Waagerechte Reihen ---------- */
.reihe { margin-bottom: var(--s-page); }
/* Überschrift links, „Alle zeigen" rechts — steht mehr drin, als die Reihe
zeigt, soll die Reihe keine Sackgasse sein. */
.reihe-kopf {
display: flex; align-items: baseline; gap: var(--s-regular);
display: flex; align-items: center; gap: var(--s-regular);
margin-bottom: var(--s-regular);
}
.reihe-kopf h2 { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: -.01em; }
.reihe-kopf .mehr {
margin-left: auto; flex: none;
background: none; border: 0; cursor: pointer; padding: 0;
font-size: 13px; color: var(--accent);
background: none; border: 0; padding: 0; font-size: 13px; color: var(--accent);
}
.reihe-kopf .mehr:hover { text-decoration: underline; }
.strip {
display: grid; grid-auto-flow: column; grid-auto-columns: var(--card-w);
gap: var(--s-loose); overflow-x: auto; padding-bottom: var(--s-snug);
scrollbar-width: thin; scroll-snap-type: x proximity;
.reihe-pfeile { margin-left: auto; display: flex; gap: var(--s-tight); }
.pfeil {
width: 28px; height: 28px; display: grid; place-items: center;
background: var(--elevated); border: 0; border-radius: 50%; color: var(--text-dim);
}
.pfeil:hover { background: var(--raised); color: var(--text); }
.pfeil svg { width: 16px; height: 16px; }
.strip {
display: grid; grid-auto-flow: column; grid-auto-columns: 150px;
gap: var(--s-loose);
overflow-x: auto; scroll-snap-type: x proximity;
scrollbar-width: none; padding-bottom: var(--s-tight);
}
.strip::-webkit-scrollbar { display: none; }
.strip > * { scroll-snap-align: start; }
.strip::-webkit-scrollbar { height: 8px; }
.strip::-webkit-scrollbar-thumb { background: var(--elevated); border-radius: 4px; }
/* ---------- Detailseite ---------- */
.detail-head {
position: relative; margin: calc(var(--s-section) * -1) calc(var(--s-section) * -1) var(--s-section);
padding: var(--s-section); overflow: hidden;
.detail { position: relative; }
/* **Der Hintergrund füllt die Seite, nicht nur einen Streifen.** Bei Plex
liegt er unscharf und abgedunkelt hinter allem und gibt der Seite die
Farbe des Films. */
.detail-hintergrund {
position: fixed; top: var(--topbar-h); left: var(--sidebar-w); right: 0; bottom: 0;
z-index: -1;
background-size: cover; background-position: center top;
opacity: .28; filter: blur(2px);
mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.45) 55%, transparent 92%);
}
/* Der unscharfe Hintergrund, der die Seite trägt — Plex' auffälligster Zug. */
.detail-head::before {
content: ''; position: absolute; inset: -40px;
background-image: var(--backdrop); background-size: cover; background-position: center;
filter: blur(28px) saturate(1.2); opacity: .35; z-index: 0;
.detail-kopf {
display: flex; gap: var(--s-page); align-items: flex-start;
margin-bottom: var(--s-page);
}
.detail-head::after {
content: ''; position: absolute; inset: 0; z-index: 0;
background: linear-gradient(to bottom, rgba(11,11,13,.3), var(--canvas));
.detail-kopf .plakat {
flex: none; width: 240px;
box-shadow: 0 12px 40px rgba(0, 0, 0, .6);
}
.detail-head > * { position: relative; z-index: 1; }
.detail-text { min-width: 0; flex: 1; padding-top: var(--s-snug); }
.detail-inner { display: flex; gap: var(--s-section); margin-top: var(--s-loose); align-items: flex-start; }
.detail-inner .poster { width: 200px; flex: 0 0 200px; }
.detail-text h1 { margin: 0 0 var(--s-snug); font-size: 30px; letter-spacing: -.02em; }
.detail-text p { margin: 0; color: var(--text-dim); max-width: 60ch; }
.detail h2 { font-size: 17px; margin: var(--s-section) 0 var(--s-regular); }
/* ---------- Listen und Einstellungen ---------- */
.settings { display: flex; flex-direction: column; gap: var(--s-regular); }
.setting {
background: var(--surface); border: 1px solid var(--separator);
border-radius: var(--r-panel); padding: var(--s-loose);
display: flex; flex-direction: column; gap: var(--s-snug);
/* Der Schriftzug des Films statt der Systemschrift — wenn es einen gibt. */
.detail-text .schriftzug {
max-width: 420px; max-height: 110px; margin-bottom: var(--s-regular);
object-fit: contain; object-position: left bottom; display: block;
}
button.setting { cursor: pointer; text-align: left; width: 100%; }
button.setting:hover { background: var(--elevated); }
.detail-text h1 { margin: 0 0 var(--s-snug); font-size: 34px; font-weight: 700; letter-spacing: -.03em; }
.detail-text .unter { margin: 0 0 var(--s-regular); font-size: 17px; color: var(--text-dim); }
.hint { color: var(--text-dim); font-size: 13px; }
.tiny { color: var(--text-faint); font-size: 12px; }
.note { color: var(--text-faint); font-size: 12px; }
.error {
background: rgba(220, 60, 60, .14); border: 1px solid rgba(220, 60, 60, .4);
color: #FF9A9A; border-radius: var(--r-control); padding: var(--s-regular);
.eckdaten {
display: flex; align-items: center; gap: var(--s-regular); flex-wrap: wrap;
color: var(--text-dim); font-size: 13px; margin-bottom: var(--s-loose);
}
.empty { color: var(--text-faint); padding: var(--s-section) 0; grid-column: 1 / -1; }
.freigabe {
border: 1px solid var(--text-faint); border-radius: 3px;
padding: 0 6px; font-size: 11px; font-weight: 700; letter-spacing: .04em;
color: var(--text-dim);
}
.bewertung { display: flex; align-items: center; gap: 5px; color: var(--text); font-weight: 600; }
.bewertung svg { width: 15px; height: 15px; color: var(--accent); }
.werbezeile { color: var(--text-muted); font-style: italic; margin: 0 0 var(--s-loose); }
.beschreibung { max-width: 70ch; margin: 0 0 var(--s-section); color: var(--text-dim); }
.aktionen { display: flex; align-items: center; gap: var(--s-regular); margin-bottom: var(--s-section); flex-wrap: wrap; }
.btn {
display: inline-flex; align-items: center; gap: var(--s-snug);
background: var(--elevated); border: 0; border-radius: var(--r-pill);
padding: 9px var(--s-loose); color: var(--text);
}
.btn:hover { background: var(--raised); }
.btn:disabled { opacity: .5; cursor: default; }
.btn svg { width: 18px; height: 18px; }
.btn-primary { background: var(--accent); color: #fff; font-weight: 600; }
.btn-primary:hover { background: var(--accent-dim); }
/* Runde Knöpfe für alles ohne Beschriftung — wie Plex' Merkliste und Haken. */
.rund {
width: 38px; height: 38px; padding: 0; border-radius: 50%;
display: grid; place-items: center;
background: none; border: 1px solid var(--line); color: var(--text-dim);
}
.rund:hover { border-color: var(--text-muted); color: var(--text); }
.rund.an { background: var(--accent); border-color: var(--accent); color: #fff; }
.rund svg { width: 19px; height: 19px; }
/* Die Medienangaben als Tabelle — Plex' „Video / Ton / Untertitel". */
.medientabelle { display: grid; grid-template-columns: auto 1fr; gap: var(--s-snug) var(--s-section); margin-bottom: var(--s-section); }
.medientabelle dt { color: var(--text-muted); font-size: 13px; }
.medientabelle dd { margin: 0; font-size: 13px; }
/* Besetzung: runde Porträts, wie bei Plex. */
.besetzung { display: grid; grid-auto-flow: column; grid-auto-columns: 110px; gap: var(--s-loose); overflow-x: auto; scrollbar-width: none; }
.besetzung::-webkit-scrollbar { display: none; }
.kopf-kachel { display: flex; flex-direction: column; align-items: center; gap: var(--s-snug); text-align: center; }
.kopf-kachel .portrait {
width: 92px; height: 92px; border-radius: 50%; overflow: hidden;
background: var(--placeholder); display: grid; place-items: center;
color: var(--text-faint); font-size: 26px; font-weight: 600;
}
.kopf-kachel .portrait img { width: 100%; height: 100%; object-fit: cover; }
.kopf-kachel strong { font-size: 12px; font-weight: 500; }
.kopf-kachel span { font-size: 11px; color: var(--text-muted); }
/* Genre-Kacheln, Plex' „Kategorien". */
.kategorien { display: grid; gap: var(--s-loose); grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); }
.kategorie {
aspect-ratio: 16 / 9; border: 0; border-radius: var(--r-control);
display: grid; place-items: center; color: #fff;
font-size: 19px; font-weight: 700; letter-spacing: -.01em;
position: relative; overflow: hidden;
}
.kategorie:hover { filter: brightness(1.15); }
/* ---------- Kleinteile ---------- */
h2 { font-size: 19px; font-weight: 700; letter-spacing: -.01em; margin: 0 0 var(--s-regular); }
.seitentitel { margin: 0; font-size: 22px; font-weight: 700; letter-spacing: -.02em; }
.einstellungen { display: flex; flex-direction: column; gap: var(--s-regular); margin-bottom: var(--s-page); }
.einstellung {
background: var(--surface); border: 1px solid var(--line);
border-radius: var(--r-control); padding: var(--s-loose);
display: flex; flex-direction: column; gap: var(--s-regular);
}
.einstellung.waehlbar { text-align: left; width: 100%; }
.einstellung.waehlbar:hover { border-color: var(--accent); }
.zeile { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.zeile strong { font-weight: 600; }
.winzig { font-size: 12px; color: var(--text-muted); }
.hinweis { color: var(--text-dim); font-size: 13px; }
.feld {
background: var(--elevated); border: 1px solid transparent;
border-radius: var(--r-control); padding: 9px var(--s-regular);
color: var(--text); outline: none; min-width: 0;
}
.feld:focus { border-color: var(--accent); }
.knopfreihe { display: flex; gap: var(--s-snug); flex-wrap: wrap; align-items: center; }
.fehler {
background: rgba(229, 72, 77, .12); border: 1px solid rgba(229, 72, 77, .35);
border-radius: var(--r-control); padding: var(--s-regular); color: #FF9A9D; font-size: 13px;
}
.leer { color: var(--text-muted); padding: var(--s-section) 0; }
.spinner {
width: 22px; height: 22px; margin: var(--s-section) auto;
border: 2px solid var(--separator); border-top-color: var(--accent);
border: 2px solid var(--line); border-top-color: var(--accent);
border-radius: 50%; animation: dreh .7s linear infinite;
}
@keyframes dreh { to { transform: rotate(360deg); } }
/* ---------- Abspielen ---------- */
.pl-fill { position: fixed; inset: 0; background: #000; z-index: 50; }
/* ---------- Hinweis auf TMDB ---------- */
.attribution {
margin-top: var(--s-section); padding-top: var(--s-loose);
border-top: 1px solid var(--separator);
color: var(--text-faint); font-size: 11px; display: flex; gap: var(--s-snug); align-items: center;
display: flex; align-items: center; gap: var(--s-snug);
margin-top: var(--s-page); padding-top: var(--s-loose);
border-top: 1px solid var(--line);
color: var(--text-faint); font-size: 12px;
}
.attribution img { height: 14px; opacity: .7; }
/* ---------- Schmale Fenster ---------- */
/* Der Spieler über allem. */
.pl-fill {
position: fixed; inset: 0; z-index: 100; background: #000;
}
@media (max-width: 760px) {
/* ---------- Schmale Bildschirme ---------- */
@media (max-width: 860px) {
#app { flex-direction: column; }
#chrome {
width: 100%; flex: none; height: auto; position: static;
flex-direction: row; overflow-x: auto; padding: var(--s-snug);
border-right: 0; border-bottom: 1px solid var(--separator);
position: static; width: auto; flex-direction: row; overflow-x: auto;
border-right: 0; border-bottom: 1px solid var(--line); padding: var(--s-snug);
}
.marke, .nav-gruppe { display: none; }
#chrome button { border-left: 0; border-bottom: 3px solid transparent; white-space: nowrap; }
#chrome button.check { border-left-color: transparent; border-bottom-color: var(--accent); background: none; }
.nav-gruppe { display: none; }
.nav-fuss { margin: 0; padding: 0; display: flex; }
#content { padding: var(--s-loose); }
.detail-inner { flex-direction: column; }
.detail-inner .poster { width: 140px; flex: none; }
#content { margin-left: 0; padding: var(--s-loose); }
.detail-hintergrund { left: 0; }
.detail-kopf { flex-direction: column; gap: var(--s-loose); }
.detail-kopf .plakat { width: 160px; }
.detail-text h1 { font-size: 26px; }
#topbar .suchfeld { max-width: none; }
}