Files
Flaro/web/app/src/style.css
T
ScarriffleandClaude Opus 5 f2b5ee633b Dialoge statt Blöcke, bessere Filter, ein Dashboard — und eine NAS-Anleitung
**Was etwas anlegt oder ändert, steht jetzt in einem Dialog.** Das Formular
zum Anlegen einer Mediathek nahm als Block in der Seite Platz für etwas weg,
das man einmal im Monat braucht — und sah aus wie eine Eingabe, die noch
aussteht. Dasselbe für Benutzer, Umbenennen, den Servernamen und die
Metadaten-Einstellungen.

Gebaut mit dem `<dialog>` des Browsers, nicht mit einem nachgebauten: Escape
schliesst, der Hintergrund wird unbedienbar, der Tastaturfokus bleibt
gefangen, Vorlesesoftware kündigt ihn als Dialog an. Jede Zeile, die das
selbst baut, ist eine Zeile, die es schlechter kann. Scheitert das
Speichern, bleibt der Dialog offen — ihn zu schliessen hiesse, die Eingabe
wegzuwerfen und die Meldung dorthin zu schreiben, wo gerade niemand hinsieht.

**Die Filter sehen nicht mehr nach Eingabe aus.** Bei Plex stehen dort
Wörter mit einem kleinen Pfeil; ein umrandetes Auswahlfeld sagt „hier fehlt
noch etwas", und das stimmt nicht. Gesetzte Filter werden farbig, daneben
steht die Anzahl, und „Filter zurücksetzen" erscheint nur, wenn es etwas
zurückzusetzen gibt. Dazu neu: gesehen / noch nicht gesehen.

Das siebt vorerst in der Oberfläche, nicht am Server — er kann es noch
nicht. Der Preis steht in der Zahl daneben: sie zeigt dann „3 von 7".

---

**„Wird abgespielt" — wer gerade was schaut.**

Abgeleitet aus den Fortschrittsmeldungen, nicht aus einer eigenen Anmeldung
am Spieler: jeder Client meldet beim Abspielen ohnehin alle zehn Sekunden,
wo er steht. Wer das als Lebenszeichen liest, braucht kein zweites
Verfahren, das ein Client vergessen könnte — bleibt die Meldung aus, läuft
der Eintrag nach einer Minute von selbst ab.

Im Arbeitsspeicher, nicht in der Datenbank: „wer schaut gerade was" ist eine
Frage über den Augenblick. Nach einem Neustart lautet die Antwort „niemand",
und das zu speichern hiesse, Wiedergaben anzuzeigen, die es nicht gibt.

Zwei Geräte desselben Menschen sind zwei Zeilen — derselbe Mensch kann auf
dem Fernseher und auf dem Telefon gleichzeitig etwas laufen lassen.
Geschlüsselt wird deshalb über das Gerätetoken, nicht über den Benutzer und
nicht über den Gerätenamen: zwei iPhones heissen beide „iPhone".

**Nur für Administratoren.** Wer wann welchen Film anschaut, ist eine
Auskunft über Menschen, nicht über den Server. In einem Haushalt mag das
harmlos sein; die Vorgabe eines offenen Projekts darf es trotzdem nicht sein.

Der Spieler meldet beim Schliessen ausdrücklich „beendet". Ohne das stünde
ein geschlossener Film noch eine Minute als „läuft" — und wer gerade den
Spieler zugemacht hat, sähe sich selbst beim Schauen zu.

---

**`docs/NAS-EINRICHTEN.md`** beantwortet die Frage, die offen war: **es ist
ein einziges Image.** Die Weboberfläche wird beim Bauen hineingelegt und vom
Server selbst ausgeliefert — ein Container, drei Volumes, kein Proxy, keine
Datenbank daneben.

Drei Wege aufs NAS: über die Container-Registry deines Gitea (die läuft —
nachgeprüft), als Datei über `docker save`, oder dort bauen. Dazu der
Schritt, den man am leichtesten überspringt und am teuersten bezahlt: die
Architektur. Ein Mac baut ohne Angabe arm64, ein Intel-NAS braucht amd64,
und `exec format error` sagt das nicht deutlich.

Eine Zahl darin war geraten und ist korrigiert: `docker save | gzip` ergibt
nicht 90 MB, sondern 230 — die Ebenen eines Images sind bereits gepackt.

378 Tests grün, sechs davon neu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-04 08:03:07 +02:00

570 lines
21 KiB
CSS

/* Flaro — Weboberfläche.
*
* 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: #1D1D22;
--raised: #26262C;
--placeholder: #202026;
--line: rgba(255, 255, 255, .08);
--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: 24px; --s-page: 32px;
--r-control: 8px; --r-card: 6px; --r-pill: 999px;
--sidebar-w: 240px;
--topbar-h: 56px;
color-scheme: dark;
}
* { box-sizing: border-box; }
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, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
.hidden { display: none !important; }
/* **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; }
/* ---------- Kopfleiste ---------- */
#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;
}
/* ---------- 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; 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: 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;
}
#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-faint);
}
#chrome button.check .zahl { color: var(--accent); }
.nav-fuss { margin-top: auto; padding-top: var(--s-loose); }
#content {
flex: 1; min-width: 0;
margin-left: var(--sidebar-w);
padding: var(--s-section) var(--s-page) var(--s-page);
}
/* ---------- Anmeldung ---------- */
#login {
min-height: 100vh; display: grid; place-items: center; padding: var(--s-section);
background: var(--canvas);
}
.login-card {
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; color: var(--accent); }
.bleiben {
display: flex; align-items: center; gap: var(--s-snug);
color: var(--text-dim); font-size: 13px; cursor: pointer; user-select: none;
}
.bleiben input { accent-color: var(--accent); width: 15px; height: 15px; margin: 0; }
/* ---------- Kopf einer Mediathek ---------- */
.seitenkopf {
display: flex; align-items: flex-start; gap: var(--s-section);
margin-bottom: var(--s-section);
}
.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);
}
/* 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;
}
.reiter button:hover { color: var(--text); }
.reiter button.check { color: var(--accent); border-bottom-color: var(--accent); }
/* Filterzeile darunter — Pillen, keine Formularfelder.
*
* **Bei Plex sieht ein Filter nicht nach Eingabe aus.** Dort stehen Wörter
* mit einem kleinen Pfeil daneben; ein umrandetes Auswahlfeld sagt „hier
* fehlt noch etwas", und das stimmt nicht — es steht ja schon etwas drin. */
.filterzeile {
display: flex; align-items: center; gap: var(--s-snug);
margin-bottom: var(--s-section); flex-wrap: wrap;
}
.filterzeile .anzahl {
background: var(--elevated); border-radius: var(--r-pill);
padding: 3px 11px; font-size: 13px; color: var(--text-dim);
font-variant-numeric: tabular-nums;
}
.pille {
position: relative; display: inline-flex; align-items: center;
}
.pille select {
appearance: none; -webkit-appearance: none;
background: none; border: 0; outline: none; cursor: pointer;
color: var(--text-dim); font-weight: 500;
padding: 6px 26px 6px 11px; border-radius: var(--r-pill);
}
.pille select:hover { background: var(--elevated); color: var(--text); }
.pille select:focus-visible { box-shadow: 0 0 0 2px var(--accent); }
/* Gesetzt heisst farbig — so sieht man ohne Lesen, dass gefiltert wird. */
.pille.gesetzt select { color: var(--accent); }
.pille::after {
content: ''; position: absolute; right: 11px; top: 50%;
width: 6px; height: 6px; margin-top: -4px;
border-right: 1.6px solid currentColor; border-bottom: 1.6px solid currentColor;
transform: rotate(45deg); color: var(--text-faint); pointer-events: none;
}
.pille.gesetzt::after { color: var(--accent); }
.filterzeile .zuruecksetzen {
background: none; border: 0; padding: 6px 4px;
color: var(--text-muted); font-size: 13px;
}
.filterzeile .zuruecksetzen:hover { color: var(--text); text-decoration: underline; }
.filterzeile .suchhuelle {
margin-left: auto; 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: 32px;
color: var(--text-muted); min-width: 220px;
}
.filterzeile .suchhuelle:focus-within { border-color: var(--accent); color: var(--text); }
.filterzeile .suchhuelle input {
flex: 1; min-width: 0; background: none; border: 0; outline: none; color: var(--text);
}
/* ---------- Kacheln ---------- */
.raster {
display: grid; gap: var(--s-loose);
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.kachel {
display: flex; flex-direction: column; gap: var(--s-snug);
background: none; border: 0; padding: 0; text-align: left; min-width: 0;
}
.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;
}
.plakat img {
width: 100%; height: 100%; object-fit: cover;
opacity: 0; transition: opacity .2s;
}
.plakat.geladen img { opacity: 1; }
.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; z-index: 2;
background: rgba(255, 255, 255, .22);
}
.fortschritt > div { height: 100%; min-width: 4px; background: var(--accent); }
.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;
}
.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); }
.reihe-kopf {
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 {
background: none; border: 0; padding: 0; font-size: 13px; color: var(--accent);
}
.reihe-kopf .mehr:hover { text-decoration: underline; }
.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; }
/* ---------- Detailseite ---------- */
.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%);
}
.detail-kopf {
display: flex; gap: var(--s-page); align-items: flex-start;
margin-bottom: var(--s-page);
}
.detail-kopf .plakat {
flex: none; width: 240px;
box-shadow: 0 12px 40px rgba(0, 0, 0, .6);
}
.detail-text { min-width: 0; flex: 1; padding-top: 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;
}
.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); }
.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);
}
.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(--line); border-top-color: var(--accent);
border-radius: 50%; animation: dreh .7s linear infinite;
}
@keyframes dreh { to { transform: rotate(360deg); } }
.attribution {
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; }
/* Eine laufende Wiedergabe im Dashboard. */
.einstellung.laeuft { gap: var(--s-snug); }
.laeuft-bild {
flex: none; width: 44px; aspect-ratio: 2 / 3;
border-radius: 3px; overflow: hidden; background: var(--placeholder);
}
.laeuft-bild img { width: 100%; height: 100%; object-fit: cover; }
.fortschritt.breit { position: static; height: 3px; border-radius: 2px; overflow: hidden; }
/* ---------- Dialoge ---------- */
/* Das `<dialog>` des Browsers: Escape schliesst, der Hintergrund ist
unbedienbar, der Fokus bleibt gefangen. Hier bleibt nur das Aussehen. */
.dlg {
width: min(520px, calc(100vw - 2 * var(--s-section)));
padding: 0; border: 1px solid var(--line);
border-radius: var(--r-control);
background: var(--surface); color: var(--text);
box-shadow: 0 24px 64px rgba(0, 0, 0, .7);
}
.dlg::backdrop { background: rgba(0, 0, 0, .62); backdrop-filter: blur(2px); }
.dlg-kopf {
display: flex; align-items: center; gap: var(--s-regular);
padding: var(--s-loose) var(--s-section);
border-bottom: 1px solid var(--line);
}
.dlg-kopf h2 { margin: 0; flex: 1; font-size: 17px; }
.dlg-zu {
width: 30px; height: 30px; display: grid; place-items: center;
background: none; border: 0; border-radius: 50%; color: var(--text-muted);
}
.dlg-zu:hover { background: var(--elevated); color: var(--text); }
.dlg-koerper {
padding: var(--s-section);
display: flex; flex-direction: column; gap: var(--s-loose);
max-height: 60vh; overflow-y: auto;
}
.dlg-feld { display: flex; flex-direction: column; gap: var(--s-snug); }
.dlg-feld.waagerecht {
flex-direction: row; align-items: center; gap: var(--s-snug); cursor: pointer;
}
.dlg-feld.waagerecht input { accent-color: var(--accent); width: 15px; height: 15px; margin: 0; }
.dlg-beschriftung { font-size: 13px; color: var(--text-dim); }
.dlg-fuss {
display: flex; justify-content: flex-end; gap: var(--s-snug);
padding: var(--s-loose) var(--s-section);
border-top: 1px solid var(--line);
}
.btn-gefahr { background: var(--destructive); color: #fff; font-weight: 600; }
.btn-gefahr:hover { background: #C53338; }
/* Der Spieler über allem. */
.pl-fill {
position: fixed; inset: 0; z-index: 100; background: #000;
}
/* ---------- Schmale Bildschirme ---------- */
@media (max-width: 860px) {
#app { flex-direction: column; }
#chrome {
position: static; width: auto; flex-direction: row; overflow-x: auto;
border-right: 0; border-bottom: 1px solid var(--line); padding: var(--s-snug);
}
#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 { 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; }
}