Filipes Kurznotiz vom 28.09.2026, von links nach rechts:
Kachel sichtbar -> geschlossen -> Vorbereitung -> Wartebereich/
Chat -> Countdown -> LIVE -> Reaction + Gaeste + Chat + PayPal
-> Ende
DREI STAENDE, NICHT SIEBEN
„Wartebereich" und „Countdown" sind keine eigenen Zustaende, sondern
das, was „Vorbereitung" auf dem Bildschirm TUT. Drei Staende, die
sich gegenseitig ausschliessen, sind pruefbar; sieben, von denen sich
vier ueberlappen, sind es nicht.
DAS VIDEO LAEUFT NICHT UEBER DIESEN SERVER
Naheliegend waere: Der Host spielt ab, alle sehen seinen Bildschirm.
Das waere aus zwei Gruenden falsch. Rechtlich ist ein
weitergesendetes YouTube-Video eine oeffentliche Wiedergabe -- genau
die Sache, fuer die Kanaele gesperrt werden. Und technisch kostet es
Bandbreite und Qualitaet.
Jeder Zuschauer laedt das Video deshalb SELBST. Uebertragen wird nur
der Spielstand: Kennung, laeuft/pausiert, Sekunde. Das sind ein paar
Byte, jeder sieht es in voller Qualitaet, und alle sind auf derselben
Sekunde. Nachgefuehrt wird erst ab anderthalb Sekunden Abweichung --
ein Player, dem man jede Sekunde eine neue Position gibt, ruckelt
sichtbar.
DIE KAMERAS LAUFEN DIREKT VON MENSCH ZU MENSCH
Ueber denselben Weg wie die Anrufe im Haus (seit 18.09.), nur mit
mehr Empfaengern. Das hat eine Grenze, und sie ist gerechnet, nicht
geraten: Bei 360p und rund 350 kbit/s sind zwoelf Zuschauer etwa
4 Mbit/s Upload beim Host. Darueber schaltet die Sendung von selbst
auf Ton um -- wer keine Kamera mehr bekommt, hoert alles, sieht das
Video und kann schreiben. Ehrlicher als eine Verbindung, die stockt,
und sichtbar im Regiepult.
Heute sind es elf Menschen im ganzen Haus (gemessen: 1 admin, 1 hand,
1 linke, 4 modi, 4 gast). Die Grenze ist weit weg -- sie steht
trotzdem drin, weil sie sonst erst auffaellt, wenn es zu spaet ist.
DIE SEITE IST ANDERS GEBAUT ALS JEDE ANDERE IM HAUS
Ueberall sonst: Kacheln, Karten, Listen -- man liest, entscheidet,
geht wieder. Hier sitzt man. Eine Stunde, mit anderen, auf EINE
Sache schauend. Deshalb kein Raster, sondern ein SAAL: grosse Flaeche
fuer das Video, Kamerabilder als schwebende Fenster darueber, der
Chat als Schiene daneben. Die Seite scrollt nicht -- ein Video, das
beim Tippen im Chat nach oben rutscht, ist der schnellste Weg, dass
jemand aufhoert zu schreiben.
Fuer den Host ein REGIEPULT: vier senkrechte Regler nebeneinander wie
an einem Mischpult, darueber die Sendung, daneben Gaeste und
Anordnung, unten drei grosse Knoepfe. Es SCHIEBT den Saal, es deckt
ihn nicht zu.
Die Kachel traegt ihren Zustand als Farbe: grau geschlossen,
bernstein in Vorbereitung, rot auf Sendung. Keine Ton-Nummer -- der
Farbraum ist bei 46 voll, und sie braucht auch keine.
PAYPAL: EINE QUELLE
Der Knopf nimmt den Weg, der auf der Unterstuetzen-Seite hinterlegt
ist -- derselbe Eintrag, dieselbe Pflege. Ist dort nichts eingetragen
oder steht er auf unsichtbar, erscheint hier kein Knopf. Eine
geratene Adresse ist an dieser Stelle die gefaehrlichste aller
Abkuerzungen.
=======================================================================
ACHT FEHLER, DIE OHNE MESSUNG LIVE GEGANGEN WAEREN
=======================================================================
1. `data-live` WAR SCHON VERGEBEN. Die Draussen-Kachel bekommt es,
sobald Filipe auf Twitch sendet. Meine Regel haette ihr waehrend
jedes Streams die Farbe genommen -- genau dann, wenn sie wichtig
ist. Heisst jetzt `data-sendung`, und pruef-reaktion haelt beides
auseinander.
2. DIE INHALTSRICHTLINIE HAETTE YOUTUBE LAUTLOS GESPERRT. Die Datei
warnt an genau dieser Stelle selbst davor: Am 27.08.2026 hat
`frame-src 'none'` den Musik-Knopf stillgelegt -- der Knopf
reagierte, das Feld ging auf, und wo die Player sein sollten,
blieb es leer. Hier waere das Ergebnis eine schwarze Leinwand vor
Publikum gewesen. youtube-nocookie.com fuer den Rahmen (setzt keine
Werbekennungen), www.youtube.com fuer die Einbett-API,
i.ytimg.com fuer die Vorschaubilder.
3. KAMERA UND MIKROFON WAREN GESPERRT. Dieselbe Falle, vor der
index.js selbst warnt -- und die am 18.09. schon einmal zugeschlagen
hat. Die Ausnahme ist jetzt eine benannte MENGE statt eines zweiten
Sonderfalls, und pruef-kamera-richtlinie.mjs haelt sie GEGEN DEN
QUELLTEXT: Welche Seite laedt ein Skript, das getUserMedia
aufruft? Genau die muss drinstehen -- und keine andere. Eine
Liste, die abgeleitet wird, kann nicht veralten.
4. ZWEI ANRUFE AN DIESELBE PERSON. Zwischen `await kameraHolen()` und
dem Anlegen der Verbindung laeuft alles andere weiter; jeder Takt
sagte wieder „den kenne ich noch nicht". Der Empfaenger antwortete
auf beide Angebote, und die zweite Antwort traf eine Verbindung,
die laengst stand.
5. DAS ANGEBOT GING HINAUS, BEVOR DER EMPFAENGER ZUHOEREN KONNTE.
Gemessen:
[spur] an [3] reaktion_signal | offen: [2,1]
...
[spur] Strom auf fuer 3 Lenny
Die Anmeldung ist ein gewoehnlicher Abruf und sofort durch, der
Ereignisstrom eine stehende Verbindung. Der Host erfaehrt vom
Neuankoemmling also zuverlaessig, BEVOR der zuhoeren kann.
Die Richtung ist jetzt umgedreht: Wer bereit ist, BITTET um den
Anruf -- er ist der Einzige, der das sicher weiss. Dazu ein
eigener, schneller Takt (2,5 s) und eine Ruecknahme, wenn ein
Angebot bei niemandem ankommt.
6. EIN VIDEO MIT TON STARTET NICHT VON ALLEIN. `videoWidth` war 640,
das Bild kam also an -- und das Fenster blieb schwarz. Kein
Fehler, keine Meldung, es passiert einfach nichts. Die Kameras
starten jetzt stumm (stumm darf losgehen), ein Knopf schaltet den
Ton frei, und die erste Beruehrung der Seite tut es ohnehin.
7. DIE LADE AM HANDY GING NICHT AUF. Gemessen: ein 390x775 grosser
Saal mit 219 px Video und 556 px Leere darunter. Statt den Knopf
zu reparieren, ist die Lade weg -- unter Kopfleiste und Video
bleiben auf einem Telefon rund 550 px, das ist mehr Chat, als eine
Lade je zeigen wuerde. Ein Zustand weniger ist besser als ein
Zustand, der funktioniert.
8. `sendBeacon` KANN NUR POST. Beim Schliessen des Fensters wird ein
gewoehnlicher Abruf abgebrochen; mein DELETE waere nie angekommen,
und jeder haette zwei Minuten lang als anwesend gegolten.
Dazu drei Funde der Hauspruefungen, alle von mir verursacht:
17 Schriftgroessen unter der Lesbarkeitsgrenze von 11,5 px, elf
Maschinenworte ohne deutschen Satz, und ein Aufbewahrungseintrag ohne
Rechtsgrundlage.
=======================================================================
GEMESSEN
pruef-reaktion 74 Punkte, 0 Fehler (11 Abschnitte)
pruef-kamera-richtlinie 10 Punkte, 0 Fehler (neu, abgeleitet)
mess-reaktion beide Kameras kommen an, 640 px, laufen --
beim Zuschauer UND beim Host. Diese Messung
hat einen Rueckgabewert: Alles andere kann
gruen sein, und trotzdem sitzt jeder vor
einem schwarzen Rechteck.
pruef-handy 180 (vorher 177), pruef-notizen 79,
pruef-aufbewahrung 45, pruef-meldungen 8, pruef-css-klassen 33,
pruef-struktur 35, pruef-crew-adresse 161,
pruef-haus-trennung 100, pruef-start-ansicht 160 -- alle 0 Fehler.
796 lines
26 KiB
CSS
796 lines
26 KiB
CSS
/* =====================================================================
|
|
DIE REACTION (28.09.2026)
|
|
|
|
Diese Seite ist absichtlich anders gebaut als jede andere im Haus.
|
|
Überall sonst gilt: Kacheln, Karten, Listen -- man liest etwas,
|
|
entscheidet und geht wieder. Hier sitzt man. Eine Stunde lang, mit
|
|
anderen zusammen, und schaut auf EINE Sache.
|
|
|
|
Daraus folgen drei Entscheidungen, die sonst nirgends gelten:
|
|
|
|
1. DIE SEITE SCROLLT NICHT. Sie füllt genau den Bildschirm. Ein
|
|
Video, das beim Tippen im Chat nach oben rutscht, ist der
|
|
schnellste Weg, dass jemand aufhört zu schreiben.
|
|
|
|
2. DIE FLÄCHE IST DUNKLER ALS ÜBERALL SONST. Der Rest des Hauses
|
|
steht auf #07060f mit Licht in den Ecken; hier ist es fast
|
|
schwarz. Nicht als Stil, sondern weil daneben ein Video läuft:
|
|
Alles, was selbst leuchtet, zieht den Blick vom Bild weg.
|
|
|
|
3. DIE FARBE IST ROT -- und zwar nur hier und nur beim Senden.
|
|
Das Haus ist augenschonend gebaut, und Rot ist das Gegenteil
|
|
davon. Es ist aber kein Dauerzustand: ein bis zwei Stunden im
|
|
Monat, und in dieser Zeit SOLL es rufen. Gedeckt (#d8404a statt
|
|
reinem Rot), und was pulst, pulst in vier Sekunden -- das ist
|
|
Atmen und kein Blinken.
|
|
|
|
---------------------------------------------------------------------
|
|
DREI HÄUTE, EINE SEITE
|
|
|
|
`data-stand` am `.saal` schaltet um: zu / vorbereitung / live.
|
|
Alle drei stehen im Dokument. Beim Wechsel von „gleich" auf „live"
|
|
wird deshalb nichts neu geladen -- weder der Chat, in dem jemand
|
|
mitten im Satz ist, noch die Verbindung zur Kamera.
|
|
===================================================================== */
|
|
|
|
.reaktion-seite {
|
|
--saal: #050408;
|
|
--saal-hoch: #0b0a12;
|
|
--rand: rgba(150, 160, 190, .14);
|
|
--rand-hell: rgba(170, 180, 210, .26);
|
|
--schrift: #e9e6f2;
|
|
--schrift-leise: #a9a3bd;
|
|
--an: #d8404a;
|
|
--an-hell: #ff8b92;
|
|
--gleich: #e0a13a;
|
|
--zu: #8b93a4;
|
|
|
|
background: var(--saal);
|
|
/* Kein Scrollen: Der Saal ist so hoch wie das Fenster. `dvh` und
|
|
nicht `vh` -- auf dem Handy wandert die Adressleiste, und `vh`
|
|
rechnet mit dem Zustand von vorhin. */
|
|
min-height: 100dvh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.saal {
|
|
position: relative;
|
|
display: grid;
|
|
/* Die Kopfleiste ist 69 px hoch (gemessen, nicht geschätzt -- siehe
|
|
pruef-handy, das sie über alle Breiten abmisst). */
|
|
height: calc(100dvh - 69px);
|
|
padding: 0;
|
|
max-width: none;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------
|
|
DAS SCHILD
|
|
|
|
Oben mittig, in allen drei Zuständen. Es beantwortet die einzige
|
|
Frage, die jeder beim Öffnen hat: läuft schon was?
|
|
--------------------------------------------------------------------- */
|
|
.buehne-schild {
|
|
position: absolute;
|
|
top: 14px; left: 50%;
|
|
transform: translateX(-50%);
|
|
z-index: 40;
|
|
display: flex; align-items: center; gap: 9px;
|
|
padding: 7px 15px 8px;
|
|
border-radius: 999px;
|
|
background: rgba(8, 7, 14, .82);
|
|
border: 1px solid var(--rand);
|
|
backdrop-filter: blur(14px) saturate(1.2);
|
|
-webkit-backdrop-filter: blur(14px) saturate(1.2);
|
|
box-shadow: 0 16px 38px -18px #000;
|
|
font-size: .72rem; font-weight: 800;
|
|
letter-spacing: .14em; text-transform: uppercase;
|
|
color: var(--schrift-leise);
|
|
}
|
|
.buehne-schild__punkt {
|
|
width: 8px; height: 8px; border-radius: 50%;
|
|
background: var(--zu);
|
|
flex: none;
|
|
}
|
|
.buehne-schild__zahl {
|
|
padding-left: 9px;
|
|
border-left: 1px solid var(--rand);
|
|
color: var(--schrift);
|
|
letter-spacing: .06em;
|
|
}
|
|
|
|
.saal[data-stand="vorbereitung"] .buehne-schild {
|
|
border-color: color-mix(in srgb, var(--gleich) 40%, transparent);
|
|
color: color-mix(in srgb, var(--gleich) 30%, #ffffff);
|
|
}
|
|
.saal[data-stand="vorbereitung"] .buehne-schild__punkt { background: var(--gleich); }
|
|
|
|
.saal[data-stand="live"] .buehne-schild {
|
|
border-color: color-mix(in srgb, var(--an-hell) 50%, transparent);
|
|
color: #ffe9ea;
|
|
box-shadow: 0 0 30px -10px var(--an), 0 16px 38px -18px #000;
|
|
}
|
|
.saal[data-stand="live"] .buehne-schild__punkt {
|
|
background: var(--an);
|
|
box-shadow: 0 0 12px 1px var(--an);
|
|
animation: reakt-atem 4s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes reakt-atem {
|
|
0%, 100% { opacity: 1; transform: scale(1); }
|
|
50% { opacity: .5; transform: scale(.82); }
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------
|
|
HÄUTE EIN- UND AUSBLENDEN
|
|
--------------------------------------------------------------------- */
|
|
#teil-zu, #teil-warte, #teil-live { display: none; }
|
|
.saal[data-stand="zu"] #teil-zu { display: grid; }
|
|
.saal[data-stand="vorbereitung"] #teil-warte { display: grid; }
|
|
.saal[data-stand="live"] #teil-live { display: grid; }
|
|
|
|
/* =====================================================================
|
|
ZU — der Vorhang
|
|
===================================================================== */
|
|
.vorhang {
|
|
position: relative;
|
|
place-items: center;
|
|
padding: 0 28px;
|
|
text-align: center;
|
|
}
|
|
.vorhang__licht {
|
|
position: absolute; inset: 0;
|
|
background:
|
|
radial-gradient(90% 55% at 50% 0%, rgba(120, 130, 165, .12), transparent 62%),
|
|
radial-gradient(120% 70% at 50% 104%, rgba(70, 80, 115, .10), transparent 60%);
|
|
pointer-events: none;
|
|
}
|
|
.vorhang__innen { position: relative; max-width: 30rem; }
|
|
.vorhang__marke {
|
|
display: flex; align-items: center; justify-content: center; gap: 10px;
|
|
font-size: .74rem; font-weight: 700;
|
|
letter-spacing: .38em; text-transform: uppercase;
|
|
color: var(--schrift-leise);
|
|
}
|
|
.vorhang__marke i {
|
|
display: block; width: 26px; height: 1px;
|
|
background: linear-gradient(90deg, transparent, var(--schrift-leise));
|
|
}
|
|
.vorhang__marke i + i { background: linear-gradient(90deg, var(--schrift-leise), transparent); }
|
|
|
|
.vorhang__titel {
|
|
margin: 16px 0 0;
|
|
font-size: clamp(1.9rem, 7vw, 2.7rem);
|
|
font-weight: 800; line-height: 1.08;
|
|
letter-spacing: -.024em;
|
|
color: var(--schrift);
|
|
text-wrap: balance;
|
|
}
|
|
.vorhang__satz {
|
|
margin: 14px 0 0;
|
|
font-size: .96rem; line-height: 1.6;
|
|
color: var(--schrift-leise);
|
|
text-wrap: pretty;
|
|
}
|
|
.vorhang__naechste {
|
|
margin: 22px 0 0;
|
|
display: inline-block;
|
|
padding: 9px 16px;
|
|
border-radius: 12px;
|
|
background: color-mix(in srgb, var(--gleich) 12%, transparent);
|
|
border: 1px solid color-mix(in srgb, var(--gleich) 30%, transparent);
|
|
color: color-mix(in srgb, var(--gleich) 24%, #ffffff);
|
|
font-size: .86rem; font-weight: 700;
|
|
}
|
|
|
|
/* =====================================================================
|
|
VORBEREITUNG — der Wartebereich
|
|
===================================================================== */
|
|
.warte { place-items: center; padding: 0 20px; }
|
|
.warte__buehne {
|
|
display: grid; gap: 26px;
|
|
width: min(54rem, 100%);
|
|
grid-template-columns: 1fr;
|
|
text-align: center;
|
|
}
|
|
.warte__bild {
|
|
aspect-ratio: 16 / 9;
|
|
border-radius: 20px;
|
|
background: linear-gradient(160deg, #131120, #08070e);
|
|
border: 1px solid var(--rand);
|
|
background-size: cover; background-position: center;
|
|
box-shadow: 0 40px 80px -46px #000;
|
|
}
|
|
.warte__kicker {
|
|
font-size: .74rem; font-weight: 800;
|
|
letter-spacing: .34em; text-transform: uppercase;
|
|
color: color-mix(in srgb, var(--gleich) 26%, #ffffff);
|
|
}
|
|
.warte__titel {
|
|
margin: 12px 0 0;
|
|
font-size: clamp(1.6rem, 6vw, 2.3rem);
|
|
font-weight: 800; line-height: 1.1;
|
|
letter-spacing: -.022em; color: var(--schrift);
|
|
text-wrap: balance;
|
|
}
|
|
.warte__satz { margin: 14px 0 0; color: var(--schrift-leise); font-size: .92rem; }
|
|
|
|
.countdown {
|
|
display: inline-flex; align-items: baseline; gap: 10px;
|
|
margin: 18px 0 0; padding: 10px 20px 12px;
|
|
border-radius: 16px;
|
|
background: color-mix(in srgb, var(--gleich) 10%, rgba(8, 7, 14, .7));
|
|
border: 1px solid color-mix(in srgb, var(--gleich) 32%, transparent);
|
|
}
|
|
.countdown__zahl {
|
|
font-size: 2rem; font-weight: 800;
|
|
letter-spacing: -.03em;
|
|
color: color-mix(in srgb, var(--gleich) 22%, #ffffff);
|
|
/* Tabellenziffern: Ohne sie springt die ganze Zeile bei jedem
|
|
Sekundenwechsel, weil die 1 schmaler ist als die 8. */
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.countdown__wort { font-size: .78rem; color: var(--schrift-leise); letter-spacing: .04em; }
|
|
|
|
/* =====================================================================
|
|
LIVE — der Kinosaal
|
|
===================================================================== */
|
|
.kino {
|
|
grid-template-columns: 1fr minmax(19rem, 23rem);
|
|
gap: 0;
|
|
height: 100%;
|
|
min-height: 0; /* sonst wächst das Raster über das Fenster */
|
|
}
|
|
|
|
.leinwand {
|
|
position: relative;
|
|
min-width: 0; min-height: 0;
|
|
display: grid; place-items: center;
|
|
background: #000;
|
|
overflow: hidden;
|
|
}
|
|
.leinwand__video { position: relative; width: 100%; height: 100%; }
|
|
.leinwand__video iframe {
|
|
position: absolute; inset: 0;
|
|
width: 100%; height: 100%;
|
|
border: 0;
|
|
}
|
|
.leinwand__warte {
|
|
position: absolute; inset: 0;
|
|
display: grid; place-items: center;
|
|
color: var(--schrift-leise); font-size: .9rem;
|
|
}
|
|
|
|
/* ---- Die Kamerafenster ----------------------------------------------
|
|
Sie liegen ÜBER dem Video. Genau so sieht eine Reaction aus: das
|
|
Bild groß, das Gesicht klein davor.
|
|
|
|
`pointer-events: none` am Behälter und `auto` an den Fenstern: Der
|
|
Bereich dazwischen gehört dem Video (Pause, Lautstärke), die
|
|
Fenster selbst nehmen Klicks an. Ohne das läge eine unsichtbare
|
|
Scheibe über der ganzen Leinwand.
|
|
--------------------------------------------------------------------- */
|
|
.kameras {
|
|
position: absolute;
|
|
right: 16px; bottom: 16px;
|
|
display: flex; flex-direction: column; gap: 12px;
|
|
pointer-events: none;
|
|
z-index: 20;
|
|
}
|
|
.kamera {
|
|
position: relative;
|
|
width: clamp(120px, 17vw, 208px);
|
|
aspect-ratio: 4 / 3;
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
background: #0b0a12;
|
|
border: 1px solid var(--rand-hell);
|
|
box-shadow: 0 24px 50px -22px #000;
|
|
pointer-events: auto;
|
|
}
|
|
.kamera video {
|
|
width: 100%; height: 100%;
|
|
object-fit: cover;
|
|
display: block;
|
|
background: #0b0a12;
|
|
}
|
|
.kamera__name {
|
|
position: absolute; left: 8px; bottom: 8px;
|
|
padding: 3px 9px 4px;
|
|
border-radius: 999px;
|
|
font-size: .72rem; font-weight: 700;
|
|
background: rgba(6, 5, 11, .76);
|
|
border: 1px solid var(--rand);
|
|
color: var(--schrift);
|
|
backdrop-filter: blur(6px);
|
|
max-width: calc(100% - 16px);
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
.kamera[data-host="ja"] { border-color: color-mix(in srgb, var(--an-hell) 46%, transparent); }
|
|
.kamera[data-stumm="ja"]::after {
|
|
content: "stumm";
|
|
position: absolute; right: 8px; top: 8px;
|
|
padding: 3px 8px 4px; border-radius: 999px;
|
|
font-size: .72rem; font-weight: 800; letter-spacing: .08em;
|
|
background: rgba(216, 64, 74, .82); color: #fff;
|
|
}
|
|
.kamera--leer {
|
|
display: grid; place-items: center;
|
|
font-size: .72rem; color: var(--schrift-leise);
|
|
border-style: dashed;
|
|
}
|
|
|
|
/* Anordnung „gleich": Video und Kameras nebeneinander statt übereinander. */
|
|
.kino[data-layout="gleich"] .kameras {
|
|
position: static;
|
|
flex-direction: row;
|
|
justify-content: center;
|
|
padding: 10px;
|
|
background: #07060d;
|
|
}
|
|
.kino[data-layout="gleich"] .leinwand {
|
|
grid-template-rows: 1fr auto;
|
|
align-content: stretch;
|
|
}
|
|
/* Anordnung „kamera": die Kameras groß, das Video klein in der Ecke. */
|
|
.kino[data-layout="kamera"] .kamera { width: clamp(160px, 30vw, 380px); }
|
|
.kino[data-layout="kamera"] .kameras {
|
|
right: auto; left: 50%; top: 50%; bottom: auto;
|
|
transform: translate(-50%, -50%);
|
|
flex-direction: row;
|
|
}
|
|
.kino[data-layout="kamera"] .leinwand__video {
|
|
position: absolute; right: 14px; bottom: 14px;
|
|
width: clamp(140px, 22vw, 260px); height: auto; aspect-ratio: 16/9;
|
|
border-radius: 14px; overflow: hidden;
|
|
border: 1px solid var(--rand-hell);
|
|
z-index: 25;
|
|
}
|
|
|
|
/* ---- Die Chatschiene ------------------------------------------------- */
|
|
.schiene {
|
|
display: grid;
|
|
grid-template-rows: auto 1fr auto auto;
|
|
min-height: 0;
|
|
background: #07060d;
|
|
border-left: 1px solid var(--rand);
|
|
}
|
|
.schiene__kopf {
|
|
display: flex; align-items: center; gap: 10px;
|
|
padding: 14px 16px 12px;
|
|
border-bottom: 1px solid var(--rand);
|
|
}
|
|
.schiene__titel {
|
|
margin: 0; font-size: .74rem; font-weight: 800;
|
|
letter-spacing: .16em; text-transform: uppercase;
|
|
color: var(--schrift-leise);
|
|
}
|
|
.schiene__zahl {
|
|
margin-left: auto;
|
|
font-size: .72rem; font-weight: 700;
|
|
padding: 2px 9px 3px; border-radius: 999px;
|
|
background: rgba(255, 255, 255, .06);
|
|
border: 1px solid var(--rand);
|
|
color: var(--schrift-leise);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.schiene__zu { display: none; }
|
|
|
|
.schiene__liste {
|
|
margin: 0; padding: 12px 14px;
|
|
list-style: none;
|
|
overflow-y: auto;
|
|
min-height: 0;
|
|
display: flex; flex-direction: column; gap: 9px;
|
|
scrollbar-width: thin;
|
|
}
|
|
.beitrag {
|
|
display: grid; gap: 2px;
|
|
padding: 8px 11px 9px;
|
|
border-radius: 12px;
|
|
background: rgba(255, 255, 255, .035);
|
|
border: 1px solid transparent;
|
|
font-size: .86rem; line-height: 1.42;
|
|
color: var(--schrift);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
.beitrag__wer {
|
|
font-size: .72rem; font-weight: 800;
|
|
letter-spacing: .02em;
|
|
color: var(--schrift-leise);
|
|
display: flex; align-items: center; gap: 7px;
|
|
}
|
|
.beitrag__weg {
|
|
margin-left: auto;
|
|
appearance: none; border: 0; background: none;
|
|
color: var(--schrift-leise);
|
|
cursor: pointer; padding: 2px 4px; border-radius: 6px;
|
|
font-size: .72rem;
|
|
}
|
|
.beitrag__weg:hover { color: var(--an-hell); background: rgba(216, 64, 74, .12); }
|
|
.beitrag[data-host="ja"] {
|
|
background: color-mix(in srgb, var(--an) 12%, transparent);
|
|
border-color: color-mix(in srgb, var(--an) 30%, transparent);
|
|
}
|
|
.beitrag[data-host="ja"] .beitrag__wer { color: var(--an-hell); }
|
|
.beitrag[data-team="ja"] .beitrag__wer { color: #9ab7ff; }
|
|
|
|
.schiene__form {
|
|
display: flex; gap: 8px;
|
|
padding: 12px 14px;
|
|
border-top: 1px solid var(--rand);
|
|
}
|
|
.schiene__feld {
|
|
flex: 1 1 auto; min-width: 0;
|
|
appearance: none;
|
|
padding: 11px 13px;
|
|
border-radius: 12px;
|
|
border: 1px solid var(--rand);
|
|
background: rgba(255, 255, 255, .04);
|
|
color: var(--schrift);
|
|
font: inherit; font-size: .9rem;
|
|
}
|
|
.schiene__feld:focus-visible {
|
|
outline: none;
|
|
border-color: var(--rand-hell);
|
|
box-shadow: 0 0 0 3px rgba(154, 183, 255, .16);
|
|
}
|
|
.schiene__senden {
|
|
flex: none;
|
|
width: 44px; height: 44px;
|
|
display: grid; place-items: center;
|
|
border-radius: 12px;
|
|
border: 1px solid color-mix(in srgb, var(--an) 42%, transparent);
|
|
background: color-mix(in srgb, var(--an) 24%, transparent);
|
|
color: #ffd9db; cursor: pointer;
|
|
}
|
|
.schiene__senden svg { width: 19px; height: 19px; fill: currentColor; }
|
|
|
|
.danke {
|
|
display: flex; align-items: center; justify-content: center; gap: 9px;
|
|
margin: 0 14px 14px;
|
|
padding: 12px 14px;
|
|
min-height: 44px;
|
|
border-radius: 12px;
|
|
text-decoration: none;
|
|
font-size: .86rem; font-weight: 700;
|
|
color: #0a2a1c;
|
|
background: linear-gradient(180deg, #7de2a8, #47c97f);
|
|
box-shadow: 0 14px 30px -16px #47c97f;
|
|
}
|
|
.danke svg { width: 18px; height: 18px; fill: currentColor; flex: none; }
|
|
|
|
|
|
/* =====================================================================
|
|
DAS REGIEPULT
|
|
===================================================================== */
|
|
/* ==== DAS PULT SCHIEBT, ES DECKT NICHT ZU =========================
|
|
(28.09.2026, gefunden im ersten Bild.)
|
|
|
|
Erst lag es als `position: fixed` ueber der Seite. Aufgeklappt
|
|
verdeckte es die untere Haelfte: Das Video war abgeschnitten, der
|
|
Chat auch -- ausgerechnet fuer den, der sendet und beides im Blick
|
|
haben muss.
|
|
|
|
Jetzt ist es die zweite Zeile des Saals. Der Saal ist ein Raster
|
|
mit `1fr auto`; klappt das Pult auf, wird der Kinosaal darueber
|
|
von selbst kleiner. Nichts wird verdeckt, und niemand muss eine
|
|
Hoehe ausrechnen, die spaeter nicht mehr stimmt. */
|
|
.saal { grid-template-rows: 1fr auto; }
|
|
|
|
.pult {
|
|
z-index: 60;
|
|
background: rgba(7, 6, 13, .94);
|
|
backdrop-filter: blur(18px) saturate(1.2);
|
|
-webkit-backdrop-filter: blur(18px) saturate(1.2);
|
|
border-top: 1px solid var(--rand-hell);
|
|
box-shadow: 0 -30px 70px -40px #000;
|
|
}
|
|
.pult__griff {
|
|
width: 100%;
|
|
appearance: none; border: 0; background: none;
|
|
display: flex; align-items: center; justify-content: center; gap: 10px;
|
|
padding: 9px 0 8px;
|
|
min-height: 44px;
|
|
cursor: pointer;
|
|
color: var(--schrift-leise);
|
|
font-size: .74rem; font-weight: 800;
|
|
letter-spacing: .2em; text-transform: uppercase;
|
|
}
|
|
.pult__griff-strich {
|
|
width: 34px; height: 3px; border-radius: 3px;
|
|
background: var(--rand-hell);
|
|
}
|
|
.pult__innen {
|
|
display: none;
|
|
gap: 18px;
|
|
padding: 4px 18px 18px;
|
|
/* Hoechstens die halbe Hoehe -- darueber bleibt vom Saal nichts
|
|
mehr uebrig, und wer sendet, schaut auf das Bild und nicht auf
|
|
die Knoepfe. Was nicht hineinpasst, rollt. */
|
|
max-height: min(46dvh, 26rem);
|
|
overflow-y: auto;
|
|
}
|
|
.pult[data-auf="ja"] .pult__innen { display: grid; }
|
|
|
|
.pult__block {
|
|
display: grid; gap: 10px;
|
|
padding: 14px 15px 15px;
|
|
border-radius: 14px;
|
|
background: rgba(255, 255, 255, .03);
|
|
border: 1px solid var(--rand);
|
|
}
|
|
.pult__ueber {
|
|
margin: 0;
|
|
font-size: .74rem; font-weight: 800;
|
|
letter-spacing: .2em; text-transform: uppercase;
|
|
color: var(--schrift-leise);
|
|
display: flex; align-items: baseline; gap: 10px;
|
|
}
|
|
.pult__klein { font-size: .72rem; letter-spacing: .04em; text-transform: none; opacity: .8; }
|
|
.pult__zeile { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
|
|
.pult__feld { display: grid; gap: 5px; flex: 1 1 14rem; min-width: 0; }
|
|
.pult__feld--kurz { flex: 0 1 12rem; }
|
|
.pult__feld > span {
|
|
font-size: .72rem; font-weight: 700;
|
|
color: var(--schrift-leise); letter-spacing: .04em;
|
|
}
|
|
.pult__feld input, .pult__wahl {
|
|
appearance: none;
|
|
padding: 10px 12px; min-height: 44px;
|
|
border-radius: 11px;
|
|
border: 1px solid var(--rand);
|
|
background: rgba(255, 255, 255, .04);
|
|
color: var(--schrift); font: inherit; font-size: .88rem;
|
|
min-width: 0;
|
|
}
|
|
.pult__wahl { flex: 1 1 12rem; }
|
|
|
|
/* ---- Die Regler ------------------------------------------------------
|
|
Vier Tonquellen nebeneinander: Host, Gast 1, Gast 2, YouTube. Sie
|
|
stehen SENKRECHT wie an einem Mischpult und nicht waagerecht wie
|
|
ein Formularregler -- vier waagerechte Regler untereinander liest
|
|
man als Liste, vier senkrechte nebeneinander sieht man auf einen
|
|
Blick.
|
|
--------------------------------------------------------------------- */
|
|
.regler-reihe { display: flex; gap: 12px; flex-wrap: wrap; }
|
|
.regler {
|
|
display: grid; justify-items: center; gap: 8px;
|
|
padding: 12px 10px 10px;
|
|
border-radius: 12px;
|
|
background: rgba(0, 0, 0, .3);
|
|
border: 1px solid var(--rand);
|
|
min-width: 5.4rem;
|
|
}
|
|
.regler__wort {
|
|
font-size: .74rem; font-weight: 800;
|
|
letter-spacing: .08em; text-transform: uppercase;
|
|
color: var(--schrift-leise);
|
|
max-width: 5rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
.regler input[type="range"] {
|
|
appearance: none; -webkit-appearance: none;
|
|
writing-mode: vertical-lr;
|
|
direction: rtl;
|
|
width: 26px; height: 104px;
|
|
background: none;
|
|
}
|
|
.regler input[type="range"]::-webkit-slider-runnable-track {
|
|
width: 6px; border-radius: 6px;
|
|
background: linear-gradient(180deg, var(--an), #3a3550);
|
|
}
|
|
.regler input[type="range"]::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
width: 22px; height: 14px; border-radius: 4px;
|
|
background: #e9e6f2;
|
|
border: 1px solid #0b0a12;
|
|
box-shadow: 0 2px 8px -2px #000;
|
|
margin-left: -8px;
|
|
}
|
|
.regler input[type="range"]::-moz-range-track {
|
|
width: 6px; border-radius: 6px;
|
|
background: linear-gradient(180deg, var(--an), #3a3550);
|
|
}
|
|
.regler input[type="range"]::-moz-range-thumb {
|
|
width: 22px; height: 14px; border-radius: 4px;
|
|
background: #e9e6f2; border: 1px solid #0b0a12;
|
|
}
|
|
.regler__zahl {
|
|
font-size: .72rem; font-variant-numeric: tabular-nums;
|
|
color: var(--schrift-leise);
|
|
}
|
|
.regler__stumm {
|
|
appearance: none; cursor: pointer;
|
|
min-height: 30px; padding: 4px 10px;
|
|
border-radius: 8px;
|
|
border: 1px solid var(--rand);
|
|
background: rgba(255, 255, 255, .04);
|
|
color: var(--schrift-leise);
|
|
font-size: .72rem; font-weight: 700;
|
|
}
|
|
.regler[data-stumm="ja"] .regler__stumm {
|
|
background: color-mix(in srgb, var(--an) 30%, transparent);
|
|
border-color: color-mix(in srgb, var(--an) 50%, transparent);
|
|
color: #ffd9db;
|
|
}
|
|
|
|
/* ---- Gäste ----------------------------------------------------------- */
|
|
.gast-reihe { display: flex; flex-wrap: wrap; gap: 9px; }
|
|
.gast-karte {
|
|
display: flex; align-items: center; gap: 9px;
|
|
padding: 8px 10px 8px 12px;
|
|
border-radius: 999px;
|
|
background: rgba(255, 255, 255, .04);
|
|
border: 1px solid var(--rand);
|
|
font-size: .82rem; color: var(--schrift);
|
|
}
|
|
.gast-karte__platz {
|
|
font-size: .72rem; font-weight: 800;
|
|
color: var(--schrift-leise); letter-spacing: .08em;
|
|
}
|
|
.gast-karte button {
|
|
appearance: none; cursor: pointer;
|
|
min-width: 34px; min-height: 34px;
|
|
border-radius: 999px;
|
|
border: 1px solid var(--rand);
|
|
background: rgba(255, 255, 255, .05);
|
|
color: var(--schrift-leise);
|
|
font-size: .72rem; font-weight: 700;
|
|
}
|
|
.gast-leer { font-size: .8rem; color: var(--schrift-leise); }
|
|
|
|
/* ---- Anordnung ------------------------------------------------------- */
|
|
.layout-reihe { display: flex; gap: 9px; flex-wrap: wrap; }
|
|
.layout-knopf {
|
|
appearance: none; cursor: pointer;
|
|
min-height: 44px; padding: 9px 15px;
|
|
border-radius: 11px;
|
|
border: 1px solid var(--rand);
|
|
background: rgba(255, 255, 255, .04);
|
|
color: var(--schrift-leise);
|
|
font-size: .82rem; font-weight: 700;
|
|
}
|
|
.layout-knopf[aria-pressed="true"] {
|
|
background: rgba(154, 183, 255, .14);
|
|
border-color: rgba(154, 183, 255, .38);
|
|
color: #cfe0ff;
|
|
}
|
|
|
|
/* ---- Die drei großen Knöpfe ------------------------------------------ */
|
|
.pult__block--tun {
|
|
display: flex; flex-wrap: wrap; gap: 10px;
|
|
background: none; border: 0; padding: 0;
|
|
}
|
|
.tun {
|
|
appearance: none; cursor: pointer;
|
|
flex: 1 1 10rem;
|
|
min-height: 52px;
|
|
border-radius: 14px;
|
|
border: 1px solid var(--rand-hell);
|
|
background: rgba(255, 255, 255, .05);
|
|
color: var(--schrift);
|
|
font-size: .94rem; font-weight: 800;
|
|
letter-spacing: .01em;
|
|
}
|
|
.tun--vor {
|
|
border-color: color-mix(in srgb, var(--gleich) 44%, transparent);
|
|
background: color-mix(in srgb, var(--gleich) 14%, transparent);
|
|
color: color-mix(in srgb, var(--gleich) 24%, #ffffff);
|
|
}
|
|
.tun--live {
|
|
border-color: color-mix(in srgb, var(--an) 56%, transparent);
|
|
background: linear-gradient(180deg, color-mix(in srgb, var(--an) 74%, #000), color-mix(in srgb, var(--an) 46%, #000));
|
|
color: #fff3f3;
|
|
box-shadow: 0 16px 34px -20px var(--an);
|
|
}
|
|
.tun--aus { color: var(--schrift-leise); }
|
|
.tun[disabled] { opacity: .4; cursor: default; }
|
|
|
|
/* =====================================================================
|
|
DAS HANDY
|
|
|
|
Aus zwei Spalten wird ein Stapel, und der Chat wird zur Lade. Das
|
|
Video bleibt oben und behält sein Seitenverhältnis -- es ist der
|
|
Grund, warum jemand hier ist.
|
|
===================================================================== */
|
|
@media (max-width: 860px) {
|
|
.kino {
|
|
grid-template-columns: 1fr;
|
|
grid-template-rows: auto 1fr;
|
|
}
|
|
.leinwand {
|
|
aspect-ratio: 16 / 9;
|
|
height: auto;
|
|
width: 100%;
|
|
}
|
|
.schiene {
|
|
border-left: 0;
|
|
border-top: 1px solid var(--rand);
|
|
min-height: 0;
|
|
}
|
|
.kameras {
|
|
right: 10px; bottom: 10px;
|
|
flex-direction: row;
|
|
}
|
|
.kamera { width: clamp(88px, 26vw, 140px); }
|
|
|
|
/* ==== KEINE LADE, SONDERN EIN STAPEL =========================
|
|
(28.09.2026, nach der ersten Messung auf dem Handy.)
|
|
|
|
Hier stand eine Lade: Der Chat lag zugeklappt über allem und
|
|
fuhr auf Knopfdruck von unten ein. Gemessen war das Ergebnis
|
|
ein 390x775 großer Saal mit einem 219 Pixel hohen Video und
|
|
556 Pixeln Leere darunter -- weil der Knopf, der die Lade
|
|
öffnet, gar nicht erschien.
|
|
|
|
Statt den Knopf zu reparieren, ist die Lade weg. Sie war die
|
|
falsche Lösung für ein Problem, das es nicht gibt: Auf einem
|
|
844 Pixel hohen Telefon bleiben unter Kopfleiste und Video
|
|
rund 550 Pixel übrig. Das ist mehr Chat, als eine Lade je
|
|
zeigen würde -- und es gibt keinen Zustand, in dem etwas
|
|
klemmen kann.
|
|
|
|
Ein Zustand weniger ist besser als ein Zustand, der
|
|
funktioniert. */
|
|
.schiene { display: grid; }
|
|
.schiene__zu { display: none !important; }
|
|
|
|
/* Das Schild wird kleiner und rueckt hoch: Auf 390 Pixeln teilt es
|
|
sich den Platz mit der Titelzeile von YouTube, und zwei Zeilen
|
|
Grossbuchstaben sind dort ein Klotz. */
|
|
.buehne-schild {
|
|
top: 8px;
|
|
padding: 5px 11px 6px;
|
|
font-size: .64rem;
|
|
gap: 7px;
|
|
white-space: nowrap;
|
|
}
|
|
.buehne-schild__zahl { padding-left: 7px; }
|
|
|
|
/* Der Ton-Knopf in die MITTE des Bildes. Unten liegen bei 219
|
|
Pixeln Hoehe schon die Kamerafenster -- der Knopf lag halb
|
|
darueber. In der Mitte steht er dort, wo ohnehin hingesehen
|
|
wird, und verdeckt nur das, was man gleich sowieso hoert. */
|
|
.ton-an {
|
|
bottom: auto;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
|
|
.warte__buehne { gap: 18px; }
|
|
.pult__innen { max-height: 62dvh; }
|
|
}
|
|
|
|
/* Wer weniger Bewegung eingestellt hat, bekommt keinen Puls. Die
|
|
Aussage bleibt -- der Punkt steht dann einfach da. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.buehne-schild__punkt { animation: none !important; }
|
|
}
|
|
|
|
/* ---- Der Ton-Knopf ---------------------------------------------------
|
|
Er liegt über der Leinwand, mittig unten -- dort, wo der Blick
|
|
ohnehin ist, und weit genug von den Kamerafenstern entfernt. Er
|
|
verschwindet in dem Moment, in dem der Ton läuft; ein Knopf, der
|
|
bleibt, nachdem er getan hat, was er soll, wird zur Deko. */
|
|
.ton-an {
|
|
position: absolute;
|
|
left: 50%; bottom: 20px;
|
|
transform: translateX(-50%);
|
|
z-index: 30;
|
|
display: flex; align-items: center; gap: 9px;
|
|
appearance: none; cursor: pointer;
|
|
min-height: 44px; padding: 10px 18px;
|
|
border-radius: 999px;
|
|
border: 1px solid color-mix(in srgb, var(--an-hell) 50%, transparent);
|
|
background: rgba(10, 8, 16, .88);
|
|
backdrop-filter: blur(12px);
|
|
-webkit-backdrop-filter: blur(12px);
|
|
color: #ffe9ea;
|
|
font-size: .88rem; font-weight: 700;
|
|
box-shadow: 0 18px 40px -18px #000, 0 0 26px -10px var(--an);
|
|
}
|
|
.ton-an svg {
|
|
width: 19px; height: 19px;
|
|
fill: none; stroke: currentColor; stroke-width: 1.8;
|
|
stroke-linecap: round; stroke-linejoin: round;
|
|
}
|