„4 warten auf Freigabe" stand in der Mediathekenzeile, und die Liste darüber war leer — sogar versteckt. Zwei Gründe, beide behoben. Die Prüfliste wurde einmal beim Öffnen geholt und nie wieder. Findet ein laufender Scan danach etwas Neues, zählt die Zeile es mit, die Liste aber nicht. Sie hört jetzt auf dieselbe Meldung wie die Seitenleiste: Scan durch, etwas freigegeben, Mediathek geändert — nachladen. Und der Knopf gab blind frei. Auf die Frage „wo sehe ich die vier Filme?" war das keine Antwort: er veröffentlichte sie, ohne sie zu zeigen. Jetzt führt er dorthin, wo sie mit Plakat, Jahr und Dateiname stehen und sich einzeln anhaken lassen — und hebt die Stelle kurz hervor, damit man nicht weitersucht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1629 lines
63 KiB
CSS
1629 lines
63 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 {
|
|
/* --- Akzent ------------------------------------------------------------
|
|
*
|
|
* **Lilac, nicht Orchidee.** Die Apps starten ab Werk mit `#9B8AD4`
|
|
* (`AccentOption.swift`), der Web-Client stand auf `#C04AC9`. Dieselbe
|
|
* Software in zwei Farben ist keine Gestaltung, sondern Drift.
|
|
*
|
|
* Die beiden abgeleiteten Werte werden **gerechnet**, nicht gepflegt —
|
|
* sonst müsste man sie bei jedem Farbwechsel von Hand mitziehen und
|
|
* vergisst es beim zweiten Mal. */
|
|
--accent: #9B8AD4;
|
|
--accent-dim: color-mix(in srgb, var(--accent) 72%, black);
|
|
--accent-soft: color-mix(in srgb, var(--accent) 16%, transparent);
|
|
|
|
/* --- Flächen -----------------------------------------------------------
|
|
*
|
|
* Auf FlaroDesign zurückgeführt: `elevated`, `placeholder`, `text`,
|
|
* `text-dim`, `text-muted` und die Trennlinie wichen in der letzten
|
|
* Stelle ab — unsichtbar einzeln, zusammen aber genau der Grund, warum
|
|
* App und Browser nicht wie dasselbe Programm aussahen. */
|
|
--canvas: #0B0B0D;
|
|
--surface: #141417;
|
|
--elevated: #1D1D21;
|
|
--raised: #26262B;
|
|
--placeholder: #26262B;
|
|
--line: rgba(255, 255, 255, .10);
|
|
|
|
/* Kopf- und Seitenleiste. **Eigenes Token, weil es vorher als `#0E0E11`
|
|
* hart im Regelwerk stand** — und sich damit jedem Theme entzog. */
|
|
--chrome: #0E0E11;
|
|
|
|
--text: #F5F5F7;
|
|
--text-dim: #A0A0A8;
|
|
--text-muted: #6E6E76;
|
|
--text-faint: #55555C;
|
|
--destructive: #E5484D;
|
|
--destructive-dim: color-mix(in srgb, var(--destructive) 78%, black);
|
|
|
|
/* Schatten als Token, nicht fünfmal fest verdrahtet. */
|
|
--schatten-karte: 0 8px 24px rgba(0, 0, 0, .55);
|
|
--schatten-plakat: 0 12px 40px rgba(0, 0, 0, .6);
|
|
--schatten-dialog: 0 24px 64px rgba(0, 0, 0, .7);
|
|
|
|
--s-tight: 4px; --s-snug: 8px; --s-regular: 12px;
|
|
--s-loose: 16px; --s-section: 24px; --s-page: 32px;
|
|
|
|
/* --- Formen ------------------------------------------------------------
|
|
*
|
|
* **Drei Radien statt vier, und keiner mehr zufällig.** Vorher: 6 px auf
|
|
* Plakaten, 8 px auf Karten und Feldern, 999 px auf *jedem* Knopf, dazu
|
|
* zweimal nacktes 3 px. Vollrunde Primärknöpfe neben 8-px-Karten ergeben
|
|
* keine Formensprache — und sie sind der zweitstärkste Hinweis darauf,
|
|
* dass eine Oberfläche nicht entworfen, sondern zusammengesetzt wurde.
|
|
*
|
|
* 8 und 10 sind dieselben Werte wie in FlaroDesign. */
|
|
--r-karte: 8px;
|
|
--r-flaeche: 10px;
|
|
--r-knopf: 6px;
|
|
--r-pill: 999px;
|
|
|
|
/* Rückwärtsnamen, solange noch Regeln daran hängen. */
|
|
--r-card: var(--r-karte);
|
|
--r-control: var(--r-flaeche);
|
|
|
|
/* --- Schrift -----------------------------------------------------------
|
|
*
|
|
* **Eine Skala statt sechs Zufallswerten.** Die Überschriften standen auf
|
|
* 34 / 26 / 22 / 20 / 19 / 17 px — Werte ohne Verhältnis zueinander, alle
|
|
* in 700, mit fast gleichem negativem Spacing. Das erzeugt keine
|
|
* Hierarchie, sondern Rauschen. */
|
|
--t-hero: 32px;
|
|
--t-titel: 24px;
|
|
--t-abschnitt: 19px;
|
|
--t-gross: 15px;
|
|
--t-normal: 13px;
|
|
--t-klein: 11px;
|
|
|
|
--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.55 Inter, -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: var(--chrome);
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
#topbar .marke {
|
|
font-size: var(--t-abschnitt); 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: var(--chrome);
|
|
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);
|
|
display: flex; align-items: center; justify-content: space-between; gap: var(--s-snug);
|
|
}
|
|
|
|
/* Der Stift erscheint beim Überfahren der Seitenleiste — ständig sichtbar
|
|
* wäre er ein Knopf, den man einmal im Jahr braucht, an einer Stelle, an der
|
|
* jeden Tag etwas anderes gesucht wird. Bei Tastaturfokus immer. */
|
|
.nav-bearbeiten {
|
|
background: none; border: 0; padding: 2px; border-radius: var(--r-knopf);
|
|
color: var(--text-faint); opacity: 0; transition: opacity .12s ease;
|
|
}
|
|
#chrome:hover .nav-bearbeiten,
|
|
.nav-bearbeiten:focus-visible,
|
|
.nav-bearbeiten.an { opacity: 1; }
|
|
.nav-bearbeiten:hover { color: var(--text); }
|
|
.nav-bearbeiten.an { color: var(--accent); }
|
|
.nav-bearbeiten svg { width: 15px; height: 15px; display: block; }
|
|
|
|
/* ---------- Umsortieren durch Ziehen ----------
|
|
*
|
|
* Der Behälter braucht keine eigene Form; er existiert, damit beim Ziehen
|
|
* gerechnet werden kann, die wievielte Zeile unter dem Zeiger liegt.
|
|
*/
|
|
.nav-liste { display: flex; flex-direction: column; }
|
|
|
|
#chrome button.ziehbar {
|
|
cursor: grab;
|
|
/* Ohne das übernimmt der Browser die senkrechte Geste und scrollt, statt
|
|
* die Zeile zu bewegen — auf dem Telefon wäre Ziehen damit unmöglich. */
|
|
touch-action: none;
|
|
user-select: none;
|
|
position: relative;
|
|
}
|
|
#chrome button.ziehbar:active { cursor: grabbing; }
|
|
|
|
.nav-griff {
|
|
display: inline-flex; color: var(--text-faint); flex: none; margin-right: -2px;
|
|
}
|
|
.nav-griff svg { width: 14px; height: 14px; }
|
|
#chrome button.ziehbar:hover .nav-griff { color: var(--text-muted); }
|
|
|
|
/* Die gezogene Zeile liegt über den anderen und wirft einen Schatten —
|
|
* sonst sieht man nicht, dass sie abgehoben ist. */
|
|
#chrome button.wird-gezogen {
|
|
z-index: 3; cursor: grabbing;
|
|
background: var(--elevated); box-shadow: var(--schatten-dialog);
|
|
opacity: .95;
|
|
}
|
|
|
|
/* Die weichenden Zeilen gleiten, die gezogene folgt dem Zeiger ohne
|
|
* Verzögerung — eine Übergangszeit auf ihr machte sie zäh. */
|
|
#chrome .nav-liste.zieht button:not(.wird-gezogen) {
|
|
transition: transform .14s ease;
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
#chrome .nav-liste.zieht button:not(.wird-gezogen) { transition: none; }
|
|
}
|
|
|
|
#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: var(--t-titel); 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: var(--t-titel); 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 .auswahl {
|
|
background: none; border: 0; color: var(--text-dim); font-weight: 500;
|
|
padding: 6px 11px; border-radius: var(--r-pill);
|
|
}
|
|
.pille .auswahl:hover { background: var(--elevated); color: var(--text); }
|
|
/* Gesetzt heisst farbig — so sieht man ohne Lesen, dass gefiltert wird. */
|
|
.pille.gesetzt .auswahl { color: var(--accent); }
|
|
|
|
/* ---------- Auswahlfeld ----------
|
|
*
|
|
* Ersetzt <select>. Dessen Liste zeichnet das Betriebssystem: mitten in der
|
|
* dunklen Oberfläche erschien ein helles Systemmenü, das sich weder färben
|
|
* noch runden lässt. Die Liste hier ist dieselbe Fläche wie das Kontextmenü
|
|
* — gleicher Rahmen, gleiche Radien, gleicher Schatten.
|
|
*/
|
|
.auswahl {
|
|
display: inline-flex; align-items: center; gap: 6px;
|
|
background: var(--elevated); border: 1px solid var(--line);
|
|
border-radius: var(--r-knopf); cursor: pointer;
|
|
color: var(--text-dim); font-size: var(--t-normal); font-weight: 500;
|
|
padding: 7px 10px; text-align: left;
|
|
}
|
|
.auswahl:hover { color: var(--text); }
|
|
.auswahl > span:first-child {
|
|
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
.auswahl-pfeil { display: inline-flex; color: var(--text-faint); }
|
|
.auswahl-pfeil svg { width: 15px; height: 15px; }
|
|
.auswahl[aria-expanded='true'] .auswahl-pfeil { transform: rotate(180deg); }
|
|
|
|
.auswahl-liste {
|
|
position: fixed; z-index: 120;
|
|
max-height: min(50vh, 360px); overflow-y: auto;
|
|
padding: var(--s-tight);
|
|
background: var(--elevated);
|
|
border: 1px solid var(--line); border-radius: var(--r-flaeche);
|
|
box-shadow: var(--schatten-dialog);
|
|
display: flex; flex-direction: column;
|
|
}
|
|
.auswahl-punkt {
|
|
background: none; border: 0; border-radius: var(--r-knopf);
|
|
padding: 8px var(--s-regular); text-align: left; white-space: nowrap;
|
|
color: var(--text-dim); font-size: var(--t-normal);
|
|
}
|
|
.auswahl-punkt:hover { background: var(--raised); color: var(--text); }
|
|
/* Das Gewählte trägt die Akzentfarbe — ohne Haken, der die Breite verschiebt. */
|
|
.auswahl-punkt.gewaehlt { color: var(--accent); font-weight: 600; }
|
|
|
|
.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-karte); overflow: hidden;
|
|
transition: transform .18s ease-out, box-shadow .18s ease-out;
|
|
}
|
|
.plakat img {
|
|
width: 100%; height: 100%; object-fit: cover;
|
|
opacity: 0; transition: opacity .2s;
|
|
}
|
|
.plakat.geladen img { opacity: 1; }
|
|
|
|
/* **Hover mit Inhalt.** Zwei Bildpunkte Anheben waren kaum zu sehen; ein
|
|
leichtes Hochskalieren hebt die Kachel aus der Reihe heraus, so wie bei
|
|
Netflix. Nur `transform` und `box-shadow`, damit nichts umbricht. */
|
|
.kachel:hover .plakat,
|
|
.kachel:focus-visible .plakat {
|
|
transform: scale(1.04);
|
|
box-shadow: var(--schatten-karte);
|
|
z-index: 3;
|
|
}
|
|
|
|
/* **Der Abspielknopf als Kreis unten links, nicht als Vollflächen-Overlay.**
|
|
Das Overlay legte sich über das ganze Plakat und verdeckte genau das Bild,
|
|
nach dem man gerade sucht. */
|
|
.schweber {
|
|
position: absolute; left: var(--s-snug); bottom: var(--s-snug);
|
|
display: grid; place-items: center;
|
|
width: 38px; height: 38px; border-radius: 50%;
|
|
background: rgba(10, 10, 12, .78);
|
|
backdrop-filter: blur(6px);
|
|
opacity: 0; transform: translateY(6px);
|
|
transition: opacity .18s ease-out, transform .18s ease-out;
|
|
}
|
|
.schweber svg { width: 20px; height: 20px; color: #fff; }
|
|
.kachel:hover .schweber,
|
|
.kachel:focus-visible .schweber { opacity: 1; transform: none; }
|
|
|
|
/* **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 {
|
|
/* **Lauter als die Zeile darunter.** Mit 13 px/500 war der Titel leiser
|
|
als manche Metazeile daneben — die Typografie erzeugte damit keine
|
|
Hierarchie, sondern Rauschen. */
|
|
font-weight: 600; font-size: 14px; color: var(--text);
|
|
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: var(--t-abschnitt); font-weight: 700; letter-spacing: -.015em; }
|
|
.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. */
|
|
/* **Über die volle Breite, nicht erst hinter der Seitenleiste.**
|
|
*
|
|
* Vorher begann das Bild bei `left: 240px` und war mit `opacity: .28` und
|
|
* `blur(2px)` weder Bild noch Fläche — ein grau verwischter Rest, der nichts
|
|
* tat. Jetzt taucht es die Seite in die Farbe des Films, so wie bei Plex. */
|
|
.detail-hintergrund {
|
|
position: fixed; inset: 0;
|
|
z-index: -1;
|
|
background-size: cover; background-position: center top;
|
|
opacity: .45; filter: blur(28px) saturate(1.1);
|
|
/* Weiter hochgezogen als das Bild selbst: die Unschärfe würde sonst am
|
|
Rand als harte Kante sichtbar. */
|
|
transform: scale(1.1);
|
|
mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.55) 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: var(--t-hero); 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-knopf);
|
|
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; }
|
|
/* **Der Primärknopf ist hell, nicht farbig.**
|
|
*
|
|
* Genau das macht Netflix: der Abspielknopf ist weiss mit schwarzer Schrift,
|
|
* die Markenfarbe erscheint nur im Schriftzug. Der Akzent sass hier auf neun
|
|
* Dingen gleichzeitig — Marke, Navigation, Knopf, Fortschritt, Plakette,
|
|
* Stern, Fokus, Filter, Kontokreis. Wenn alles Akzent ist, markiert er
|
|
* nichts. */
|
|
.btn-primary { background: var(--text); color: var(--canvas); font-weight: 600; }
|
|
.btn-primary:hover { background: #fff; }
|
|
.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: var(--t-abschnitt); font-weight: 700; letter-spacing: -.015em; margin: 0 0 var(--s-regular); }
|
|
.seitentitel { margin: 0; font-size: var(--t-titel); 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); } }
|
|
/* Derselbe Kreis, nur neben Text statt über einer leeren Fläche. */
|
|
.spinner.klein { width: 14px; height: 14px; margin: 0; flex: none; }
|
|
|
|
.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;
|
|
}
|
|
/* Im Bilddialog ist die Höhe fest: so springt die Fläche beim Reiterwechsel
|
|
* nicht, und der Reiter bleibt an derselben Stelle. */
|
|
.dlg-breit .dlg-koerper { height: 62vh; max-height: none; }
|
|
.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: var(--destructive-dim); }
|
|
|
|
/* ---------- Der Spieler ----------
|
|
*
|
|
* **Eigene Steuerung statt der eingebauten.** Die Leiste des Browsers kennt
|
|
* weder Tonspuren noch Qualitätsstufen — und die Tonspurwahl ist der Grund,
|
|
* aus dem es diesen Server gibt. Ausserdem sah sie, wie der Benutzer sagte,
|
|
* „gefühlt von 1930" aus. */
|
|
.pl {
|
|
position: fixed; inset: 0; z-index: 150; background: #000;
|
|
display: grid; place-items: center; outline: none;
|
|
}
|
|
.pl-video { width: 100%; height: 100%; object-fit: contain; background: #000; }
|
|
|
|
.pl-dreher { position: absolute; margin: 0; }
|
|
|
|
/* Kopf und Leiste blenden sich weg, während etwas läuft. */
|
|
.pl-kopf, .pl-leiste {
|
|
position: absolute; left: 0; right: 0; z-index: 2;
|
|
display: flex; align-items: center; gap: var(--s-regular);
|
|
padding: 14px 18px;
|
|
transition: opacity .2s ease-out, transform .2s ease-out;
|
|
}
|
|
.pl-kopf {
|
|
top: 0; justify-content: space-between;
|
|
background: linear-gradient(to bottom, rgba(0, 0, 0, .78), transparent);
|
|
padding-top: max(14px, env(safe-area-inset-top));
|
|
}
|
|
.pl-leiste {
|
|
bottom: 0; flex-direction: column; align-items: stretch; gap: 8px;
|
|
background: linear-gradient(to top, rgba(0, 0, 0, .86), transparent);
|
|
padding-bottom: max(14px, env(safe-area-inset-bottom));
|
|
}
|
|
.pl-still .pl-kopf { opacity: 0; transform: translateY(-8px); pointer-events: none; }
|
|
.pl-still .pl-leiste { opacity: 0; transform: translateY(8px); pointer-events: none; }
|
|
.pl-still .pl-video { cursor: none; }
|
|
|
|
.pl-titel {
|
|
font-size: 15px; font-weight: 600; color: #fff;
|
|
text-shadow: 0 1px 3px rgba(0, 0, 0, .7);
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
|
|
.pl-unten { display: flex; align-items: center; justify-content: space-between; gap: var(--s-regular); }
|
|
.pl-gruppe { display: flex; align-items: center; gap: 6px; min-width: 0; }
|
|
|
|
/* **44 Bildpunkte, nicht 32.** Darunter trifft ein Finger daneben, und ein
|
|
Fehlgriff auf der Zeitleiste wirft einen aus dem Film. */
|
|
.pl-rund {
|
|
display: grid; place-items: center;
|
|
width: 44px; height: 44px; flex: 0 0 auto;
|
|
border: 0; border-radius: 50%; background: transparent; color: #fff;
|
|
cursor: pointer; transition: background .15s;
|
|
}
|
|
.pl-rund:hover, .pl-rund:focus-visible { background: rgba(255, 255, 255, .16); }
|
|
.pl-rund svg { width: 22px; height: 22px; }
|
|
|
|
.pl-stand, .pl-rest {
|
|
font-size: 13px; color: rgba(255, 255, 255, .88);
|
|
font-variant-numeric: tabular-nums; white-space: nowrap;
|
|
}
|
|
|
|
/* Zeit- und Lautstärkeregler: dieselbe Form, verschiedene Breite. */
|
|
.pl-zeit, .pl-laut {
|
|
-webkit-appearance: none; appearance: none;
|
|
height: 20px; background: transparent; cursor: pointer; margin: 0;
|
|
}
|
|
.pl-zeit { width: 100%; }
|
|
.pl-laut { width: 84px; flex: 0 0 auto; }
|
|
|
|
.pl-zeit::-webkit-slider-runnable-track, .pl-laut::-webkit-slider-runnable-track {
|
|
height: 4px; border-radius: 2px;
|
|
background: linear-gradient(to right, var(--accent) var(--anteil, 0%),
|
|
rgba(255, 255, 255, .28) var(--anteil, 0%));
|
|
}
|
|
.pl-laut::-webkit-slider-runnable-track { background: rgba(255, 255, 255, .28); }
|
|
|
|
.pl-zeit::-moz-range-track, .pl-laut::-moz-range-track {
|
|
height: 4px; border-radius: 2px; background: rgba(255, 255, 255, .28);
|
|
}
|
|
.pl-zeit::-moz-range-progress { height: 4px; border-radius: 2px; background: var(--accent); }
|
|
|
|
.pl-zeit::-webkit-slider-thumb, .pl-laut::-webkit-slider-thumb {
|
|
-webkit-appearance: none; appearance: none;
|
|
width: 14px; height: 14px; margin-top: -5px;
|
|
border-radius: 50%; background: #fff; border: 0;
|
|
box-shadow: 0 1px 4px rgba(0, 0, 0, .5);
|
|
}
|
|
.pl-zeit::-moz-range-thumb, .pl-laut::-moz-range-thumb {
|
|
width: 14px; height: 14px; border-radius: 50%; background: #fff; border: 0;
|
|
}
|
|
.pl-zeit:focus-visible, .pl-laut:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
|
|
|
|
/* Die Menüs gehen nach oben auf — nach unten wäre ausserhalb des Bildes. */
|
|
.pl-menue { position: relative; }
|
|
.pl-liste {
|
|
position: absolute; bottom: calc(100% + 8px); right: 0;
|
|
display: none; flex-direction: column; min-width: 180px;
|
|
padding: 6px; background: rgba(20, 20, 23, .96);
|
|
border: 1px solid var(--line); border-radius: var(--r-karte);
|
|
box-shadow: 0 12px 36px rgba(0, 0, 0, .6);
|
|
max-height: 50vh; overflow-y: auto;
|
|
}
|
|
.pl-menue.auf .pl-liste { display: flex; }
|
|
|
|
.pl-punkt {
|
|
display: flex; align-items: center; gap: 8px;
|
|
background: none; border: 0; color: var(--text);
|
|
padding: 9px 12px; border-radius: var(--r-control);
|
|
font-size: 14px; text-align: left; cursor: pointer; white-space: nowrap;
|
|
}
|
|
.pl-punkt:hover, .pl-punkt:focus-visible { background: var(--raised); }
|
|
.pl-punkt .icon { width: 16px; height: 16px; flex: 0 0 auto; }
|
|
.pl-punkt .icon svg { width: 16px; height: 16px; }
|
|
/* Ohne Haken bleibt der Platz stehen — sonst springt die Beschriftung. */
|
|
.pl-punkt:not(.an) { padding-left: 36px; }
|
|
|
|
.pl-meldung {
|
|
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
|
|
z-index: 3; max-width: min(520px, calc(100vw - 48px));
|
|
padding: var(--s-regular) var(--s-section);
|
|
background: rgba(20, 20, 23, .96); color: var(--text);
|
|
border: 1px solid rgba(229, 72, 77, .45); border-radius: var(--r-karte);
|
|
text-align: center; line-height: 1.5; font-size: 15px;
|
|
}
|
|
|
|
/* Hinter dem Spieler soll nichts scrollen — aber nur im ganzen Bild. */
|
|
body.pl-offen { overflow: hidden; }
|
|
|
|
/* ---------- Die kleine Fassung ----------
|
|
*
|
|
* **Wie bei Plex und YouTube: der Film rutscht nach unten, statt zu enden.**
|
|
* Wer nebenbei Plakate geraderückt oder etwas sucht, soll ihn nicht verlieren.
|
|
*
|
|
* Es ist dieselbe Hülle und dasselbe Videoelement — nur anders eingerahmt.
|
|
* Ein zweiter Spieler unten hiesse, das Video aus dem Dokument zu nehmen und
|
|
* woanders wieder einzuhängen; dabei hört es auf zu spielen und puffert neu.
|
|
*/
|
|
.pl.pl-klein {
|
|
inset: auto 0 0 0;
|
|
height: 72px;
|
|
/* **`place-items` muss ausdrücklich zurück.** Im ganzen Bild zentriert die
|
|
Hülle ihre Kinder; in der Leiste zog das die Beschriftung in die Mitte,
|
|
weit weg vom Vorschaubild, an dem sie hängt. */
|
|
display: block;
|
|
place-items: normal;
|
|
background: var(--chrome);
|
|
border-top: 1px solid var(--line);
|
|
box-shadow: 0 -8px 28px rgba(0, 0, 0, .45);
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
}
|
|
/* **Unter dem Fortschrittsstrich, nicht darüber.**
|
|
*
|
|
* Das Bild lag bündig am oberen Rand und schob sich über den Strich — auf der
|
|
* rechten Seite, wo noch nichts gefüllt war, verschwand er ganz darunter.
|
|
* Zwei Bildpunkte Abstand und eine Ebene darunter, dann läuft er durch. */
|
|
.pl.pl-klein .pl-video {
|
|
position: absolute; left: 0; top: 2px; z-index: 0;
|
|
width: 128px; height: 70px;
|
|
object-fit: cover; cursor: pointer;
|
|
}
|
|
.pl.pl-klein .pl-kopf,
|
|
.pl.pl-klein .pl-leiste,
|
|
.pl.pl-klein .pl-dreher,
|
|
.pl.pl-klein .pl-meldung { display: none; }
|
|
.pl-kleinleiste { display: none; }
|
|
.pl.pl-klein .pl-kleinleiste {
|
|
display: flex; align-items: center; gap: var(--s-snug);
|
|
width: 100%; height: 100%; padding: 0 var(--s-regular) 0 140px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.pl-kleintext { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
|
|
.pl-kleintitel {
|
|
font-size: 14px; font-weight: 600; color: var(--text);
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
.pl-kleinstand {
|
|
font-size: 12px; color: var(--text-dim); font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* Der Fortschritt als dünner Strich ganz oben auf der Leiste. */
|
|
.pl-kleinbalken {
|
|
position: absolute; left: 0; right: 0; top: 0; height: 2px; z-index: 2;
|
|
background: rgba(255, 255, 255, .14);
|
|
}
|
|
.pl-kleinbalken div { height: 100%; background: var(--accent); transition: width .25s linear; }
|
|
|
|
/* **Platz für die Leiste.** Ohne das läge die letzte Kachelreihe darunter,
|
|
und niemand käme an den letzten Film. */
|
|
body.pl-leiste-da #content { padding-bottom: 88px; }
|
|
body:has(.pl.pl-klein) .hinweise { bottom: 92px; }
|
|
body:has(.pl.pl-klein) .auswahlleiste { bottom: 92px; }
|
|
|
|
@media (max-width: 640px) {
|
|
.pl.pl-klein .pl-video { width: 96px; height: 71px; }
|
|
.pl.pl-klein .pl-kleinleiste { padding-left: 106px; }
|
|
.pl-kleinstand { display: none; }
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.pl-kleinbalken div { transition: none; }
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.pl-laut { display: none; }
|
|
.pl-rest { display: none; }
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.pl-kopf, .pl-leiste { transition: none; }
|
|
}
|
|
|
|
/* ---------- 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; }
|
|
}
|
|
|
|
/* --- Ordnerauswahl ------------------------------------------------------
|
|
*
|
|
* Klappt im Dialog auf, statt einen zweiten darüberzulegen. Die Höhe ist
|
|
* begrenzt und scrollt: ein Verzeichnis mit zweihundert Unterordnern würde
|
|
* den Dialog sonst über den Bildschirmrand schieben. */
|
|
|
|
.ordnerfeld { display: flex; flex-direction: column; gap: var(--s-snug); }
|
|
|
|
.ordnerzeile { display: flex; gap: var(--s-snug); }
|
|
.ordnerzeile .feld { flex: 1; min-width: 0; }
|
|
.ordnerzeile .btn { flex: none; gap: 6px; }
|
|
|
|
.ordnerbaum {
|
|
border: 1px solid var(--line); border-radius: var(--r-control);
|
|
background: var(--canvas); overflow: hidden;
|
|
}
|
|
|
|
.ordnerkopf {
|
|
display: flex; align-items: center; gap: var(--s-snug);
|
|
padding: var(--s-snug); border-bottom: 1px solid var(--line);
|
|
}
|
|
.ordnerkopf .rund { width: 30px; height: 30px; flex: none; }
|
|
.ordnerkopf .rund svg { width: 16px; height: 16px; }
|
|
.ordnerkopf .rund:disabled { opacity: .35; }
|
|
|
|
/* Der Pfad scrollt waagerecht statt umzubrechen — ein tiefer Pfad soll die
|
|
Kopfzeile nicht dreizeilig machen. */
|
|
.ordnerpfad {
|
|
display: flex; align-items: center; gap: 2px;
|
|
overflow-x: auto; scrollbar-width: none; min-width: 0;
|
|
}
|
|
.ordnerpfad::-webkit-scrollbar { display: none; }
|
|
.ordnerpfad .pfadstueck {
|
|
background: none; border: 0; padding: 2px 5px; border-radius: 4px;
|
|
color: var(--text-dim); font-size: 12px; white-space: nowrap;
|
|
}
|
|
.ordnerpfad .pfadstueck:hover { background: var(--elevated); color: var(--text); }
|
|
.ordnerpfad .pfadstueck:not(:last-child)::after { content: '/'; margin-left: 5px; color: var(--text-faint); }
|
|
.ordnerpfad .pfadstueck.wurzel::after { content: none; }
|
|
|
|
.ordnerliste {
|
|
max-height: 220px; overflow-y: auto;
|
|
display: flex; flex-direction: column; padding: var(--s-tight);
|
|
}
|
|
.ordnerliste .winzig { padding: var(--s-regular); }
|
|
|
|
.ordnereintrag {
|
|
display: flex; align-items: center; gap: var(--s-snug);
|
|
background: none; border: 0; border-radius: 5px;
|
|
padding: 7px var(--s-snug); text-align: left;
|
|
color: var(--text-dim); font-size: 13px;
|
|
}
|
|
.ordnereintrag svg { width: 17px; height: 17px; color: var(--text-faint); }
|
|
.ordnereintrag:hover { background: var(--elevated); color: var(--text); }
|
|
.ordnereintrag:hover svg { color: var(--accent); }
|
|
|
|
.ordnerfuss {
|
|
padding: var(--s-snug); border-top: 1px solid var(--line);
|
|
display: flex; justify-content: flex-end;
|
|
}
|
|
/* Der Knopf trägt den ganzen Pfad — bei einem langen muss er kürzen dürfen,
|
|
sonst wächst der Dialog mit. */
|
|
.ordnerfuss .btn {
|
|
max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
display: block;
|
|
}
|
|
|
|
/* Protokollanzeige — Festbreitenschrift, begrenzte Höhe, eigener Bildlauf. */
|
|
.protokoll {
|
|
margin: var(--s-regular) 0 0;
|
|
padding: var(--s-regular);
|
|
background: var(--canvas);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--r-control);
|
|
font: 12px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
color: var(--text-dim);
|
|
max-height: 420px;
|
|
overflow: auto;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
}
|
|
|
|
/* Der Originaltitel unter dem übersetzten — leiser, aber lesbar. */
|
|
.originaltitel {
|
|
margin-top: 2px;
|
|
font-size: 15px;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
|
|
/* --- Fokus und Bewegung --------------------------------------------------
|
|
*
|
|
* **Ein sichtbarer Fokusring auf allem, was man bedienen kann.** Vorher
|
|
* färbte sich allenfalls ein Feldrahmen; mit der Tastatur war die Oberfläche
|
|
* praktisch unbedienbar, weil man nicht sah, wo man gerade ist. */
|
|
|
|
:where(button, a, input, select, textarea, [tabindex]):focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 2px;
|
|
border-radius: var(--r-knopf);
|
|
}
|
|
|
|
.kachel:focus-visible { outline: none; }
|
|
.kachel:focus-visible .plakat {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 3px;
|
|
}
|
|
|
|
/* **Wer „Bewegung reduzieren" eingestellt hat, meint es.**
|
|
*
|
|
* Das gilt im ganzen Projekt bisher nirgends. Animationen ganz abzuschalten
|
|
* wäre falsch — ein Zustandswechsel ohne jede Rückmeldung wirkt wie ein
|
|
* Aussetzer. Auf einen Hauch zu kürzen behält die Rückmeldung und nimmt die
|
|
* Bewegung. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after {
|
|
animation-duration: .01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: .01ms !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
|
|
.kachel:hover .plakat,
|
|
.kachel:focus-visible .plakat { transform: none; }
|
|
}
|
|
|
|
/* --- Hero ----------------------------------------------------------------
|
|
*
|
|
* Die Startseite begann bei einer Reihe aus 150 px breiten Plakaten; das
|
|
* grösste Element über dem Falz war 225 px hoch. Ein Bild über die volle
|
|
* Breite ist der grösste Einzelunterschied im Eindruck — und ein Layout-,
|
|
* kein Farbproblem. */
|
|
|
|
.hero {
|
|
position: relative;
|
|
height: min(62vh, 560px);
|
|
/* Randlos: der Inhaltsbereich hat seitlich Polster, das Bild soll es
|
|
nicht haben. Negative Ränder statt eines eigenen Containers. */
|
|
margin: calc(var(--s-page) * -1) calc(var(--s-page) * -1) var(--s-section);
|
|
overflow: hidden;
|
|
display: flex;
|
|
align-items: flex-end;
|
|
}
|
|
|
|
.hero-buehne { position: absolute; inset: 0; }
|
|
|
|
.hero-folie {
|
|
position: absolute; inset: 0;
|
|
background-size: cover;
|
|
background-position: center 20%;
|
|
opacity: 0;
|
|
transition: opacity .9s ease-in-out;
|
|
}
|
|
.hero-folie.an { opacity: 1; }
|
|
|
|
/* **Zwei Verläufe, nicht einer.** Der von unten lässt die erste Reihe sich
|
|
darüberschieben; der von links sorgt dafür, dass der Text trägt, auch wenn
|
|
das Bild dort hell ist. */
|
|
.hero-buehne::after {
|
|
content: '';
|
|
position: absolute; inset: 0;
|
|
background:
|
|
linear-gradient(to top, var(--canvas) 2%, rgba(11, 11, 13, .55) 45%, transparent 85%),
|
|
linear-gradient(to right, rgba(11, 11, 13, .88) 0%, rgba(11, 11, 13, .35) 45%, transparent 75%);
|
|
}
|
|
|
|
.hero-inhalt {
|
|
position: relative;
|
|
padding: var(--s-page);
|
|
max-width: 620px;
|
|
display: flex; flex-direction: column; gap: var(--s-regular);
|
|
}
|
|
|
|
.hero-schriftzug {
|
|
max-width: 380px; max-height: 120px;
|
|
object-fit: contain; object-position: left bottom;
|
|
}
|
|
|
|
.hero-titel {
|
|
margin: 0;
|
|
font-size: var(--t-hero); font-weight: 700; letter-spacing: -.03em;
|
|
}
|
|
|
|
.hero-meta { font-size: var(--t-normal); color: var(--text-dim); }
|
|
|
|
/* Drei Zeilen, dann Schluss — eine volle Inhaltsangabe gehört auf die
|
|
Detailseite, nicht in den Aufmacher. */
|
|
.hero-text {
|
|
margin: 0;
|
|
color: var(--text-dim);
|
|
display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.hero-knoepfe { display: flex; gap: var(--s-snug); margin-top: var(--s-tight); }
|
|
.hero-knoepfe .btn { height: 40px; padding: 0 var(--s-section); gap: 6px; }
|
|
|
|
.hero-punkte {
|
|
position: absolute; right: var(--s-page); bottom: var(--s-page);
|
|
display: flex; gap: 6px;
|
|
}
|
|
.hero-punkt {
|
|
width: 26px; height: 3px; padding: 0; border: 0; border-radius: 2px;
|
|
background: rgba(255, 255, 255, .28);
|
|
transition: background .2s ease;
|
|
}
|
|
.hero-punkt.an { background: var(--text); }
|
|
.hero-punkt:hover { background: rgba(255, 255, 255, .6); }
|
|
|
|
@media (max-width: 860px) {
|
|
.hero {
|
|
height: min(52vh, 420px);
|
|
margin: calc(var(--s-loose) * -1) calc(var(--s-loose) * -1) var(--s-section);
|
|
}
|
|
.hero-inhalt { padding: var(--s-loose); max-width: none; }
|
|
.hero-punkte { right: var(--s-loose); bottom: var(--s-loose); }
|
|
}
|
|
|
|
/* --- Kontextmenü ---------------------------------------------------------
|
|
*
|
|
* Gesehen, Favorit und Weglegen gab es nur auf der Detailseite. In einer
|
|
* Übersicht mit zwanzig Titeln hiess „die hab ich alle gesehen" vierzig
|
|
* Seitenwechsel. */
|
|
|
|
.kontextmenue {
|
|
position: fixed; z-index: 100;
|
|
min-width: 220px; padding: var(--s-tight);
|
|
background: var(--elevated);
|
|
border: 1px solid var(--line); border-radius: var(--r-flaeche);
|
|
box-shadow: var(--schatten-dialog);
|
|
display: flex; flex-direction: column;
|
|
}
|
|
|
|
.kontextpunkt {
|
|
display: flex; align-items: center; gap: var(--s-snug);
|
|
background: none; border: 0; border-radius: var(--r-knopf);
|
|
padding: 8px var(--s-regular); text-align: left;
|
|
color: var(--text-dim); font-size: var(--t-normal);
|
|
}
|
|
.kontextpunkt:hover { background: var(--raised); color: var(--text); }
|
|
.kontextpunkt svg { width: 17px; height: 17px; }
|
|
.kontextpunkt.gefahr { color: var(--destructive); }
|
|
|
|
/* --- Bildauswahl ---------------------------------------------------------- */
|
|
|
|
.dlg-breit { width: min(860px, 100vw - 48px); }
|
|
|
|
/* ---------- Ein Vorschlag im Zuordnungsdialog ----------
|
|
*
|
|
* Mit Plakat: „Dark" von 2017 und „Dark Shadows" von 1966 sind an einer
|
|
* Textzeile kaum zu unterscheiden, am Bild sofort.
|
|
*/
|
|
.vorschlag {
|
|
display: flex; gap: var(--s-regular); align-items: flex-start; width: 100%;
|
|
background: var(--raised); border: 1px solid transparent;
|
|
border-radius: var(--r-karte); padding: var(--s-regular); text-align: left;
|
|
}
|
|
.vorschlag:hover { background: var(--elevated); border-color: var(--line); }
|
|
.vorschlag:disabled { opacity: .4; }
|
|
/* Die laufende Zeile bleibt hell, während die anderen zurücktreten — sonst
|
|
* sähe genau die, auf die man geklickt hat, am totesten aus. */
|
|
.vorschlag.laeuft { opacity: 1; border-color: var(--accent); background: var(--elevated); }
|
|
|
|
.vorschlag-bild {
|
|
width: 54px; aspect-ratio: 2 / 3; flex: none;
|
|
border-radius: var(--r-knopf); overflow: hidden; background: var(--placeholder);
|
|
}
|
|
.vorschlag-bild img { width: 100%; height: 100%; object-fit: cover; opacity: 0; }
|
|
.vorschlag-bild.geladen img { opacity: 1; transition: opacity .18s ease; }
|
|
|
|
.vorschlag-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
|
|
.vorschlag-text .hinweis { margin: 0; }
|
|
|
|
.vorschlag-lauf {
|
|
display: flex; align-items: center; gap: var(--s-snug); flex: none;
|
|
color: var(--accent); font-size: var(--t-klein); font-weight: 600;
|
|
align-self: center;
|
|
}
|
|
|
|
/* Die Herkunft im Zuordnungsdialog — der Pfad bricht um statt abzuschneiden,
|
|
* weil ausgerechnet das Ende die Antwort trägt. */
|
|
.quelle {
|
|
display: flex; flex-direction: column; gap: 2px;
|
|
padding: var(--s-snug) var(--s-regular);
|
|
background: var(--raised); border-radius: var(--r-knopf);
|
|
border-left: 2px solid var(--accent);
|
|
}
|
|
.quelle-pfad {
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
font-size: var(--t-klein); color: var(--text); word-break: break-all;
|
|
}
|
|
|
|
/* Ein aufklappbarer Block je Grund — zugeklappt sieht man die Verteilung,
|
|
* aufgeklappt die Pfade. Dreitausend Zeilen am Stück liest niemand. */
|
|
.grundblock { margin-top: var(--s-snug); }
|
|
.grundblock summary {
|
|
cursor: pointer; padding: 6px 0; color: var(--text-dim); font-size: var(--t-normal);
|
|
}
|
|
.grundblock summary:hover { color: var(--text); }
|
|
.grundblock summary strong { color: var(--accent); margin-right: 6px; }
|
|
.grundblock .protokoll { max-height: 220px; margin-top: var(--s-snug); }
|
|
|
|
/* Der Dateiname über einer Aktionsreihe — nur wenn es mehrere gibt. */
|
|
.dateiname.fassung {
|
|
font-family: inherit; color: var(--text-dim); font-weight: 600;
|
|
}
|
|
.dateiname {
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
color: var(--text-faint); margin-top: var(--s-snug);
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
|
|
/* Der Fühler, der das Nachladen auslöst — unsichtbar, aber nicht null hoch:
|
|
* ein Element ohne Höhe kreuzt die Sichtgrenze nie zuverlässig. */
|
|
.nachlader { height: 1px; }
|
|
|
|
/* Der dritte Knopf im Dialogfuss steht links aussen — er gehört weder zu
|
|
* Abbrechen noch zu Bestätigen. */
|
|
.dlg-weiterer { margin-right: auto; }
|
|
|
|
/* Das Band an einer Kachel, die noch nicht freigegeben ist. Unten links,
|
|
* damit es weder dem Haken oben rechts noch dem Fortschrittsbalken am
|
|
* unteren Rand in die Quere kommt. */
|
|
.wartet {
|
|
position: absolute; left: 0; bottom: 0;
|
|
padding: 3px 8px; border-top-right-radius: var(--r-knopf);
|
|
background: color-mix(in srgb, var(--accent) 88%, black);
|
|
color: #fff; font-size: 11px; font-weight: 700; letter-spacing: .02em;
|
|
}
|
|
|
|
.bildblock { display: flex; flex-direction: column; gap: var(--s-snug); }
|
|
.bildblock h3 { margin: 0; font-size: var(--t-gross); font-weight: 600; flex: 1; }
|
|
|
|
/* ---------- Bildwahl: ein Reiter je Typ ----------
|
|
*
|
|
* Vorher standen Plakat, Hintergrund und Schriftzug untereinander: der
|
|
* Dialogkörper scrollte, und jedes Raster darin scrollte noch einmal. Wo das
|
|
* Rad landet, hing davon ab, über welcher Fläche der Zeiger gerade stand.
|
|
* Jetzt ist ein Typ sichtbar, und genau eine Fläche scrollt.
|
|
*/
|
|
.bildreiter {
|
|
display: flex; gap: var(--s-tight);
|
|
border-bottom: 1px solid var(--line);
|
|
margin: calc(var(--s-loose) * -1) 0 0;
|
|
position: sticky; top: 0; z-index: 2; background: var(--surface);
|
|
}
|
|
.bildreiter-knopf {
|
|
display: inline-flex; align-items: baseline; gap: 6px;
|
|
background: none; border: 0; border-bottom: 2px solid transparent;
|
|
padding: 10px var(--s-regular); margin-bottom: -1px;
|
|
color: var(--text-muted); font-size: var(--t-normal); font-weight: 600;
|
|
}
|
|
.bildreiter-knopf:hover { color: var(--text); }
|
|
.bildreiter-knopf.an { color: var(--text); border-bottom-color: var(--accent); }
|
|
.bildreiter-knopf .winzig { color: var(--text-faint); }
|
|
|
|
/* **Die eine scrollende Fläche.** Der Dialogkörper selbst wächst nicht mehr
|
|
* mit, sonst wären es wieder zwei. */
|
|
.bildbuehne { display: flex; flex-direction: column; min-height: 220px; }
|
|
|
|
.bildgitter {
|
|
display: grid; gap: var(--s-snug);
|
|
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
|
|
padding: 2px;
|
|
}
|
|
|
|
.bildwahl {
|
|
position: relative; padding: 0; border: 2px solid transparent;
|
|
border-radius: var(--r-karte); overflow: hidden;
|
|
background: var(--placeholder);
|
|
aspect-ratio: var(--verhaeltnis, 2 / 3);
|
|
}
|
|
.bildwahl img { width: 100%; height: 100%; object-fit: contain; display: block; }
|
|
.bildwahl:hover { border-color: var(--accent); }
|
|
/* Das gesetzte Bild ist markiert und nicht anklickbar — es ist schon gewählt. */
|
|
.bildwahl.an { border-color: var(--accent); opacity: 1; }
|
|
.bildwahl:disabled { cursor: default; }
|
|
|
|
.bildwahl-marke {
|
|
position: absolute; right: 4px; top: 4px;
|
|
display: grid; place-items: center;
|
|
width: 22px; height: 22px; border-radius: 50%;
|
|
background: var(--accent); color: #fff;
|
|
}
|
|
.bildwahl-marke svg { width: 14px; height: 14px; }
|
|
|
|
/* Prüfliste — mit Miniplakat, damit erkennbar ist, worum es geht. */
|
|
.pruefzeile .pruef-bild {
|
|
width: 40px; height: 60px; flex: none;
|
|
border-radius: 4px; overflow: hidden;
|
|
background: var(--placeholder);
|
|
}
|
|
.pruefzeile .pruef-bild img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
.pruefzeile .knopfreihe { align-items: center; }
|
|
|
|
/* Der Zeitpunktwähler — der Browser bringt Kalender und Uhr mit, aber nicht
|
|
die Farben. Ohne das eigene Farbschema wäre das Symbol unsichtbar dunkel. */
|
|
input[type="datetime-local"].feld {
|
|
color-scheme: dark;
|
|
min-height: 38px;
|
|
}
|
|
input[type="datetime-local"].feld::-webkit-calendar-picker-indicator {
|
|
filter: invert(1) opacity(.55);
|
|
cursor: pointer;
|
|
}
|
|
input[type="datetime-local"].feld::-webkit-calendar-picker-indicator:hover {
|
|
filter: invert(1) opacity(.9);
|
|
}
|
|
|
|
/* Die Mehrfachauswahl in der Prüfliste. */
|
|
.pruefzeile input[type="checkbox"] {
|
|
width: 17px; height: 17px; flex: none;
|
|
accent-color: var(--accent);
|
|
}
|
|
.reihe-kopf .knopfreihe .bleiben {
|
|
display: flex; align-items: center; gap: 6px;
|
|
color: var(--text-dim); font-size: var(--t-normal);
|
|
}
|
|
.reihe-kopf .knopfreihe .bleiben input { accent-color: var(--accent); width: 16px; height: 16px; }
|
|
|
|
/* Der sichtbare Griff zum Kontextmenü auf einer Kachel.
|
|
*
|
|
* Rechtsklick findet nur, wer ihn vermutet, und auf dem Telefon gibt es ihn
|
|
* gar nicht. Beim Überfahren und bei Tastaturfokus erscheinen drei Punkte. */
|
|
/* **Unten rechts, nicht oben rechts.**
|
|
*
|
|
* Oben rechts sitzt der Gesehen-Haken, und beide übereinander waren genau das,
|
|
* was auf dem Plakat wie ein Fehler aussah. Jetzt hat jede Ecke eine Aufgabe:
|
|
* oben links die Auswahl, oben rechts der Stand, unten links Abspielen, unten
|
|
* rechts das Menü. */
|
|
.kachel-mehr {
|
|
position: absolute; right: 6px; bottom: 6px; z-index: 4;
|
|
display: grid; place-items: center;
|
|
width: 28px; height: 28px; border-radius: 50%;
|
|
background: rgba(10, 10, 12, .78);
|
|
backdrop-filter: blur(6px);
|
|
color: var(--text);
|
|
opacity: 0; transition: opacity .15s ease;
|
|
cursor: pointer;
|
|
}
|
|
.kachel-mehr svg { width: 16px; height: 16px; }
|
|
.kachel:hover .kachel-mehr,
|
|
.kachel:focus-within .kachel-mehr,
|
|
.kachel-mehr:focus-visible { opacity: 1; }
|
|
/* Auf Geräten ohne Mauszeiger steht er dauerhaft da — sonst käme man nie
|
|
heran. */
|
|
@media (hover: none) {
|
|
.kachel-mehr { opacity: .85; }
|
|
}
|
|
|
|
/* ---------- Kurze Meldungen am unteren Rand ----------
|
|
*
|
|
* **Für alles, was keinen Ort auf der Seite hat.** Eine Aktion aus einem
|
|
* Kontextmenü wirkt, nachdem das Menü schon zu ist — scheitert sie, gäbe es
|
|
* sonst nirgends Platz dafür. Vorher verschwand so ein Fehler spurlos.
|
|
*
|
|
* Über allem ausser den Dialogen (`z-index: 200`): eine Meldung, die hinter
|
|
* einem Dialog aufgeht, hat niemand gesehen. Dialoge liegen in der obersten
|
|
* Ebene des Browsers und bleiben dennoch darüber — deshalb erscheinen
|
|
* Meldungen aus einem Dialog weiterhin in seiner eigenen Fehlerzeile. */
|
|
.hinweise {
|
|
position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
|
|
z-index: 200; display: flex; flex-direction: column; gap: 8px;
|
|
align-items: center; pointer-events: none; max-width: min(520px, calc(100vw - 32px));
|
|
}
|
|
.hinweiszeile {
|
|
background: var(--raised); color: var(--text);
|
|
border: 1px solid var(--line); border-radius: var(--r-control);
|
|
padding: 10px 16px; font-size: 14px; line-height: 1.4;
|
|
box-shadow: 0 8px 24px rgba(0, 0, 0, .45);
|
|
animation: hinweis-auf .18s ease-out;
|
|
}
|
|
.hinweiszeile.fehler {
|
|
background: #3A1B1E; border-color: rgba(229, 72, 77, .45); color: #FFB3B5;
|
|
}
|
|
.hinweiszeile.geht { opacity: 0; transform: translateY(6px); transition: all .2s ease-in; }
|
|
@keyframes hinweis-auf { from { opacity: 0; transform: translateY(8px); } }
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.hinweiszeile { animation: none; }
|
|
.hinweiszeile.geht { transition: none; }
|
|
}
|
|
|
|
/* ---------- Mehrfachauswahl in der Mediathek ----------
|
|
*
|
|
* **Oben links, gegenüber vom Gesehen-Haken.** Jede Ecke des Plakats hat eine
|
|
* Aufgabe: oben links die Auswahl, oben rechts der Stand, unten links
|
|
* Abspielen, unten rechts das Menü. Zwei Dinge in derselben Ecke sahen auf
|
|
* dem Plakat wie ein Fehler aus.
|
|
*
|
|
* **Kein Modus, den man erst einschaltet.** Das Kästchen erscheint beim
|
|
* Überfahren — so wie die drei Punkte. Wer nichts damit zu tun hat, sieht es
|
|
* nie; wer etwas auswählen will, findet es dort, wo er ohnehin hinsieht. */
|
|
.wahlmarke {
|
|
position: absolute; top: 8px; left: 8px; z-index: 5;
|
|
width: 26px; height: 26px; border-radius: 50%;
|
|
display: grid; place-items: center;
|
|
background: rgba(10, 10, 12, .62);
|
|
border: 2px solid rgba(255, 255, 255, .72);
|
|
backdrop-filter: blur(4px);
|
|
cursor: pointer;
|
|
opacity: 0;
|
|
transition: opacity .15s ease, background .15s ease-out, border-color .15s ease-out;
|
|
}
|
|
.wahlmarke svg { width: 15px; height: 15px; color: #fff; opacity: 0; transition: opacity .15s; }
|
|
|
|
/* Beim Überfahren, bei Tastaturfokus — und durchgehend, solange überhaupt
|
|
etwas gewählt ist. Sonst müsste man für jede weitere Kachel erst wieder
|
|
das richtige Eck treffen. */
|
|
.kachel:hover .wahlmarke,
|
|
.kachel:focus-within .wahlmarke,
|
|
.raster.waehlt .wahlmarke,
|
|
.kachel.gewaehlt .wahlmarke { opacity: 1; }
|
|
.wahlmarke:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
|
|
.kachel.gewaehlt .wahlmarke {
|
|
background: var(--accent); border-color: var(--accent);
|
|
}
|
|
.kachel.gewaehlt .wahlmarke svg { opacity: 1; }
|
|
|
|
/* **Solange etwas gewählt ist, führt kein Klick mehr woandershin.** Der
|
|
Abspielknopf und der Griff blieben sonst stehen und täten etwas anderes als
|
|
das, was der Klick daneben tut. */
|
|
.raster.waehlt .schweber,
|
|
.raster.waehlt .kachel-mehr { display: none; }
|
|
.raster.waehlt .kachel { cursor: pointer; }
|
|
.raster.waehlt .kachel:hover .plakat { transform: scale(1.02); }
|
|
|
|
/* Das Gewählte deutlich abheben — Rahmen und leichtes Abdunkeln, damit es
|
|
auch ohne Farbwahrnehmung zu erkennen ist. */
|
|
.kachel.gewaehlt .plakat {
|
|
outline: 3px solid var(--accent); outline-offset: -3px;
|
|
}
|
|
.kachel.gewaehlt .plakat img { filter: brightness(.72); }
|
|
|
|
/* **Die Leiste klebt unten**, über dem Inhalt, damit sie beim Scrollen durch
|
|
vierhundert Kacheln nicht verschwindet. */
|
|
.auswahlleiste {
|
|
position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%);
|
|
z-index: 90; display: flex; align-items: center; gap: var(--s-regular);
|
|
flex-wrap: wrap; justify-content: center;
|
|
max-width: min(760px, calc(100vw - 24px));
|
|
padding: 10px 16px;
|
|
background: var(--elevated); border: 1px solid var(--line);
|
|
border-radius: var(--r-karte);
|
|
box-shadow: 0 10px 32px rgba(0, 0, 0, .55);
|
|
}
|
|
.auswahlzahl { font-size: 14px; font-weight: 600; white-space: nowrap; }
|
|
|
|
/* Das Kreuz steht abgesetzt — es tut nichts mit den Titeln, es räumt auf. */
|
|
.auswahl-zu {
|
|
display: grid; place-items: center; flex: 0 0 auto;
|
|
width: 34px; height: 34px; border-radius: 50%;
|
|
border: 0; background: transparent; color: var(--text-dim);
|
|
cursor: pointer; transition: background .15s, color .15s;
|
|
margin-left: 4px;
|
|
}
|
|
.auswahl-zu:hover, .auswahl-zu:focus-visible { background: var(--raised); color: var(--text); }
|
|
.auswahl-zu svg { width: 17px; height: 17px; }
|
|
|
|
/* **Während des Streichens markiert der Browser nichts.** Sonst zieht er die
|
|
Beschriftungen blau an, als wolle man Text kopieren. */
|
|
body.keine-auswahl { user-select: none; -webkit-user-select: none; }
|
|
.raster.zieht .kachel { cursor: crosshair; }
|
|
/* Kein Ziehgut aus Plakaten — sonst hängt eines am Mauszeiger. */
|
|
.plakat img { -webkit-user-drag: none; user-drag: none; }
|
|
|
|
@media (max-width: 640px) {
|
|
.auswahlleiste { left: 12px; right: 12px; transform: none; max-width: none; }
|
|
.auswahlleiste .knopfreihe { width: 100%; }
|
|
.auswahlleiste .btn { flex: 1 1 auto; justify-content: center; }
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.wahlmarke, .wahlmarke svg { transition: none; }
|
|
}
|
|
|
|
/* **Die Meldung weicht der Auswahlleiste aus.** Beide wollen den unteren
|
|
Rand; die Meldung lag zwar davor, verdeckte aber genau die Knöpfe, die man
|
|
als Nächstes drückt. */
|
|
body:has(.auswahlleiste:not(.hidden)) .hinweise { bottom: 104px; }
|
|
@media (max-width: 640px) {
|
|
body:has(.auswahlleiste:not(.hidden)) .hinweise { bottom: 148px; }
|
|
}
|
|
|
|
/* Die Leiste kommt mit dem ersten Haken — ohne Sprung. */
|
|
.auswahlleiste:not(.hidden) { animation: leiste-auf .18s ease-out; }
|
|
@keyframes leiste-auf { from { opacity: 0; transform: translate(-50%, 8px); } }
|
|
@media (max-width: 640px) {
|
|
@keyframes leiste-auf { from { opacity: 0; transform: translateY(8px); } }
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.auswahlleiste:not(.hidden) { animation: none; }
|
|
}
|
|
|
|
/* ---------- Bearbeiten: alles zu einem Titel an einer Stelle ---------- */
|
|
|
|
.bearb-kopf {
|
|
display: flex; gap: var(--s-regular); align-items: center;
|
|
padding-bottom: var(--s-regular); border-bottom: 1px solid var(--line);
|
|
margin-bottom: var(--s-section);
|
|
}
|
|
.bearb-plakat {
|
|
width: 72px; aspect-ratio: 2 / 3; flex: 0 0 auto;
|
|
border-radius: var(--r-control); overflow: hidden; background: var(--placeholder);
|
|
}
|
|
.bearb-plakat img { width: 100%; height: 100%; object-fit: cover; }
|
|
.bearb-kopftext { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
.bearb-kopftext strong { font-size: 17px; }
|
|
|
|
.bearb-abschnitt { margin-bottom: var(--s-section); }
|
|
.bearb-abschnitt:last-child { margin-bottom: 0; }
|
|
.bearb-abschnitt h3 {
|
|
font-size: 14px; font-weight: 600; margin: 0 0 4px;
|
|
text-transform: uppercase; letter-spacing: .04em; color: var(--text-dim);
|
|
}
|
|
.bearb-abschnitt .hinweis { margin: 0 0 var(--s-snug); }
|
|
/* Das Eingabefeld nimmt den Platz, die Knöpfe nur, was sie brauchen. */
|
|
.bearb-abschnitt .knopfreihe .feld { flex: 1 1 220px; min-width: 0; }
|
|
|
|
/* **Sagt, dass hier jemand von Hand eingegriffen hat.** Ohne das sieht ein
|
|
gesetzter Wert aus wie ein geholter, und niemand weiss, warum das
|
|
Auffrischen ihn in Ruhe lässt. */
|
|
.bearb-merker {
|
|
display: inline-flex; align-items: center; gap: 5px;
|
|
margin-top: var(--s-snug);
|
|
font-size: 12px; color: var(--accent);
|
|
}
|
|
.bearb-merker svg { width: 14px; height: 14px; }
|
|
|
|
@media (max-width: 560px) {
|
|
.bearb-abschnitt .knopfreihe .feld { flex: 1 1 100%; }
|
|
}
|
|
|
|
/* Die Bühne hinter den Reitern im Bearbeiten-Dialog. */
|
|
.bearb-buehne { margin-top: var(--s-regular); }
|
|
/* Die Reiterleiste sitzt hier enger als über einer ganzen Seite. */
|
|
.dlg .reiter { margin: 0; border-bottom: 1px solid var(--line); }
|
|
|
|
/* ---------- Ohne Zuordnung: eine Liste zum Durchgehen ---------- */
|
|
|
|
.ohnezuordnung {
|
|
display: flex; flex-direction: column; gap: 2px;
|
|
margin-top: var(--s-snug);
|
|
max-height: 320px; overflow-y: auto;
|
|
}
|
|
.ohnezuordnung-zeile {
|
|
display: flex; align-items: baseline; gap: var(--s-regular);
|
|
width: 100%; text-align: left; cursor: pointer;
|
|
background: none; border: 0; color: var(--text);
|
|
padding: 7px var(--s-snug); border-radius: var(--r-control);
|
|
}
|
|
.ohnezuordnung-zeile:hover,
|
|
.ohnezuordnung-zeile:focus-visible { background: var(--raised); }
|
|
.oz-titel { font-size: 14px; flex: 0 0 auto; max-width: 45%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
/* Der Dateiname steht daneben — daran erkennt man, was gemeint ist. */
|
|
.oz-dazu {
|
|
font-size: 12px; color: var(--text-muted); flex: 1 1 auto;
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
|
|
/* Jede Datei eines mehrfach belegten Titels einzeln, mit einem Weg heraus. */
|
|
.fassungstitel { padding: 4px 0; }
|
|
.fassungsdatei {
|
|
display: flex; align-items: center; gap: var(--s-snug);
|
|
padding: 3px var(--s-snug) 3px 24px;
|
|
}
|
|
.fassungsdatei .oz-dazu { flex: 1 1 auto; }
|
|
.btn.klein { padding: 4px 10px; font-size: 12px; flex: 0 0 auto; }
|
|
|
|
/* Kurz hervorheben, wohin gesprungen wurde — sonst sucht man oben weiter. */
|
|
.hervor { animation: hervor 1.6s ease-out; }
|
|
@keyframes hervor {
|
|
0%, 60% { background: var(--accent-soft); border-radius: var(--r-karte); }
|
|
100% { background: transparent; }
|
|
}
|
|
@media (prefers-reduced-motion: reduce) { .hervor { animation: none; } }
|