Wunsch Filipe: "ich will dass du diese seite viel krasser und detaillierter machst, ich will dass du dich informierst und alles reinsetzt was wir noch gebrauchen koennten." NICHT ALLES, SONDERN WAS TAEGLICH FEHLT. Der Chat konnte schon Raeume, Verlauf, Gelesen-Stand, Live-Zustellung, Gruppen und Zuruecknehmen. Drei Dinge fehlten, und jedes davon kostet ohne es echte Zeit: 1. SUCHE IN DEN NACHRICHTEN. Ein Chat ohne sie ist ab dem zweiten Monat ein Archiv, in dem man nichts findet. Ein Suchfeld gab es -- es durchsuchte aber nur die NAMEN der Gespraeche, also die kleinere Haelfte. Jetzt durchsucht dasselbe Feld beides und zeigt die Fundstellen UNTER der Gespraechsliste: Wer "Patrick" eingibt, will vielleicht das Gespraech und vielleicht die Nachricht -- ein Umschalter haette ihn zwingen wollen, das vorher zu wissen. 2. ANTWORTEN MIT ZITAT. Zu zweit weiss man meistens, worauf sich etwas bezieht. In einer Gruppe laufen drei Faeden parallel, und "ja, mach das" kann alles heissen. Das Zitat steht IN der Blase (es gehoert zur Antwort, nicht darueber) und fuehrt per Klick zur Stelle. 3. DIE LINIE "AB HIER NEU". Wer nach zwei Tagen zurueckkommt, sucht sonst die Stelle, an der er aufgehoert hat, indem er Uhrzeiten liest. BEWUSST NICHT GEBAUT: Anhaenge (dafuer gibt es den Dateien-Bereich mit Rechten und Ablauf), Reaktionen (eine vierte Sache, bevor die drei sich bewaehrt haben) und Tipp-Anzeigen (dauernder Verkehr fuer eine Auskunft, die man in zwei Sekunden ohnehin sieht). DIE SICHERHEIT DER SUCHE STEHT IM JOIN, nicht in einer nachtraeglichen Pruefung: `chat_teilnehmer` wird mit der eigenen Personenkennung verbunden, und was dort nicht drinsteht, kommt gar nicht erst aus der Datenbank. Ein Filter, der erst hinterher aussortiert, ist eine Zeile davon entfernt, vergessen zu werden. Ebenso beim Zitat: Worauf geantwortet wird, muss im SELBEN Raum liegen -- sonst koennte jemand die Kennung aus einem fremden Gespraech mitschicken, und beim Empfaenger stuende ein Zitat aus einem Raum, den er nie gesehen hat. DREI FEHLER, DIE DER DURCHLAUF GEFUNDEN HAT: 1. `ESCAPE '\'` IN EINEM TEMPLATE-LITERAL. Dort ist `\'` eine Fluchtsequenz fuer das Anfuehrungszeichen -- SQLite bekam ein LEERES Fluchtzeichen und antwortete "ESCAPE expression must be a single character". Die Suche war damit komplett tot. Kein Syntaxfehler, kein Warnhinweis: Erst der Aufruf mit echten Daten hat es gezeigt. 2. DIE MASKIERUNG KANNTE ZWEI VON DREI ZEICHEN. `%` und `_` waren dabei, der Backslash nicht -- ausgerechnet das Fluchtzeichen selbst. Geprueft wird das jetzt an der ZEILE AUS DER DATEI, nicht an einem Nachbau: Mein erster Test hat die Maskierung nachgebaut und dabei die Shell-Maskierung mitgeschleppt -- er meldete einen Fehler, den nur er hatte. 3. DIE UNGELESEN-LINIE SCHIEN NICHT ZU FUNKTIONIEREN. Sie tat es -- mein Testaufbau war falsch: Filipes Seite war noch offen, die neuen Nachrichten kamen ueber den Live-Strom an und wurden sofort als gelesen gemeldet. Es gab schlicht nichts Ungelesenes. Erst als er die Seite verlaesst, bevor Patrick schreibt, steht die Linie da -- und zwar genau vor "Neu von Patrick, eins", und beim zweiten Oeffnen ist sie weg. Der Gelesen-Stand wird deshalb beim OEFFNEN mitgeschickt, bevor er gesetzt wird -- eine Zeile spaeter waere er immer die letzte Nachricht, und die Linie staende nie irgendwo. Ohne Volltextindex, mit Absicht: `LIKE` liest die Tabelle, und bei einem Team dieser Groesse sind das einige tausend Zeilen. Ein FTS5-Index waere eine zweite Tabelle, die synchron gehalten werden muss -- genau daran gehen solche Sachen kaputt. Wenn der Verlauf sechsstellig wird, ist das der Zeitpunkt dafuer, nicht heute. Geprueft: pruef-chat, pruef-chat-optik, pruef-css-klassen, pruef-workspace-seiten, pruef-handy -- alle in Ordnung. Dazu im Browser durchgespielt: Zitat gesetzt und gelesen, Suche nach "Bitrate" (1 Treffer), nach "100 %" (1 Treffer -- die Maskierung haelt), nach Unsinn (0), einbuchstabige Suche (zu kurz), Antwortleiste mit dem richtigen Namen, Ungelesen-Linie an der richtigen Stelle und beim zweiten Oeffnen weg. Co-Authored-By: Claude Opus 5 <[email protected]>
797 lines
30 KiB
CSS
797 lines
30 KiB
CSS
/* =====================================================================
|
|
chat.css — Gespräche im Workspace.
|
|
|
|
Wunsch Filipe, 06.09.2026: *"richtig krass, richtig hochwertig,
|
|
professionell und ultra geil."*
|
|
|
|
WAS DAS HIER HEISST — und was nicht:
|
|
|
|
Nicht mehr Effekte. Ein Chat wird täglich benutzt und oft lange
|
|
angesehen; was beim ersten Blick beeindruckt, stört beim hundertsten.
|
|
Die Wirkung kommt aus vier Dingen:
|
|
|
|
1. EINE FLÄCHE, kein scrollendes Dokument. Die Seite hat die Höhe
|
|
des Fensters, gescrollt wird nur INNERHALB der Spalten. Das
|
|
Eingabefeld steht immer an derselben Stelle -- man muss es nie
|
|
suchen.
|
|
2. RUHE IM VERLAUF. Fast schwarzer Grund, die eigenen Nachrichten
|
|
in einem gedeckten Blau, fremde in einem neutralen Grau. Kein
|
|
Farbverlauf in der Blase; er macht Text unruhig zu lesen.
|
|
3. GRUPPIERUNG. Mehrere Nachrichten derselben Person rücken
|
|
zusammen und wiederholen den Namen nicht. Ein Datumstrenner
|
|
gliedert den Verlauf.
|
|
4. GENUG LUFT. Zeilenhöhe 1.5, Blasen mit ordentlichem Innenabstand.
|
|
Enge sieht auf einem Bildschirmfoto ordentlich aus und liest
|
|
sich schlecht.
|
|
|
|
Augenschonend ist Hausregel: keine harten Weißflächen, keine
|
|
Neonränder, Bewegung nur als kurze Blende.
|
|
===================================================================== */
|
|
|
|
/* ---------- Die Fläche --------------------------------------------------- */
|
|
|
|
.chat-seite {
|
|
/* Der Rest des Hauses scrollt; hier soll die Seite stehen.
|
|
|
|
100dvh statt 100vh: Auf dem Handy ändert sich die sichtbare Höhe,
|
|
wenn die Adresszeile ein- und ausfährt -- mit vh springt das
|
|
Eingabefeld dabei aus dem Bild.
|
|
|
|
--kopf-hoehe WIRD GEMESSEN, nicht geraten (chat.js). Der erste
|
|
Entwurf hatte hier 62 px stehen; am Handy ist die Kopfleiste aber
|
|
zweizeilig und gut doppelt so hoch. Folge: Die Seite war zu lang,
|
|
und das Eingabefeld stand halb unterhalb des Bildschirmrands --
|
|
ausgerechnet das Feld, um das es geht. Der Rückfallwert bleibt
|
|
stehen für den Moment vor der ersten Messung. */
|
|
height: calc(100dvh - var(--kopf-hoehe, 62px));
|
|
display: flex; flex-direction: column;
|
|
padding-bottom: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.chat {
|
|
flex: 1;
|
|
min-height: 0; /* sonst wächst das Raster über die Fläche */
|
|
display: grid;
|
|
grid-template-columns: minmax(240px, 320px) 1fr;
|
|
gap: 0;
|
|
border: 1px solid var(--rand);
|
|
border-radius: var(--radius);
|
|
background:
|
|
radial-gradient(120% 90% at 0% 0%, rgba(255, 255, 255, .03), transparent 55%),
|
|
var(--flaeche);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.chat__spalte {
|
|
display: flex; flex-direction: column;
|
|
min-height: 0; min-width: 0;
|
|
}
|
|
/* Der Trenner zwischen Liste und Verlauf. Läuft oben und unten aus --
|
|
eine durchgehende Linie zerschneidet die Kachel in zwei. */
|
|
.chat__spalte--verlauf { position: relative; }
|
|
.chat__spalte--verlauf::before {
|
|
content: ""; position: absolute; left: 0; top: 12px; bottom: 12px; width: 1px;
|
|
background: linear-gradient(180deg,
|
|
transparent, var(--rand-hell) 15%, var(--rand-hell) 85%, transparent);
|
|
}
|
|
|
|
.chat__kopf {
|
|
display: flex; align-items: center; gap: 12px;
|
|
padding: 15px 17px;
|
|
border-bottom: 1px solid var(--rand);
|
|
flex: none;
|
|
}
|
|
.chat__kopf-text { min-width: 0; }
|
|
.chat__titel {
|
|
margin: 0;
|
|
font-size: 1.02rem; font-weight: 650; color: var(--text);
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
.chat__unter {
|
|
margin: 2px 0 0;
|
|
font-size: .78rem; color: var(--text-still);
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
.chat__kopf .knopf { margin-left: auto; flex: none; }
|
|
|
|
/* Der Zurück-Pfeil gehört dem Handy. Am Rechner stehen beide Spalten
|
|
nebeneinander, da gibt es nichts, wohin man zurückkönnte. */
|
|
.chat__zurueck {
|
|
display: none;
|
|
flex: none;
|
|
width: 34px; height: 34px;
|
|
align-items: center; justify-content: center;
|
|
border: 1px solid var(--rand); border-radius: 10px;
|
|
background: rgba(255, 255, 255, .04);
|
|
color: var(--text-leise); cursor: pointer;
|
|
}
|
|
.chat__zurueck svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; }
|
|
.chat__zurueck:hover { color: var(--text); border-color: var(--rand-hell); }
|
|
|
|
.chat__suche { padding: 10px 14px 4px; flex: none; }
|
|
.chat__suche input { width: 100%; }
|
|
|
|
.chat__leer {
|
|
margin: 0; padding: 20px 18px;
|
|
font-size: .86rem; color: var(--text-still); line-height: 1.55;
|
|
}
|
|
|
|
/* ---------- Links: die Gespräche ----------------------------------------- */
|
|
|
|
.chat__liste {
|
|
flex: 1; min-height: 0;
|
|
overflow-y: auto;
|
|
margin: 0; padding: 8px; list-style: none;
|
|
display: grid; gap: 3px; align-content: start;
|
|
}
|
|
|
|
.chat-raum__knopf {
|
|
--r: var(--akzent);
|
|
width: 100%;
|
|
display: grid; grid-template-columns: auto 1fr auto;
|
|
align-items: center; gap: 11px;
|
|
padding: 10px 11px;
|
|
border: 1px solid transparent; border-radius: 12px;
|
|
background: transparent;
|
|
color: inherit; font: inherit; text-align: left; cursor: pointer;
|
|
transition: background .16s ease, border-color .16s ease;
|
|
}
|
|
.chat-raum__knopf:hover { background: rgba(255, 255, 255, .04); }
|
|
.chat-raum__knopf:focus-visible { outline: 2px solid var(--akzent); outline-offset: -2px; }
|
|
|
|
/* Das offene Gespräch bleibt sichtbar markiert -- sonst weiß man nach
|
|
dem Scrollen nicht mehr, wo man ist. */
|
|
.chat-raum[data-offen="ja"] .chat-raum__knopf {
|
|
background: color-mix(in srgb, var(--akzent) 11%, transparent);
|
|
border-color: color-mix(in srgb, var(--akzent) 32%, transparent);
|
|
}
|
|
|
|
.chat-raum__zeichen {
|
|
flex: none;
|
|
width: 38px; height: 38px; border-radius: 50%;
|
|
display: grid; place-items: center;
|
|
font-size: .82rem; font-weight: 700;
|
|
color: var(--r);
|
|
background: color-mix(in srgb, var(--r) 13%, transparent);
|
|
border: 1px solid color-mix(in srgb, var(--r) 30%, transparent);
|
|
}
|
|
/* Dieselben Rollenfarben wie überall im Haus. */
|
|
.chat-raum__zeichen[data-rolle="admin"] { --r: var(--dogi-haupt); }
|
|
.chat-raum__zeichen[data-rolle="manager"] { --r: var(--manager-haupt); }
|
|
.chat-raum__zeichen[data-rolle="scout"] { --r: var(--scout-haupt); }
|
|
.chat-raum__zeichen[data-rolle="creator"] { --r: var(--creator-haupt); }
|
|
/* Eine Gruppe zeigt ihre Größe statt einer Initiale -- der Name der
|
|
Gruppe steht ohnehin daneben. */
|
|
.chat-raum__zeichen[data-gruppe="ja"] {
|
|
--r: #a0aec4;
|
|
border-radius: 12px;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.chat-raum__mitte { min-width: 0; }
|
|
.chat-raum__oben { display: flex; align-items: baseline; gap: 8px; }
|
|
.chat-raum__name {
|
|
flex: 1; min-width: 0;
|
|
font-size: .89rem; font-weight: 600; color: var(--text-leise);
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
.chat-raum__zeit {
|
|
flex: none;
|
|
font-size: .75rem; color: var(--text-still);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.chat-raum__vorschau {
|
|
display: block; margin-top: 2px;
|
|
font-size: .78rem; color: var(--text-still);
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
.chat-raum__vorschau[data-weg="ja"] { font-style: italic; opacity: .8; }
|
|
|
|
/* Ungelesen: die Zahl UND ein kräftigerer Name. Nur eine Zahl übersieht
|
|
man beim Überfliegen. */
|
|
.chat-raum[data-neu="ja"] .chat-raum__name { color: var(--text); font-weight: 700; }
|
|
.chat-raum[data-neu="ja"] .chat-raum__vorschau { color: var(--text-leise); }
|
|
.chat-raum__zahl {
|
|
flex: none;
|
|
min-width: 21px; height: 21px; padding: 0 6px;
|
|
display: grid; place-items: center;
|
|
border-radius: 999px;
|
|
font-size: .72rem; font-weight: 700;
|
|
font-variant-numeric: tabular-nums;
|
|
color: #0b0f16;
|
|
background: var(--akzent);
|
|
}
|
|
|
|
/* ---------- Rechts: der Verlauf ------------------------------------------ */
|
|
|
|
.chat__verlauf {
|
|
flex: 1; min-height: 0;
|
|
overflow-y: auto;
|
|
padding: 18px 18px 8px;
|
|
display: flex; flex-direction: column; gap: 3px;
|
|
/* Ein weicher Schleier am oberen Rand: Der Verlauf scrollt unter den
|
|
Kopf, statt an einer harten Kante abgeschnitten zu wirken. */
|
|
mask-image: linear-gradient(180deg, transparent, #000 14px);
|
|
}
|
|
|
|
/* KURZE GESPRÄCHE LIEGEN UNTEN AN.
|
|
|
|
Bei drei Nachrichten standen sie oben, und darunter klaffte der halbe
|
|
Bildschirm -- man sucht die neueste dann am falschen Ende. Jeder
|
|
Messenger legt sie ans untere Ende, direkt über das Eingabefeld, und
|
|
das ist kein Geschmack: Dort schaut man hin, wenn man antworten will.
|
|
|
|
`margin-top: auto` auf dem ERSTEN Kind statt `justify-content:
|
|
flex-end` am Behälter. Der naheliegende Weg hat einen bekannten
|
|
Haken: In einem scrollbaren Flex-Behälter wird der obere Teil damit
|
|
unerreichbar, sobald der Inhalt überläuft -- man käme an die ältesten
|
|
Nachrichten nicht mehr heran. */
|
|
.chat__verlauf > :first-child { margin-top: auto; }
|
|
|
|
/* Der Datumstrenner. */
|
|
.chat-tag {
|
|
display: flex; align-items: center; gap: 10px;
|
|
margin: 14px 0 10px;
|
|
color: var(--text-still);
|
|
}
|
|
.chat-tag::before, .chat-tag::after {
|
|
content: ""; flex: 1; height: 1px;
|
|
background: linear-gradient(90deg, transparent, var(--rand-hell), transparent);
|
|
}
|
|
.chat-tag__text {
|
|
flex: none;
|
|
font-size: .72rem; font-weight: 700;
|
|
letter-spacing: .07em; text-transform: uppercase;
|
|
}
|
|
|
|
.chat-nachricht {
|
|
display: grid;
|
|
grid-template-columns: 30px 1fr;
|
|
gap: 9px;
|
|
align-items: flex-end;
|
|
max-width: 76%;
|
|
margin-top: 9px;
|
|
}
|
|
/* Folgt eine Nachricht derselben Person, rückt sie näher und lässt den
|
|
Kreis weg -- der Platz bleibt frei, damit die Blasen fluchten. */
|
|
.chat-nachricht[data-folge="ja"] { margin-top: 2px; }
|
|
.chat-nachricht[data-selbst="ja"] {
|
|
grid-template-columns: 1fr;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.chat-nachricht__zeichen {
|
|
--r: #a0aec4;
|
|
grid-column: 1;
|
|
width: 30px; height: 30px; border-radius: 50%;
|
|
display: grid; place-items: center;
|
|
font-size: .75rem; font-weight: 700;
|
|
color: var(--r);
|
|
background: color-mix(in srgb, var(--r) 13%, transparent);
|
|
border: 1px solid color-mix(in srgb, var(--r) 28%, transparent);
|
|
}
|
|
.chat-nachricht__zeichen[data-rolle="admin"] { --r: var(--dogi-haupt); }
|
|
.chat-nachricht__zeichen[data-rolle="manager"] { --r: var(--manager-haupt); }
|
|
.chat-nachricht__zeichen[data-rolle="scout"] { --r: var(--scout-haupt); }
|
|
.chat-nachricht__zeichen[data-rolle="creator"] { --r: var(--creator-haupt); }
|
|
/* Ohne Kreis (Folgenachricht) bleibt die Spalte trotzdem stehen. */
|
|
.chat-nachricht[data-folge="ja"]:not([data-selbst="ja"]) .chat-nachricht__blase { grid-column: 2; }
|
|
|
|
.chat-nachricht__blase {
|
|
grid-column: 2;
|
|
padding: 10px 13px 7px;
|
|
border: 1px solid var(--rand);
|
|
border-radius: 14px 14px 14px 4px;
|
|
background: rgba(255, 255, 255, .045);
|
|
min-width: 0;
|
|
}
|
|
.chat-nachricht[data-folge="ja"] .chat-nachricht__blase { border-radius: 14px 14px 14px 8px; }
|
|
|
|
/* Die eigenen: gedecktes Blau, Ecke rechts. Kein Verlauf in der Fläche
|
|
-- Text auf einem Farbverlauf liest sich unruhig. */
|
|
.chat-nachricht[data-selbst="ja"] .chat-nachricht__blase {
|
|
grid-column: 1;
|
|
border-radius: 14px 14px 4px 14px;
|
|
background: color-mix(in srgb, var(--akzent) 16%, transparent);
|
|
border-color: color-mix(in srgb, var(--akzent) 30%, transparent);
|
|
}
|
|
.chat-nachricht[data-selbst="ja"][data-folge="ja"] .chat-nachricht__blase {
|
|
border-radius: 14px 14px 8px 14px;
|
|
}
|
|
|
|
.chat-nachricht__von {
|
|
margin: 0 0 3px;
|
|
font-size: .74rem; font-weight: 700;
|
|
color: color-mix(in srgb, var(--text-leise) 85%, transparent);
|
|
}
|
|
.chat-nachricht__text {
|
|
margin: 0;
|
|
font-size: .92rem; color: var(--text);
|
|
/* pre-wrap: Zeilenumbrüche aus der Eingabe bleiben erhalten, ohne
|
|
dass dafür HTML erzeugt werden müsste (das wäre der Weg, über den
|
|
jemand Auszeichnung einschleust). */
|
|
white-space: pre-wrap;
|
|
overflow-wrap: anywhere; /* eine lange Adresse sprengt nichts */
|
|
line-height: 1.5;
|
|
}
|
|
.chat-nachricht__weg { color: var(--text-still); font-style: italic; }
|
|
.chat-nachricht[data-weg="ja"] .chat-nachricht__blase {
|
|
background: transparent;
|
|
border-style: dashed;
|
|
}
|
|
|
|
.chat-nachricht__fuss {
|
|
display: flex; align-items: center; gap: 9px;
|
|
margin-top: 4px;
|
|
}
|
|
.chat-nachricht__zeit {
|
|
font-size: .75rem; color: var(--text-still);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
/* Der Zurücknehmen-Knopf erscheint erst beim Darüberfahren -- er ist
|
|
die seltene Handlung und soll nicht in jeder Zeile mitlaufen. Auf
|
|
Geräten ohne Zeiger steht er immer da (siehe unten). */
|
|
.chat-nachricht__weg-knopf {
|
|
padding: 0; border: 0; background: none;
|
|
font: inherit; font-size: .75rem;
|
|
color: var(--text-still); cursor: pointer;
|
|
opacity: 0; transition: opacity .16s ease, color .16s ease;
|
|
}
|
|
.chat-nachricht:hover .chat-nachricht__weg-knopf,
|
|
.chat-nachricht__weg-knopf:focus-visible { opacity: 1; }
|
|
.chat-nachricht__weg-knopf:hover { color: var(--warn); }
|
|
|
|
/* Unterwegs und gescheitert -- der Unterschied muss sichtbar sein. */
|
|
.chat-nachricht__blase:has(+ *) { /* Platzhalter, damit :has unterstützt bleibt */ }
|
|
|
|
/* ---------- Das Eingabefeld ---------------------------------------------- */
|
|
|
|
.chat__eingabe {
|
|
flex: none;
|
|
display: flex; align-items: flex-end; gap: 10px;
|
|
padding: 12px 14px 14px;
|
|
border-top: 1px solid var(--rand);
|
|
background: rgba(0, 0, 0, .2);
|
|
}
|
|
.chat__eingabe-schild {
|
|
position: absolute; width: 1px; height: 1px; margin: -1px;
|
|
overflow: hidden; clip-path: inset(50%); white-space: nowrap;
|
|
}
|
|
.chat__eingabe textarea {
|
|
flex: 1; min-width: 0;
|
|
resize: none;
|
|
min-height: 42px; max-height: 160px;
|
|
line-height: 1.5;
|
|
}
|
|
.chat__senden {
|
|
flex: none;
|
|
width: 42px; height: 42px;
|
|
display: grid; place-items: center;
|
|
border: 1px solid color-mix(in srgb, var(--akzent) 40%, transparent);
|
|
border-radius: 12px;
|
|
background: color-mix(in srgb, var(--akzent) 18%, transparent);
|
|
color: color-mix(in srgb, var(--akzent) 55%, #ffffff);
|
|
cursor: pointer;
|
|
transition: background .16s ease, color .16s ease;
|
|
}
|
|
.chat__senden svg { width: 20px; height: 20px; fill: currentColor; }
|
|
.chat__senden:hover {
|
|
background: color-mix(in srgb, var(--akzent) 28%, transparent);
|
|
color: #fff;
|
|
}
|
|
.chat__senden:focus-visible { outline: 2px solid var(--akzent); outline-offset: 2px; }
|
|
|
|
/* ---------- Die Wahl im Neu-Dialog --------------------------------------- */
|
|
|
|
.chat-wahl { margin: 4px 0 6px; max-height: 46vh; overflow-y: auto; }
|
|
.chat-wahl__gruppe + .chat-wahl__gruppe { margin-top: 13px; }
|
|
.chat-wahl__titel {
|
|
margin: 0 0 7px;
|
|
display: flex; align-items: center; gap: 7px;
|
|
font-size: .75rem; font-weight: 700;
|
|
letter-spacing: .08em; text-transform: uppercase;
|
|
color: var(--text-still);
|
|
}
|
|
.chat-wahl__punkt {
|
|
width: 7px; height: 7px; border-radius: 50%;
|
|
background: var(--r, var(--text-still)); flex: none;
|
|
}
|
|
.chat-wahl__gruppe[data-rolle="admin"] { --r: var(--dogi-haupt); }
|
|
.chat-wahl__gruppe[data-rolle="manager"] { --r: var(--manager-haupt); }
|
|
.chat-wahl__gruppe[data-rolle="scout"] { --r: var(--scout-haupt); }
|
|
.chat-wahl__gruppe[data-rolle="creator"] { --r: var(--creator-haupt); }
|
|
|
|
.chat-wahl__liste { display: flex; flex-wrap: wrap; gap: 7px; }
|
|
.chat-wahl__knopf {
|
|
--r: rgba(255, 255, 255, .5);
|
|
display: inline-flex; align-items: center; gap: 9px;
|
|
min-height: 40px;
|
|
padding: 5px 14px 5px 6px;
|
|
border: 1px solid var(--rand); border-radius: 999px;
|
|
background: rgba(255, 255, 255, .035);
|
|
color: var(--text-leise); font: inherit; cursor: pointer;
|
|
transition: color .16s ease, border-color .16s ease, background .16s ease;
|
|
}
|
|
.chat-wahl__knopf[data-rolle="admin"] { --r: var(--dogi-haupt); }
|
|
.chat-wahl__knopf[data-rolle="manager"] { --r: var(--manager-haupt); }
|
|
.chat-wahl__knopf[data-rolle="scout"] { --r: var(--scout-haupt); }
|
|
.chat-wahl__knopf[data-rolle="creator"] { --r: var(--creator-haupt); }
|
|
.chat-wahl__knopf:hover { color: var(--text); border-color: rgba(255, 255, 255, .24); }
|
|
.chat-wahl__knopf:focus-visible { outline: 2px solid var(--r); outline-offset: 2px; }
|
|
.chat-wahl__zeichen {
|
|
flex: none;
|
|
width: 28px; height: 28px; border-radius: 50%;
|
|
display: grid; place-items: center;
|
|
font-size: .74rem; font-weight: 700;
|
|
color: var(--r);
|
|
background: color-mix(in srgb, var(--r) 14%, transparent);
|
|
border: 1px solid color-mix(in srgb, var(--r) 34%, transparent);
|
|
}
|
|
.chat-wahl__name { font-size: .86rem; font-weight: 550; }
|
|
.chat-wahl__knopf[data-an="ja"] {
|
|
color: var(--text); font-weight: 600;
|
|
border-color: color-mix(in srgb, var(--r) 55%, transparent);
|
|
background: color-mix(in srgb, var(--r) 13%, transparent);
|
|
}
|
|
.chat-wahl__knopf[data-an="ja"] .chat-wahl__zeichen {
|
|
color: #12151b; background: var(--r); border-color: var(--r);
|
|
}
|
|
|
|
/* ---------- Handy --------------------------------------------------------
|
|
|
|
EINE Spalte, umgeschaltet über data-ansicht. Zwei Spalten auf 390
|
|
Pixeln hiessen zwei unbenutzbare Hälften. */
|
|
@media (max-width: 860px) {
|
|
.chat { grid-template-columns: 1fr; }
|
|
.chat__spalte--verlauf::before { display: none; }
|
|
.chat__zurueck { display: flex; }
|
|
|
|
.chat[data-ansicht="liste"] .chat__spalte--verlauf { display: none; }
|
|
.chat[data-ansicht="verlauf"] .chat__spalte--liste { display: none; }
|
|
|
|
/* Mehr Breite für die Blasen -- auf einem schmalen Schirm sind 76 %
|
|
drei Wörter je Zeile. */
|
|
.chat-nachricht { max-width: 88%; }
|
|
/* Ohne Zeiger gibt es kein "Darüberfahren": Der Knopf steht immer da,
|
|
sonst wäre er auf dem Handy gar nicht erreichbar. */
|
|
.chat-nachricht__weg-knopf { opacity: .75; }
|
|
}
|
|
|
|
@media (hover: none) {
|
|
.chat-nachricht__weg-knopf { opacity: .75; }
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.chat-raum__knopf, .chat__senden, .chat-wahl__knopf,
|
|
.chat-nachricht__weg-knopf { transition: none; }
|
|
}
|
|
|
|
/* Ohne Farbverläufe tragen Kanten und Formen allein. Die eigenen
|
|
Nachrichten sind dann nur noch an der Ecke und der Ausrichtung zu
|
|
erkennen -- deshalb bekommen sie hier einen kräftigeren Rand. */
|
|
@media (forced-colors: active) {
|
|
.chat { border: 1px solid CanvasText; }
|
|
.chat__spalte--verlauf::before { background: CanvasText; }
|
|
.chat-nachricht__blase { border: 1px solid CanvasText; }
|
|
.chat-nachricht[data-selbst="ja"] .chat-nachricht__blase { border-width: 2px; }
|
|
.chat-raum__zeichen, .chat-nachricht__zeichen { border: 1px solid CanvasText; }
|
|
.chat-raum__zahl { border: 1px solid CanvasText; }
|
|
.chat-tag::before, .chat-tag::after { background: CanvasText; }
|
|
.chat__verlauf { mask-image: none; }
|
|
}
|
|
|
|
|
|
/* =====================================================================
|
|
DER CHAT (07.09.2026)
|
|
|
|
Wunsch Filipe: "mach die chat fenster auch viel geiler, viel
|
|
detaillierter und viel perfekter. ich will dass die leute auch bock
|
|
bekommen diesen chat zu benutzen."
|
|
|
|
---------------------------------------------------------------------
|
|
WORAN MAN EINEN GUTEN CHAT ERKENNT
|
|
|
|
Nicht an Effekten. An vier Dingen, die alle mit ORIENTIERUNG zu tun
|
|
haben -- und alle vier fehlten:
|
|
|
|
1. MAN SIEHT, MIT WEM MAN SCHREIBT. Initialen sind ein Platzhalter,
|
|
kein Gesicht. Die Bilder liefert der Server seit heute mit; hier
|
|
werden sie gezeichnet -- rund, mit einem feinen Rand in der Farbe
|
|
der Rolle.
|
|
|
|
2. EINE NACHRICHT HAT EINE RICHTUNG. Eigene Nachrichten kommen von
|
|
rechts und tragen den Hausakzent, fremde von links und bleiben
|
|
neutral. Die Ecke am Anfang einer Folge ist eckig, die übrigen
|
|
rund -- so sieht man, wo ein Gedanke anfängt, ohne den Namen zu
|
|
lesen.
|
|
|
|
3. EIN GESPRÄCH HAT EIN GEWICHT. Ungelesenes ist nicht "auch da",
|
|
sondern das, weshalb man die Seite geöffnet hat: kräftigerer
|
|
Grund, farbige Schiene, die Zahl als Marke.
|
|
|
|
4. DAS EINGABEFELD IST DAS WICHTIGSTE ELEMENT DER SEITE. Es war
|
|
eines von vielen. Jetzt sitzt es in einer eigenen Leiste, die
|
|
sich beim Hineinschreiben hebt.
|
|
|
|
Und was NICHT passiert ist: kein Blinken, kein Springen, keine
|
|
Verläufe im Text. Ein Chat wird stundenlang angesehen -- alles, was
|
|
einmal auffällt, nervt beim zwanzigsten Mal.
|
|
===================================================================== */
|
|
|
|
/* ---------- Das Gesicht ---------------------------------------------- */
|
|
.chat-raum__zeichen,
|
|
.chat-nachricht__zeichen {
|
|
position: relative;
|
|
overflow: hidden;
|
|
background: linear-gradient(150deg,
|
|
color-mix(in srgb, var(--rton, var(--akzent)) 30%, rgba(20, 27, 40, .9)),
|
|
rgba(12, 17, 26, .95));
|
|
box-shadow:
|
|
inset 0 0 0 1px color-mix(in srgb, var(--rton, var(--akzent)) 38%, transparent),
|
|
0 2px 6px -3px rgba(0, 0, 0, .8);
|
|
}
|
|
/* Die Farbe der Rolle -- dieselbe Zuordnung wie überall im Haus. */
|
|
.chat-raum__zeichen[data-rolle="spicy"],
|
|
.chat-nachricht__zeichen[data-rolle="spicy"] { --rton: var(--spicy-haupt); }
|
|
.chat-raum__zeichen[data-rolle="admin"],
|
|
.chat-nachricht__zeichen[data-rolle="admin"] { --rton: var(--dogi-haupt); }
|
|
.chat-raum__zeichen[data-rolle="manager"],
|
|
.chat-nachricht__zeichen[data-rolle="manager"] { --rton: var(--manager-haupt); }
|
|
.chat-raum__zeichen[data-rolle="scout"],
|
|
.chat-nachricht__zeichen[data-rolle="scout"] { --rton: var(--scout-haupt); }
|
|
.chat-raum__zeichen[data-rolle="creator"],
|
|
.chat-nachricht__zeichen[data-rolle="creator"] { --rton: var(--creator-haupt); }
|
|
|
|
/* Das Bild liegt ÜBER dem Buchstaben, nicht an seiner Stelle. Fällt es
|
|
aus, steht der Buchstabe weiterhin da. */
|
|
.chat-raum__zeichen img,
|
|
.chat-nachricht__zeichen img {
|
|
position: absolute; inset: 0;
|
|
width: 100%; height: 100%;
|
|
object-fit: cover;
|
|
border-radius: inherit;
|
|
}
|
|
|
|
/* ---------- Die Gesprächsliste --------------------------------------- */
|
|
.chat-raum__knopf {
|
|
position: relative;
|
|
border-radius: 12px;
|
|
margin: 0 8px 4px;
|
|
transition: background var(--tempo), box-shadow var(--tempo);
|
|
}
|
|
/* Das offene Gespräch trägt eine Schiene -- man sieht auch beim Scrollen
|
|
der Liste, wo man gerade ist. */
|
|
.chat-raum__knopf[data-an="ja"] {
|
|
background: linear-gradient(90deg,
|
|
color-mix(in srgb, var(--akzent) 16%, transparent),
|
|
rgba(255, 255, 255, .03) 55%);
|
|
box-shadow: inset 3px 0 0 var(--akzent);
|
|
}
|
|
/* Ungelesenes ist der Grund, warum man hier ist. */
|
|
.chat-raum__knopf:has(.chat-raum__zahl) {
|
|
background: rgba(255, 255, 255, .045);
|
|
}
|
|
|
|
/* ---------- Die Nachrichten ------------------------------------------ */
|
|
.chat-nachricht__blase {
|
|
border-radius: 16px 16px 16px 5px;
|
|
box-shadow:
|
|
inset 0 1px 0 rgba(255, 255, 255, .06),
|
|
0 2px 6px -3px rgba(0, 0, 0, .55);
|
|
}
|
|
/* Die erste Blase einer Folge bekommt oben eine eckige Ecke -- dort
|
|
fängt der Gedanke an. Die folgenden sind rundum weich. */
|
|
.chat-nachricht[data-folge="ja"] .chat-nachricht__blase {
|
|
border-top-left-radius: 8px;
|
|
}
|
|
.chat-nachricht[data-selbst="ja"] .chat-nachricht__blase {
|
|
border-radius: 16px 16px 5px 16px;
|
|
background: linear-gradient(160deg,
|
|
color-mix(in srgb, var(--akzent) 26%, rgba(17, 24, 37, .95)),
|
|
color-mix(in srgb, var(--akzent) 12%, rgba(12, 17, 27, .95)));
|
|
box-shadow:
|
|
inset 0 1px 0 rgba(255, 255, 255, .10),
|
|
0 2px 8px -3px color-mix(in srgb, var(--akzent) 45%, transparent);
|
|
}
|
|
.chat-nachricht[data-selbst="ja"][data-folge="ja"] .chat-nachricht__blase {
|
|
border-top-right-radius: 8px;
|
|
border-top-left-radius: 16px;
|
|
}
|
|
|
|
/* Der Tagestrenner: eine Linie mit dem Datum darin statt eines Wortes
|
|
im Nichts. */
|
|
.chat-tag {
|
|
display: flex; align-items: center; gap: 12px;
|
|
margin: 18px 0 12px;
|
|
}
|
|
.chat-tag::before, .chat-tag::after {
|
|
content: ""; flex: 1 1 auto; height: 1px;
|
|
background: linear-gradient(90deg, transparent, rgba(150, 186, 220, .18), transparent);
|
|
}
|
|
|
|
/* ---------- Das Eingabefeld ------------------------------------------ */
|
|
/* Es ist das wichtigste Element der Seite und sah aus wie eines von
|
|
vielen. Eine eigene Leiste, die sich beim Hineinschreiben hebt. */
|
|
/* Sie heisst .chat__eingabe (chat.html), nicht .chat-neu -- ich hatte
|
|
den Namen aus dem Kopf geschrieben. pruef-struktur meldete es als
|
|
totes CSS: eine Regel, die auf nichts zeigt, sieht aus wie erledigte
|
|
Arbeit und ist keine. */
|
|
.chat__eingabe {
|
|
position: relative;
|
|
padding: 12px 14px;
|
|
border-top: 1px solid rgba(150, 186, 220, .12);
|
|
background: linear-gradient(180deg, rgba(13, 18, 28, .6), rgba(9, 12, 20, .92));
|
|
transition: box-shadow var(--tempo);
|
|
}
|
|
.chat__eingabe:focus-within {
|
|
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--akzent) 34%, transparent);
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.chat-raum__knopf, .chat__eingabe { transition: none; }
|
|
}
|
|
@media (forced-colors: active) {
|
|
.chat-nachricht__blase,
|
|
.chat-raum__knopf[data-an="ja"] { box-shadow: none; border: 1px solid CanvasText; }
|
|
.chat-raum__zeichen, .chat-nachricht__zeichen { background: Canvas; box-shadow: none; }
|
|
}
|
|
|
|
/* =====================================================================
|
|
DREI DINGE, DIE DEM CHAT GEFEHLT HABEN (Punkt 15, 09.09.2026)
|
|
|
|
Filipe: "ich will dass du diese seite viel krasser und detaillierter
|
|
machst, ich will dass du dich informierst und alles reinsetzt was
|
|
wir noch gebrauchen könnten."
|
|
|
|
Nicht alles, sondern das, was hier täglich fehlt. Der Chat konnte
|
|
schon Räume, Verlauf, Gelesen-Stand, Live-Zustellung, Gruppen und
|
|
Zurücknehmen. Was fehlte:
|
|
|
|
1. SUCHE IN DEN NACHRICHTEN. Ein Chat ohne sie ist ab dem zweiten
|
|
Monat ein Archiv, in dem man nichts findet. Das Suchfeld gab es
|
|
— es durchsuchte aber nur die NAMEN der Gespräche.
|
|
2. ANTWORTEN MIT ZITAT. Zu zweit weiß man meist, worauf sich etwas
|
|
bezieht. In einer Gruppe laufen drei Fäden parallel, und "ja,
|
|
mach das" kann alles heißen.
|
|
3. DIE LINIE "AB HIER NEU". Wer nach zwei Tagen zurückkommt, sucht
|
|
sonst die Stelle, an der er aufgehört hat, indem er Uhrzeiten
|
|
liest.
|
|
|
|
Bewusst NICHT gebaut: Anhänge (dafür gibt es den Dateien-Bereich mit
|
|
Rechten und Ablauf), Reaktionen (eine vierte Sache, bevor die drei
|
|
sich bewährt haben) und Tipp-Anzeigen (sie kosten dauernd Verkehr
|
|
und sagen nichts, was man nicht in zwei Sekunden sieht).
|
|
===================================================================== */
|
|
|
|
/* ---- Das Zitat in der Blase ---------------------------------------- */
|
|
.chat-zitat {
|
|
display: block; width: 100%; text-align: left;
|
|
margin: 0 0 7px; padding: 6px 10px 6px 11px;
|
|
border: 0; border-radius: 0;
|
|
/* Der senkrechte Strich links ist das Zeichen für ein Zitat -- in
|
|
jedem Mailprogramm seit dreißig Jahren dasselbe. */
|
|
box-shadow: inset 3px 0 0 color-mix(in srgb, var(--akzent) 60%, transparent);
|
|
background: rgba(255, 255, 255, .05);
|
|
color: var(--text-leise);
|
|
font: inherit; cursor: pointer;
|
|
transition: background var(--tempo);
|
|
}
|
|
.chat-zitat:hover { background: rgba(255, 255, 255, .09); }
|
|
.chat-zitat__von {
|
|
display: block;
|
|
font-size: .74rem; font-weight: 700;
|
|
color: color-mix(in srgb, var(--akzent) 70%, #ffffff);
|
|
}
|
|
.chat-zitat__text {
|
|
display: block;
|
|
font-size: .8rem; line-height: 1.35;
|
|
/* Zwei Zeilen reichen: Es ist ein Hinweis auf die Stelle, nicht ihre
|
|
Wiederholung. */
|
|
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Die zitierte Nachricht, wenn man hinspringt -- kurz hervorgehoben.
|
|
Ein Sprung ohne Markierung lässt einen raten, welche Zeile gemeint
|
|
war. */
|
|
.chat-nachricht[data-hervor="ja"] .chat-nachricht__blase {
|
|
box-shadow: 0 0 0 2px color-mix(in srgb, var(--akzent) 65%, transparent);
|
|
transition: box-shadow .4s ease;
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.chat-nachricht[data-hervor="ja"] .chat-nachricht__blase { transition: none; }
|
|
}
|
|
|
|
/* ---- Die Linie "ab hier neu" --------------------------------------- */
|
|
.chat-neu {
|
|
display: flex; align-items: center; gap: 10px;
|
|
margin: 14px 2px 10px;
|
|
color: #ff8f98;
|
|
font-size: .72rem; font-weight: 700;
|
|
letter-spacing: .1em; text-transform: uppercase;
|
|
}
|
|
.chat-neu::before, .chat-neu::after {
|
|
content: ""; flex: 1; height: 1px;
|
|
background: linear-gradient(90deg, transparent, rgba(255, 143, 152, .5));
|
|
}
|
|
.chat-neu::after { background: linear-gradient(90deg, rgba(255, 143, 152, .5), transparent); }
|
|
.chat-neu__text { flex: none; }
|
|
|
|
/* ---- Die Antwortleiste über dem Eingabefeld ------------------------ */
|
|
.antwort-leiste {
|
|
display: flex; align-items: flex-start; gap: 10px;
|
|
margin-bottom: 8px; padding: 8px 10px 8px 11px;
|
|
box-shadow: inset 3px 0 0 var(--akzent);
|
|
background: rgba(255, 255, 255, .05);
|
|
}
|
|
.antwort-leiste[hidden] { display: none; }
|
|
.antwort-leiste__inhalt { min-width: 0; flex: 1; }
|
|
.antwort-leiste__schild {
|
|
/* 0,72 rem = 11,52 px. Mit 0,7 waeren es 11,2 und damit unter der
|
|
Grundlinie, die pruef-css-klassen haelt. */
|
|
font-size: .72rem; letter-spacing: .08em; text-transform: uppercase;
|
|
color: var(--text-still);
|
|
}
|
|
.antwort-leiste__von {
|
|
margin-left: 6px;
|
|
font-size: .78rem; font-weight: 700;
|
|
color: color-mix(in srgb, var(--akzent) 70%, #ffffff);
|
|
}
|
|
.antwort-leiste__text {
|
|
display: block;
|
|
font-size: .82rem; color: var(--text-leise);
|
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
}
|
|
.antwort-leiste__weg {
|
|
flex: none; width: 28px; height: 28px;
|
|
border: 0; border-radius: 50%;
|
|
background: rgba(255, 255, 255, .07);
|
|
color: var(--text-leise); font-size: .9rem; cursor: pointer;
|
|
}
|
|
.antwort-leiste__weg:hover { background: rgba(255, 255, 255, .13); color: var(--text); }
|
|
|
|
/* ---- Fundstellen in Nachrichten ------------------------------------ */
|
|
.chat-funde { padding: 4px 10px 12px; }
|
|
.chat-funde[hidden] { display: none; }
|
|
.chat-funde__schild {
|
|
margin: 8px 0 6px;
|
|
font-size: .72rem; font-weight: 700;
|
|
letter-spacing: .09em; text-transform: uppercase;
|
|
color: var(--text-still);
|
|
}
|
|
.chat-funde__liste { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
|
|
.chat-funde__leer { font-size: .84rem; color: var(--text-still); }
|
|
.chat-fund {
|
|
display: grid; gap: 2px;
|
|
width: 100%; padding: 8px 11px;
|
|
text-align: left; border: 0;
|
|
clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
|
|
background: rgba(255, 255, 255, .045);
|
|
color: var(--text); font: inherit; cursor: pointer;
|
|
transition: background var(--tempo);
|
|
}
|
|
.chat-fund:hover { background: rgba(255, 255, 255, .09); }
|
|
.chat-fund__kopf { display: flex; justify-content: space-between; gap: 8px; }
|
|
.chat-fund__raum {
|
|
font-size: .78rem; font-weight: 700;
|
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
}
|
|
.chat-fund__wann { flex: none; font-size: .72rem; color: var(--text-still); }
|
|
.chat-fund__von { font-size: .72rem; color: var(--text-still); }
|
|
.chat-fund__text {
|
|
font-size: .82rem; color: var(--text-leise); line-height: 1.35;
|
|
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Der Antworten-Knopf am Fuß einer Nachricht -- genauso leise wie
|
|
"zurücknehmen" daneben. Er ist ein Angebot, keine Aufforderung. */
|
|
.chat-nachricht__antwort-knopf {
|
|
border: 0; background: none; padding: 0;
|
|
font: inherit; font-size: .72rem;
|
|
color: var(--text-still); cursor: pointer;
|
|
text-decoration: underline; text-underline-offset: 2px;
|
|
}
|
|
.chat-nachricht__antwort-knopf:hover { color: var(--text-leise); }
|