Filipe: "in dieser kachel soll wenn ich live bin um 20h bis 22h ein
live button der richtig geil ist mit einem live roten punkt symbol am
besten, das soll aufblinken fuer zwei stunden. man soll nicht drauf
druecken koennen aber so dass es auffaellt. ... die leute sollen auch
automatisch eine benarichtigung bekommen um 20 uhr dass ich live bin."
EINE AUSKUNFT, EINE AUSLEGUNG (workspace/assets/js/live.js)
Drei Stellen sollen dasselbe wissen: die Kachel "Draussen" auf der
Startseite, die Kanalkarten auf der Draussen-Seite, die Pulskarte
darunter. Drei Abfragen waeren drei Auslegungen -- und spaetestens
beim naechsten Umbau steht auf der Kachel LIVE und daneben "wartet".
DER DRITTE AUSGANG IST HIER DIE EIGENTLICHE ARBEIT
Die oeffentliche Seite (assets/js/streamplan.js) faengt jeden Fehler
ab und setzt istLive = false. Aus "wir konnten nicht fragen" wird
dort "er sendet nicht" -- wer das liest, macht zu und verpasst den
Stream. Der Dienst SAGT sogar, ob er nachsehen konnte (autoHealthy);
gelesen wird es dort nicht.
Hier gibt es drei Antworten: live / wartet / weiss-nicht. Bei
"weiss nicht" blinkt nichts -- aber es behauptet auch niemand, dass
nichts laeuft. Sechs Antworten des Dienstes durchgespielt.
BLINKEN OHNE FLACKERN
Hausregel vom 12.08.2026: keine aggressiven Flacker-Effekte. Das ist
kein Widerspruch zu "richtig geil", sondern dieselbe Sache: Ein Rot,
das im Sekundentakt umspringt, liest man als "Alarm, ich sehe weg".
Eins, das atmet und einen Saum nach aussen schickt, liest man als
"jetzt gerade". 1,4 Sekunden, ueberall derselbe Takt -- zwei Rhythmen
nebeneinander waeren Unruhe, einer ist ein Herzschlag. Die Pruefung
verlangt ausdruecklich >= 1 s und einen einheitlichen Takt; nach dem
Wort "animation" zu suchen waere auch bei 0,1 s gruen.
Bei Bewegungsarmut steht alles still -- der rote Punkt bleibt
sichtbar, er bewegt sich nur nicht. Im Kontrastmodus sagt es ein
Rahmen, weil Farben dort nichts tragen.
KEIN KNOPF, UND ES SIEHT AUCH NICHT SO AUS
pointer-events: none ist die halbe Antwort; die andere ist die Form.
Etwas, das wie ein Knopf aussieht und nichts tut, ist eine Sackgasse.
Deshalb eine Marke wie auf einer Kamera: Punkt plus Wort. Das Wort
steht auch fuer Vorleseprogramme da -- ein stummer roter Kreis waere
die halbe Auskunft.
DIE KACHEL WIRD UEBER IHR ZIEL ERKANNT, NICHT UEBER DEN NAMEN.
Der Name hat sich in diesem Haus schon zweimal geaendert ("Unsere
Seiten" -> "Draussen"), das Ziel nie. Am 21.09. hat genau dieser
Unterschied eine ganze Hinweisspalte lahmgelegt.
DIE BENACHRICHTIGUNG ENTSCHEIDET DER DIENST, NICHT DIE UHR.
Eine Meldung "er ist live", waehrend er nicht sendet, funktioniert
genau einmal: Beim zweiten Mal weiss jeder, dass sie nichts bedeutet.
Deshalb prueft der Lauf (alle fuenf Minuten) den echten Status; bei
"weiss nicht" wird NICHTS verschickt. Ein Merkmal mit Datum haelt es
bei einer pro Abend -- sonst kaemen in zwei Stunden 24.
Eigene Art "dogfather_live", vorgegeben an und trotzdem abschaltbar:
Wer jeden Abend dieselbe Meldung bekommt und nie hinschaut, schaltet
sonst ALLES ab, und dann erreicht ihn auch keine Aufgabe mehr.
EIN FEHLER, DEN DIE PRUEFUNG GEFANGEN HAT: Der Versand las aus
"push_geraete" -- diese Tabelle gibt es nicht, sie heisst
push_anmeldungen. Der Block steht in einem try/catch; der Fehler
waere still geblieben, und die Benachrichtigung waere nie gekommen.
Die Pruefung verlangt jetzt ausdruecklich, dass die Tabelle im
Schema existiert.
NEU: pruef-livepunkt 49/0, mit drei Gegenproben.
Daneben gruen: pruef-css-klassen 30/0, pruef-push 24/0,
pruef-draussen 39/0, pruef-zwischenspeicher 27/0.
Co-Authored-By: Claude Opus 5 <[email protected]>
859 lines
27 KiB
CSS
859 lines
27 KiB
CSS
/* =====================================================================
|
||
UNSERE SEITEN (19.09.2026)
|
||
|
||
Zwei Karten, die aus dem Treff hinausführen: die Website und
|
||
VanVans Shop.
|
||
|
||
WARUM SIE ANDERS AUSSEHEN ALS DIE BRETTER-KACHELN: Sie tun etwas
|
||
anderes. Eine Brett-Kachel öffnet eine Liste im selben Fenster; diese
|
||
hier verlassen den Treff. Dass man das vorher sieht, ist kein
|
||
Schmuck, sondern die Ansage — deshalb der eigene Zuschnitt, der
|
||
deutlich sichtbare Adressname und der Pfeil nach außen.
|
||
|
||
AUGENSCHONEND, WIE ÜBERALL IM HAUS: Die Farbe liegt im Verlauf und
|
||
im Rand, nie in einer leuchtenden Fläche hinter Text. Bewegung gibt
|
||
es nur beim Darüberfahren, und `prefers-reduced-motion` schaltet sie
|
||
ganz ab.
|
||
===================================================================== */
|
||
|
||
.seiten-gitter {
|
||
display: grid;
|
||
gap: 1.1rem;
|
||
/* EINE REGEL STATT EINER ZAHL. Zwei Spalten, sobald zweimal 21rem
|
||
plus Abstand hineinpassen — und sonst eine. Kommt eine dritte
|
||
Karte dazu, ordnet sich das von selbst, ohne dass jemand eine
|
||
Schwelle nachziehen muss. */
|
||
grid-template-columns: repeat(auto-fit, minmax(min(21rem, 100%), 1fr));
|
||
margin: 1.4rem 0 0;
|
||
}
|
||
|
||
.aussen {
|
||
position: relative;
|
||
isolation: isolate;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: .75rem;
|
||
padding: 1.35rem 1.35rem 1.2rem;
|
||
border-radius: 1.1rem;
|
||
border: 1px solid color-mix(in srgb, var(--aussen-ton) 42%, transparent);
|
||
/* DREI LAGEN STATT ZWEI (19.09.2026). Filipe: "auch der hintegrrund
|
||
der kacheln, ich will es viel spezieller".
|
||
|
||
Vorher lag der Ton mit 7 % auf der Flaeche -- auf einem dunklen
|
||
Bildschirm sieht man davon fast nichts, die Kachel wirkte grau.
|
||
Jetzt: ein heller Bogen oben links (woher das Licht kommt), ein
|
||
zweiter, schwacher unten rechts (damit die Flaeche nicht flach
|
||
kippt) und ein Grundverlauf, der den Ton wirklich traegt.
|
||
|
||
Nach oben begrenzt bleibt es trotzdem: Der hellste Punkt liegt bei
|
||
28 %, und Text steht nie darauf -- er hat seine eigene Lage. */
|
||
background:
|
||
radial-gradient(120% 95% at 6% -5%,
|
||
color-mix(in srgb, var(--aussen-ton) 28%, transparent), transparent 58%),
|
||
radial-gradient(90% 80% at 105% 110%,
|
||
color-mix(in srgb, var(--aussen-ton) 16%, transparent), transparent 62%),
|
||
linear-gradient(158deg,
|
||
color-mix(in srgb, var(--aussen-ton) 14%, #0c1420),
|
||
#090f1a 72%);
|
||
box-shadow:
|
||
0 18px 38px -20px color-mix(in srgb, var(--aussen-ton) 85%, transparent),
|
||
inset 0 1px 0 color-mix(in srgb, var(--aussen-ton) 22%, transparent);
|
||
cursor: pointer;
|
||
transition: transform .22s ease, border-color .22s ease, box-shadow .22s ease;
|
||
}
|
||
|
||
/* Die Töne der beiden Karten. Sie stehen hier und nicht im HTML,
|
||
damit eine dritte Karte eine Zeile braucht und nicht zehn.
|
||
|
||
BABYBLAU UND LILA seit dem 19.09.2026 (Filipe: „die linke kachel soll
|
||
babyblau sein und rechte lila").
|
||
|
||
Babyblau ist im Haus schon vergeben — es ist der Ton der Sekunde auf
|
||
der Uhr (#7ec8f2), und dieselbe Farbe zweimal anders zu treffen wäre
|
||
schlampig. Also derselbe Wert.
|
||
|
||
Das Lila ist dasselbe, in das an diesem Tag die Stunden der Uhr
|
||
umgerechnet wurden (OKLCH-Farbton 303°). Beide Kacheln sprechen
|
||
damit die Sprache, die die Startseite ohnehin spricht.
|
||
|
||
`--logo` nennt die Maske. Sie steht an der Karte und nicht am
|
||
Logofeld, damit eine dritte Karte nur diese zwei Zeilen braucht. */
|
||
.aussen--dogi {
|
||
--aussen-ton: #7ec8f2;
|
||
--logo: url("/assets/img/marke/maske-dogi.png");
|
||
}
|
||
.aussen--shop {
|
||
--aussen-ton: #b284ec;
|
||
--logo: url("/assets/img/marke/maske-vanvan.png");
|
||
}
|
||
|
||
/* ---- DIE SCHWEBENDEN LOGOS ------------------------------------------
|
||
|
||
Vier Stück je Karte, verschieden groß, verschieden schnell, mit
|
||
verschiedener Verzögerung. Gleich wäre Tapete.
|
||
|
||
SIE LIEGEN UNTER ALLEM (`z-index: 0` im eigenen Kontext, Text
|
||
darüber). Die Deckkraft liegt zwischen 10 und 16 Prozent — der erste
|
||
Versuch mit 5 bis 9 war auf dem Bildschirmfoto praktisch unsichtbar,
|
||
und „soll mega aussehen" heißt nicht „soll man erahnen können".
|
||
|
||
Nach oben ist trotzdem eine Grenze: Ab etwa 25 Prozent fängt das
|
||
Logo an, mit der Überschrift zu konkurrieren, und der Text wird
|
||
mühsam. Was hier entstehen soll, ist Tiefe, kein zweiter Titel.
|
||
|
||
`mask-image` statt `background-image`: Die PNG-Dateien tragen nur
|
||
Alpha. Die Farbe ist `currentColor` der Karte — dadurch ist das
|
||
Logo automatisch babyblau bzw. lila, ohne zweite Datei. */
|
||
.logo-feld {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 0;
|
||
overflow: hidden;
|
||
border-radius: inherit;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.logo-feld__stueck {
|
||
position: absolute;
|
||
background-color: var(--aussen-ton);
|
||
-webkit-mask-image: var(--logo);
|
||
mask-image: var(--logo);
|
||
-webkit-mask-repeat: no-repeat;
|
||
mask-repeat: no-repeat;
|
||
-webkit-mask-size: contain;
|
||
mask-size: contain;
|
||
-webkit-mask-position: center;
|
||
mask-position: center;
|
||
will-change: transform;
|
||
}
|
||
|
||
/* Die vier Lagen. Die Zahlen sind gesetzt, nicht gerechnet — ein
|
||
Zufallsgenerator würde bei jedem Laden anders aussehen, und dann
|
||
fällt niemandem mehr auf, wenn eines daneben liegt. */
|
||
.logo-feld__stueck[data-n="1"] {
|
||
width: 15rem; height: 15rem; right: -4.5rem; top: -3.5rem;
|
||
opacity: .16;
|
||
animation: logo-schweben-a 26s ease-in-out infinite;
|
||
}
|
||
.logo-feld__stueck[data-n="2"] {
|
||
width: 8rem; height: 8rem; right: 4.5rem; bottom: -1.5rem;
|
||
opacity: .13;
|
||
animation: logo-schweben-b 34s ease-in-out infinite;
|
||
animation-delay: -6s;
|
||
}
|
||
.logo-feld__stueck[data-n="3"] {
|
||
width: 5rem; height: 5rem; right: 12rem; top: 35%;
|
||
opacity: .105;
|
||
animation: logo-schweben-a 41s ease-in-out infinite;
|
||
animation-delay: -15s;
|
||
}
|
||
.logo-feld__stueck[data-n="4"] {
|
||
width: 3.4rem; height: 3.4rem; right: 2rem; top: 18%;
|
||
opacity: .14;
|
||
animation: logo-schweben-b 29s ease-in-out infinite;
|
||
animation-delay: -21s;
|
||
}
|
||
|
||
/* ZWEI BAHNEN, NICHT EINE. Mit nur einer bewegten sich alle vier
|
||
gleich — das sieht aus, als wackle die ganze Kachel. Die zweite
|
||
läuft andersherum, dadurch entsteht der Eindruck, dass sie
|
||
unabhängig voneinander treiben.
|
||
|
||
SEHR KLEINE WEGE (unter 14 px) und sehr lange Dauern (26–41 s). Was
|
||
man beim Hinsehen als Bewegung erkennt, lenkt beim Lesen ab. Das hier
|
||
merkt man erst, wenn man zweimal hinschaut — und genau das ist
|
||
gemeint mit „ganz leicht bewegen". */
|
||
@keyframes logo-schweben-a {
|
||
0% { transform: translate3d(0, 0, 0) rotate(0deg); }
|
||
33% { transform: translate3d(-9px, -11px, 0) rotate(2.5deg); }
|
||
66% { transform: translate3d(7px, -5px, 0) rotate(-1.8deg); }
|
||
100% { transform: translate3d(0, 0, 0) rotate(0deg); }
|
||
}
|
||
@keyframes logo-schweben-b {
|
||
0% { transform: translate3d(0, 0, 0) rotate(0deg); }
|
||
40% { transform: translate3d(8px, 9px, 0) rotate(-3deg); }
|
||
75% { transform: translate3d(-6px, 4px, 0) rotate(1.5deg); }
|
||
100% { transform: translate3d(0, 0, 0) rotate(0deg); }
|
||
}
|
||
|
||
/* Wer weniger Bewegung will, bekommt die Logos trotzdem — nur still.
|
||
Sie ganz zu entfernen wäre falsch: Sie sind Gestaltung, nicht
|
||
Animation. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.logo-feld__stueck { animation: none; }
|
||
}
|
||
|
||
/* Der stille Schein im Hintergrund. Liegt UNTER allem (`z-index: -1`)
|
||
und ist so schwach, dass er bei Tageslicht kaum auffällt — er soll
|
||
Tiefe geben, nicht leuchten. */
|
||
.aussen__schein {
|
||
position: absolute;
|
||
inset: -40% -20% auto -20%;
|
||
height: 78%;
|
||
z-index: -1;
|
||
background: radial-gradient(60% 60% at 50% 50%,
|
||
color-mix(in srgb, var(--aussen-ton) 34%, transparent), transparent 70%);
|
||
filter: blur(30px);
|
||
opacity: .7;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.aussen:hover,
|
||
.aussen:focus-within {
|
||
transform: translateY(-3px);
|
||
border-color: color-mix(in srgb, var(--aussen-ton) 52%, transparent);
|
||
box-shadow: 0 22px 40px -20px color-mix(in srgb, var(--aussen-ton) 85%, transparent);
|
||
}
|
||
|
||
/* ALLES, WAS MAN LIEST, LIEGT ÜBER DEN LOGOS. Ohne diese Zeile wandert
|
||
der Text unter das Logofeld, sobald es eine eigene Ebene bekommt —
|
||
die Karte sähe dann leer aus, und man sucht den Fehler beim Text. */
|
||
.aussen > *:not(.logo-feld):not(.aussen__schein) {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.aussen__kopf {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .85rem;
|
||
}
|
||
|
||
.aussen__zeichen {
|
||
flex: 0 0 auto;
|
||
display: grid;
|
||
place-items: center;
|
||
width: 3rem;
|
||
height: 3rem;
|
||
border-radius: .8rem;
|
||
border: 1px solid color-mix(in srgb, var(--aussen-ton) 38%, transparent);
|
||
background: linear-gradient(150deg,
|
||
color-mix(in srgb, var(--aussen-ton) 20%, transparent), transparent 74%);
|
||
}
|
||
.aussen__zeichen svg {
|
||
width: 1.65rem;
|
||
height: 1.65rem;
|
||
fill: none;
|
||
stroke: color-mix(in srgb, var(--aussen-ton) 78%, #dceaf8);
|
||
stroke-width: 1.6;
|
||
stroke-linecap: round;
|
||
stroke-linejoin: round;
|
||
}
|
||
|
||
.aussen__marke {
|
||
margin: 0 0 .1rem;
|
||
font-size: .74rem;
|
||
letter-spacing: .12em;
|
||
text-transform: uppercase;
|
||
color: color-mix(in srgb, var(--aussen-ton) 55%, #9fb4c9);
|
||
}
|
||
|
||
.aussen__titel {
|
||
margin: 0;
|
||
font-size: 1.32rem;
|
||
line-height: 1.2;
|
||
/* Text bleibt hell und ruhig. Die Farbe steckt im Rahmen, nicht in
|
||
der Schrift — farbige Überschriften auf dunklem Grund sind der
|
||
schnellste Weg zu müden Augen. */
|
||
color: #e9f2fb;
|
||
}
|
||
|
||
.aussen__satz {
|
||
margin: 0;
|
||
color: var(--text-leise, #93a7bd);
|
||
font-size: .95rem;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
/* Was es auf der Seite gibt. Ruhig gesetzt: Der Punkt links ist ein
|
||
kleiner Strich in der Kartenfarbe, kein Aufzählungszeichen — vier
|
||
bunte Punkte untereinander wären mehr Unruhe als Information. */
|
||
.aussen__liste {
|
||
margin: .1rem 0 .2rem;
|
||
padding: 0;
|
||
list-style: none;
|
||
display: grid;
|
||
gap: .42rem;
|
||
}
|
||
.aussen__liste li {
|
||
position: relative;
|
||
padding-left: 1.05rem;
|
||
font-size: .89rem;
|
||
line-height: 1.45;
|
||
color: var(--text-leise, #93a7bd);
|
||
}
|
||
.aussen__liste li::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0;
|
||
top: .62em;
|
||
width: .5rem;
|
||
height: 1px;
|
||
background: color-mix(in srgb, var(--aussen-ton) 60%, transparent);
|
||
}
|
||
|
||
.aussen__weg {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
flex-wrap: wrap;
|
||
gap: .6rem;
|
||
margin: auto 0 0;
|
||
padding-top: .35rem;
|
||
}
|
||
|
||
.aussen__adresse {
|
||
font-size: .82rem;
|
||
color: color-mix(in srgb, var(--aussen-ton) 45%, #8ea3b9);
|
||
word-break: break-all;
|
||
}
|
||
|
||
.aussen__knopf {
|
||
flex: 0 0 auto;
|
||
display: inline-block;
|
||
padding: .55rem 1rem;
|
||
border-radius: .6rem;
|
||
border: 1px solid color-mix(in srgb, var(--aussen-ton) 45%, transparent);
|
||
background: color-mix(in srgb, var(--aussen-ton) 14%, transparent);
|
||
color: #eaf3fc;
|
||
font-weight: 600;
|
||
font-size: .92rem;
|
||
text-decoration: none;
|
||
/* 44 px hoch, damit ein Daumen ihn trifft. */
|
||
min-height: 2.75rem;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
transition: background-color .18s ease, border-color .18s ease;
|
||
}
|
||
.aussen__knopf:hover,
|
||
.aussen__knopf:focus-visible {
|
||
background: color-mix(in srgb, var(--aussen-ton) 26%, transparent);
|
||
border-color: color-mix(in srgb, var(--aussen-ton) 68%, transparent);
|
||
}
|
||
|
||
/* ---- DER GUTSCHEIN ---------------------------------------------------
|
||
|
||
Sieht aus wie ein abgerissener Abschnitt, weil genau das gemeint
|
||
ist: etwas zum Mitnehmen. Die gestrichelte Linie ist das einzige
|
||
Stilmittel — sie sagt "hier abtrennen", ohne ein Wort dafür zu
|
||
brauchen. */
|
||
.gutschein {
|
||
margin: .2rem 0 .1rem;
|
||
padding: .9rem 1rem 1rem;
|
||
border-radius: .8rem;
|
||
border: 1px dashed color-mix(in srgb, var(--aussen-ton) 42%, transparent);
|
||
background: color-mix(in srgb, var(--aussen-ton) 8%, transparent);
|
||
}
|
||
|
||
.gutschein__zeile {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: .55rem;
|
||
margin: 0 0 .6rem;
|
||
}
|
||
|
||
.gutschein__prozent {
|
||
font-size: 1.5rem;
|
||
font-weight: 700;
|
||
line-height: 1;
|
||
color: #f2f7fc;
|
||
}
|
||
|
||
.gutschein__wofuer {
|
||
font-size: .88rem;
|
||
color: var(--text-leise, #93a7bd);
|
||
}
|
||
|
||
.gutschein__code {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: .7rem;
|
||
width: 100%;
|
||
min-height: 3rem;
|
||
padding: .6rem .9rem;
|
||
border-radius: .6rem;
|
||
border: 1px solid color-mix(in srgb, var(--aussen-ton) 50%, transparent);
|
||
background: rgba(255, 255, 255, .04);
|
||
color: #f2f7fc;
|
||
cursor: pointer;
|
||
text-align: left;
|
||
transition: background-color .18s ease, border-color .18s ease;
|
||
}
|
||
.gutschein__code:hover,
|
||
.gutschein__code:focus-visible {
|
||
background: rgba(255, 255, 255, .08);
|
||
border-color: color-mix(in srgb, var(--aussen-ton) 72%, transparent);
|
||
}
|
||
|
||
.gutschein__wort {
|
||
font-family: ui-monospace, "SFMono-Regular", "Cascadia Code", monospace;
|
||
font-size: 1.22rem;
|
||
font-weight: 700;
|
||
letter-spacing: .1em;
|
||
}
|
||
|
||
.gutschein__hinweis {
|
||
font-size: .78rem;
|
||
color: var(--text-leise, #93a7bd);
|
||
text-align: right;
|
||
}
|
||
/* Nach dem Kopieren steht dort die Bestätigung — in Grün, aber in
|
||
einem gedeckten, das auf Dunkel nicht sticht. */
|
||
.gutschein__code[data-kopiert="ja"] .gutschein__hinweis { color: #74c79a; }
|
||
|
||
.gutschein__klein {
|
||
margin: .6rem 0 0;
|
||
font-size: .78rem;
|
||
line-height: 1.45;
|
||
color: var(--text-leise, #93a7bd);
|
||
}
|
||
|
||
.seiten-fuss {
|
||
margin: 1.4rem 0 0;
|
||
font-size: .85rem;
|
||
color: var(--text-leise, #93a7bd);
|
||
}
|
||
|
||
/* Wer Bewegung ausgeschaltet hat, bekommt keine. Die Karte bleibt
|
||
trotzdem erkennbar bedienbar — über Rand und Schatten. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.aussen,
|
||
.aussen__knopf,
|
||
.gutschein__code { transition: none; }
|
||
.aussen:hover,
|
||
.aussen:focus-within { transform: none; }
|
||
}
|
||
|
||
/* =====================================================================
|
||
DER PULS, DAS EVENT UND DIE KANAELE (21.09.2026)
|
||
|
||
Filipe wollte aus dieser Seite etwas machen, das "von den Socken
|
||
haut". Der Weg dahin war NICHT mehr Effekt, sondern eine Frage, die
|
||
vorher niemand beantwortet hat: Laeuft er gerade?
|
||
|
||
DESHALB LIEGT DER GANZE AUFWAND OBEN. Der Puls ist das Erste und
|
||
Groesste auf der Seite, und er ist das Einzige, was sich bewegt --
|
||
und zwar nur dann, wenn die Bewegung etwas bedeutet (er sendet).
|
||
Bewegung, die immer laeuft, ist Tapete; Bewegung, die genau einmal
|
||
im Tag anspringt, ist ein Signal.
|
||
|
||
AUGENSCHONEND WIE ALLES IM HAUS: Der Ton liegt im Verlauf, im Rand
|
||
und im Ring -- nie als leuchtende Flaeche hinter Text. Das Rot des
|
||
Live-Zustands ist ein gedecktes Korallrot (#e8736b), kein
|
||
Signalrot: Auf einem dunklen Bildschirm ist reines Rot nach zwei
|
||
Minuten unangenehm, und diese Seite soll man waehrend des Streams
|
||
offen lassen koennen.
|
||
|
||
`prefers-reduced-motion` schaltet jede Bewegung ab. Der Zustand
|
||
bleibt trotzdem erkennbar -- er steht in Farbe UND in Worten, nicht
|
||
nur im Puls. Eine Auskunft, die ausschliesslich an einer Animation
|
||
haengt, kommt bei einem Teil der Leute gar nicht an.
|
||
===================================================================== */
|
||
|
||
.puls {
|
||
position: relative;
|
||
isolation: isolate;
|
||
overflow: hidden;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1.25rem;
|
||
margin: 1.5rem 0 0;
|
||
padding: 1.35rem 1.4rem;
|
||
border-radius: 1.1rem;
|
||
border: 1px solid color-mix(in srgb, var(--puls-ton) 42%, transparent);
|
||
background:
|
||
radial-gradient(120% 95% at 6% -5%,
|
||
color-mix(in srgb, var(--puls-ton) 26%, transparent), transparent 58%),
|
||
radial-gradient(90% 80% at 105% 110%,
|
||
color-mix(in srgb, var(--puls-ton) 15%, transparent), transparent 62%),
|
||
linear-gradient(158deg,
|
||
color-mix(in srgb, var(--puls-ton) 13%, #0c1420),
|
||
#090f1a 72%);
|
||
box-shadow:
|
||
0 18px 38px -20px color-mix(in srgb, var(--puls-ton) 85%, transparent),
|
||
inset 0 1px 0 color-mix(in srgb, var(--puls-ton) 22%, transparent);
|
||
transition: border-color .4s ease, box-shadow .4s ease;
|
||
}
|
||
|
||
/* DIE VIER LAGEN. Jede hat ihren Ton, und der Ton ist nie die einzige
|
||
Auskunft -- daneben steht immer ein Satz. Wer Farben nicht
|
||
unterscheidet, liest trotzdem, was los ist. */
|
||
.puls { --puls-ton: #7ec8f2; } /* laedt / wartet */
|
||
.puls[data-lage="live"] { --puls-ton: #e8736b; } /* er sendet */
|
||
.puls[data-lage="unklar"] { --puls-ton: #d9a441; } /* wir wissen es nicht */
|
||
|
||
/* Die Aura liegt UNTER allem und traegt die einzige Bewegung der
|
||
Seite. Sie ist nur im Live-Zustand sichtbar. */
|
||
.puls__aura {
|
||
position: absolute;
|
||
inset: -40%;
|
||
z-index: 0;
|
||
pointer-events: none;
|
||
opacity: 0;
|
||
background: radial-gradient(closest-side,
|
||
color-mix(in srgb, var(--puls-ton) 30%, transparent), transparent 70%);
|
||
transition: opacity .6s ease;
|
||
}
|
||
.puls[data-lage="live"] .puls__aura {
|
||
opacity: 1;
|
||
animation: puls-atmen 3.4s ease-in-out infinite;
|
||
}
|
||
|
||
/* 3,4 SEKUNDEN, NICHT EINE. Ein schneller Puls wirkt wie eine Warnung
|
||
und macht nervoes; dieser hier atmet ungefaehr im Ruhepuls eines
|
||
Menschen. Die Deckkraft schwankt zwischen 55 und 100 Prozent --
|
||
sichtbar, aber nicht blinkend. */
|
||
@keyframes puls-atmen {
|
||
0%, 100% { opacity: .55; transform: scale(1); }
|
||
50% { opacity: 1; transform: scale(1.06); }
|
||
}
|
||
|
||
.puls__ring-feld {
|
||
position: relative;
|
||
z-index: 1;
|
||
flex: 0 0 auto;
|
||
width: 5.6rem;
|
||
height: 5.6rem;
|
||
display: grid;
|
||
place-items: center;
|
||
}
|
||
|
||
.puls__ring {
|
||
width: 100%;
|
||
height: 100%;
|
||
/* Oben anfangen statt rechts -- so liest sich der Bogen wie eine
|
||
Uhr und nicht wie ein Tachometer. */
|
||
transform: rotate(-90deg);
|
||
}
|
||
.puls__ring-bahn {
|
||
fill: none;
|
||
stroke: color-mix(in srgb, var(--puls-ton) 18%, transparent);
|
||
stroke-width: 6;
|
||
}
|
||
.puls__ring-fortschritt {
|
||
fill: none;
|
||
stroke: var(--puls-ton);
|
||
stroke-width: 6;
|
||
stroke-linecap: round;
|
||
/* Der Umfang wird im Skript gesetzt -- aus dem Radius gerechnet,
|
||
damit eine Aenderung am SVG nicht hier nachgezogen werden muss. */
|
||
transition: stroke-dashoffset .6s ease;
|
||
}
|
||
|
||
.puls__punkt {
|
||
position: absolute;
|
||
width: .85rem;
|
||
height: .85rem;
|
||
border-radius: 50%;
|
||
background: var(--puls-ton);
|
||
opacity: .9;
|
||
}
|
||
.puls[data-lage="live"] .puls__punkt {
|
||
animation: puls-punkt 3.4s ease-in-out infinite;
|
||
}
|
||
@keyframes puls-punkt {
|
||
0%, 100% { transform: scale(1); opacity: .75; }
|
||
50% { transform: scale(1.35); opacity: 1; }
|
||
}
|
||
|
||
.puls__text {
|
||
position: relative;
|
||
z-index: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: .2rem;
|
||
}
|
||
|
||
.puls__marke {
|
||
margin: 0;
|
||
font-size: .78rem;
|
||
letter-spacing: .08em;
|
||
text-transform: uppercase;
|
||
color: color-mix(in srgb, var(--puls-ton) 70%, #cfe0f0);
|
||
}
|
||
|
||
.puls__lage {
|
||
margin: 0;
|
||
font-size: 1.05rem;
|
||
font-weight: 600;
|
||
color: #eaf3fb;
|
||
}
|
||
|
||
/* DIE ZAHL IST DAS GROESSTE AUF DER SEITE -- sie ist der Grund, warum
|
||
man wiederkommt. `tabular-nums` verhindert das Zappeln, das sonst
|
||
jede Sekunde entsteht, wenn eine 1 schmaler ist als eine 8. */
|
||
.puls__zahl {
|
||
margin: .1rem 0 0;
|
||
font-size: clamp(1.8rem, 6vw, 2.6rem);
|
||
font-weight: 700;
|
||
line-height: 1.1;
|
||
font-variant-numeric: tabular-nums;
|
||
letter-spacing: .01em;
|
||
color: #f4f9ff;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
/* Im Live-Zustand steht dort der Titel des Streams, kein Countdown --
|
||
ein Satz in Countdown-Groesse wuerde umbrechen und die Karte
|
||
aufreissen. */
|
||
.puls[data-lage="live"] .puls__zahl {
|
||
font-size: clamp(1.1rem, 3.4vw, 1.45rem);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.puls__klein {
|
||
margin: .25rem 0 0;
|
||
font-size: .82rem;
|
||
line-height: 1.45;
|
||
color: #9fb4c8;
|
||
}
|
||
|
||
.puls__knopf {
|
||
position: relative;
|
||
z-index: 1;
|
||
/* Rechts anschlagen, solange die Karte eine Zeile ist. Bricht sie um,
|
||
hebt `margin-left: 0` unten die Regel wieder auf. */
|
||
margin-left: auto;
|
||
flex: 0 0 auto;
|
||
/* 44 px ist die Grenze im Haus -- am Daumen, nicht an der Maus
|
||
gemessen. */
|
||
min-height: 44px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: .55rem 1.05rem;
|
||
border-radius: .7rem;
|
||
border: 1px solid color-mix(in srgb, var(--puls-ton) 55%, transparent);
|
||
background: color-mix(in srgb, var(--puls-ton) 18%, transparent);
|
||
color: #f4f9ff;
|
||
font-weight: 600;
|
||
text-decoration: none;
|
||
transition: background .2s ease, border-color .2s ease, transform .2s ease;
|
||
}
|
||
.puls__knopf:hover {
|
||
background: color-mix(in srgb, var(--puls-ton) 28%, transparent);
|
||
border-color: color-mix(in srgb, var(--puls-ton) 75%, transparent);
|
||
}
|
||
.puls__knopf:focus-visible {
|
||
outline: 2px solid var(--puls-ton);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* AM HANDY UNTEREINANDER. Keine Schwelle in Pixeln, sondern die
|
||
Frage, ob der Text neben dem Ring noch lesbar ist: Unter 34rem
|
||
bleiben fuer ihn weniger als 20 Zeichen je Zeile. */
|
||
@media (max-width: 34rem) {
|
||
.puls { flex-direction: column; align-items: flex-start; gap: .9rem; }
|
||
.puls__ring-feld { width: 4.6rem; height: 4.6rem; }
|
||
/* In einer Spalte gibt es kein "rechts" -- der Knopf stellt sich
|
||
unter den Text und nimmt die volle Breite, damit er am Daumen
|
||
nicht gesucht werden muss. */
|
||
.puls__knopf { margin-left: 0; width: 100%; justify-content: center; }
|
||
}
|
||
|
||
/* Der Textblock nimmt den Platz, der zwischen Ring und Knopf uebrig
|
||
bleibt -- sonst draengt sich alles links zusammen. */
|
||
.puls__text { flex: 1 1 auto; }
|
||
|
||
/* ---- DAS BESONDERE EVENT ------------------------------------------- */
|
||
|
||
.event {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
margin: .9rem 0 0;
|
||
padding: 1rem 1.2rem;
|
||
border-radius: 1rem;
|
||
--event-ton: #d49ae8;
|
||
border: 1px solid color-mix(in srgb, var(--event-ton) 45%, transparent);
|
||
background: linear-gradient(152deg,
|
||
color-mix(in srgb, var(--event-ton) 16%, #0c1420), #090f1a 78%);
|
||
}
|
||
.event[hidden] { display: none; }
|
||
|
||
.event__zeichen {
|
||
flex: 0 0 auto;
|
||
font-size: 1.5rem;
|
||
color: var(--event-ton);
|
||
line-height: 1;
|
||
}
|
||
.event__text { min-width: 0; }
|
||
.event__marke {
|
||
margin: 0;
|
||
font-size: .74rem;
|
||
letter-spacing: .08em;
|
||
text-transform: uppercase;
|
||
color: color-mix(in srgb, var(--event-ton) 75%, #cfe0f0);
|
||
}
|
||
.event__titel {
|
||
margin: .15rem 0 0;
|
||
font-size: 1.05rem;
|
||
font-weight: 600;
|
||
color: #eaf3fb;
|
||
}
|
||
.event__zahl {
|
||
margin: .2rem 0 0;
|
||
font-variant-numeric: tabular-nums;
|
||
font-size: .92rem;
|
||
color: #b9c9da;
|
||
}
|
||
.event[data-jetzt="ja"] .event__zahl {
|
||
color: #f4f9ff;
|
||
font-weight: 700;
|
||
}
|
||
.event__knopf {
|
||
margin-left: auto;
|
||
min-height: 44px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: .5rem 1rem;
|
||
border-radius: .7rem;
|
||
border: 1px solid color-mix(in srgb, var(--event-ton) 55%, transparent);
|
||
background: color-mix(in srgb, var(--event-ton) 16%, transparent);
|
||
color: #f4f9ff;
|
||
text-decoration: none;
|
||
font-weight: 600;
|
||
}
|
||
.event__knopf:focus-visible { outline: 2px solid var(--event-ton); outline-offset: 2px; }
|
||
|
||
@media (max-width: 34rem) {
|
||
.event { flex-wrap: wrap; }
|
||
.event__knopf { margin-left: 0; width: 100%; justify-content: center; }
|
||
}
|
||
|
||
/* ---- DIE KANAELE ---------------------------------------------------- */
|
||
|
||
.kanaele { margin: 1.6rem 0 0; }
|
||
.kanaele[hidden] { display: none; }
|
||
|
||
.kanaele__titel,
|
||
.seiten-titel {
|
||
margin: 0 0 .7rem;
|
||
font-size: 1.05rem;
|
||
font-weight: 600;
|
||
color: #cfe0f0;
|
||
}
|
||
.seiten-titel { margin-top: 1.8rem; }
|
||
|
||
.kanaele__reihe {
|
||
display: grid;
|
||
gap: .8rem;
|
||
/* Dieselbe Regel wie beim Kartengitter darunter: eine Regel statt
|
||
einer Zahl. Ein vierter Kanal ordnet sich von selbst ein. */
|
||
grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
|
||
}
|
||
|
||
.kanal {
|
||
--kanal-ton: #7ec8f2;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: .15rem;
|
||
padding: .85rem 1rem;
|
||
border-radius: .9rem;
|
||
border: 1px solid color-mix(in srgb, var(--kanal-ton) 30%, transparent);
|
||
background: linear-gradient(150deg,
|
||
color-mix(in srgb, var(--kanal-ton) 11%, #0c1420), #090f1a 80%);
|
||
text-decoration: none;
|
||
transition: transform .2s ease, border-color .2s ease, background .2s ease;
|
||
}
|
||
.kanal:hover {
|
||
transform: translateY(-2px);
|
||
border-color: color-mix(in srgb, var(--kanal-ton) 58%, transparent);
|
||
}
|
||
.kanal:focus-visible { outline: 2px solid var(--kanal-ton); outline-offset: 2px; }
|
||
|
||
.kanal__name { font-weight: 600; color: #eaf3fb; }
|
||
.kanal__handle {
|
||
font-size: .85rem;
|
||
color: color-mix(in srgb, var(--kanal-ton) 78%, #cfe0f0);
|
||
overflow-wrap: anywhere;
|
||
}
|
||
.kanal__satz { margin-top: .2rem; font-size: .84rem; line-height: 1.45; color: #9fb4c8; }
|
||
|
||
.kanaele__fehler {
|
||
margin: 0;
|
||
padding: .8rem 1rem;
|
||
border-radius: .8rem;
|
||
border: 1px solid rgba(217, 164, 65, .35);
|
||
background: rgba(217, 164, 65, .08);
|
||
font-size: .88rem;
|
||
line-height: 1.5;
|
||
color: #d8c49a;
|
||
}
|
||
.kanaele__fehler[hidden] { display: none; }
|
||
|
||
/* ---- WER BEWEGUNG NICHT MOECHTE ------------------------------------- */
|
||
|
||
/* Der Zustand bleibt vollstaendig lesbar: Farbe, Ring und Text stehen
|
||
weiter da. Es hoert nur das Atmen auf. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.puls[data-lage="live"] .puls__aura,
|
||
.puls[data-lage="live"] .puls__punkt { animation: none; }
|
||
.puls__ring-fortschritt { transition: none; }
|
||
.kanal { transition: none; }
|
||
.kanal:hover { transform: none; }
|
||
}
|
||
|
||
/* =====================================================================
|
||
WENN ER SENDET, SIEHT MAN ES AUF DER GANZEN SEITE (22.09.2026)
|
||
=====================================================================
|
||
Filipe: „dan auf screen 7 soll die kachel da auch für die gleiche
|
||
zeit richtig geil aufblinken, mit einer übertrieben geilen farbe
|
||
damit die leute wissen dass sie darüber sofort drauf kommen. und
|
||
das auf screen8 auch."
|
||
|
||
ÜBERTRIEBEN GEIL — OHNE IN DEN AUGEN WEHZUTUN.
|
||
Die Hausregel vom 12.08.2026 verbietet Strobe. Das ist hier kein
|
||
Widerspruch, sondern dieselbe Sache: Ein Rot, das im Sekundentakt
|
||
an- und ausgeht, liest man als „Alarm, aber ich sehe weg". Ein Rot,
|
||
das atmet und dabei einen Saum nach außen schickt, liest man als
|
||
„jetzt gerade" — und schaut hin. Der Unterschied ist der Rhythmus,
|
||
nicht die Lautstärke.
|
||
|
||
Dieselben 1,4 Sekunden wie der Punkt in `module.css`. Zwei
|
||
Rhythmen nebeneinander wären Unruhe; einer ist ein Herzschlag.
|
||
|
||
NUR DIE, ÜBER DIE MAN HINKOMMT. Es blinkt nicht die ganze Seite,
|
||
sondern die Wege, die zum Stream führen — die Kanalkarten und die
|
||
Pulskarte. Blinkt alles, blinkt nichts.
|
||
--------------------------------------------------------------------- */
|
||
body[data-live="ja"] .kanal {
|
||
--kanal-ton: #ff1a1a;
|
||
border-color: #ff1a1a;
|
||
animation: drauss-live 1.4s ease-in-out infinite;
|
||
}
|
||
|
||
/* Die drei Karten atmen leicht versetzt -- dadurch wandert der Blick
|
||
von einer zur nächsten, statt auf einer zu kleben. */
|
||
body[data-live="ja"] .kanal:nth-child(2) { animation-delay: .18s; }
|
||
body[data-live="ja"] .kanal:nth-child(3) { animation-delay: .36s; }
|
||
|
||
@keyframes drauss-live {
|
||
0%, 100% { box-shadow: 0 0 0 0 rgba(255, 26, 26, .55); }
|
||
60% { box-shadow: 0 0 0 9px rgba(255, 26, 26, 0); }
|
||
}
|
||
|
||
/* screen8 -- die Pulskarte. Sie hat schon einen Zustand `lage="live"`;
|
||
was fehlte, war der Saum, der sie aus dem Augenwinkel holt. */
|
||
.puls[data-lage="live"] {
|
||
animation: drauss-live 1.4s ease-in-out infinite;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
body[data-live="ja"] .kanal,
|
||
.puls[data-lage="live"] {
|
||
animation: none;
|
||
box-shadow: 0 0 0 3px rgba(255, 26, 26, .5);
|
||
}
|
||
}
|
||
|
||
@media (forced-colors: active) {
|
||
body[data-live="ja"] .kanal,
|
||
.puls[data-lage="live"] {
|
||
animation: none;
|
||
border: 2px solid CanvasText;
|
||
}
|
||
}
|