Files
dogfather-universe/workspace/assets/css/calls.css
T
DogFatherGitandClaude Opus 5 4637aa24e7 Chat zwischen Creator, Scouts und Managern
Wunsch Filipe, 06.09.2026: *"eine kategorie chat, wo die creator mit
ihren manager nachrichten austauschen koennen, wie erinnerungen, fragen
und noch vieles mehr."* Auf Nachfrage entschieden: Zweier-Gespraeche UND
Gruppen; Manager und Scouts duerfen sich auch ohne gemeinsamen Creator
schreiben.

WER MIT WEM steht an EINER Stelle: schreibbareIds() in workspace.js.
Bewusst getrennt von einladbareIds() (Terminwahl): Dort geht es darum,
wen man zu einem Kalendereintrag dazustellen darf -- harmlos. Ein Chat
legt ein dauerhaftes Gespraech an, schickt eine Benachrichtigung und
macht den eigenen Namen sichtbar. Zwei Fragen, zwei Funktionen.

Ein Creator erreicht seine Betreuer und DogFather, KEINEN fremden
Creator. Eine Gruppe ist kein Schlupfloch: Jede Nummer wird einzeln
gegen dieselbe Regel geprueft.

SOFORT STATT NACHFRAGEN IM TAKT (SSE). Ein Chat, der alle fuenf
Sekunden fragt, ist fuenf Sekunden langsam und stellt bei zehn
Angemeldeten 7200 Anfragen in der Stunde fuer Nachrichten, die es
meistens nicht gibt. Kein WebSocket: Hier fliesst alles in eine
Richtung, und SSE uebersteht einen Verbindungsabriss von selbst.

DIE ZAHL STEHT AUF JEDER SEITE, nicht nur im Chat. Eine Nachricht, die
man erst sieht, wenn man den Chat aufmacht, ist keine Nachricht --
sie ist ein Fundstueck.

Push aufs Handy nur an die, die NICHT gerade zusehen. Wer die Seite
offen hat, sieht es ohnehin; ihm zusaetzlich etwas aufs Handy zu
schicken ist der schnellste Weg, dass er Benachrichtigungen abschaltet.

WAS BEWUSST NICHT GEHT:
  * Niemand liest fremde Gespraeche mit -- auch DogFather nicht. Ein
    Chat, in dem der Chef stillschweigend mitliest, ist kein Chat.
    Er kann jederzeit jedem schreiben, aber sichtbar.
  * Eine abgeschickte Nachricht laesst sich nicht aendern, nur
    zuruecknehmen. Wer Absprachen nachtraeglich umschreiben kann, macht
    den Verlauf wertlos. Zurueckgenommenes hinterlaesst einen Hinweis
    statt eines Lochs.

DAZU: CALL-LISTE NACH ZEIT GEGLIEDERT

Wunsch mit Bild von "STEHT AN 27": *"die liste soll kategorisiert sein
und werden mit einem button zum auf und zu machen."* Jetzt Heute /
Diese Woche / Naechste Woche / Spaeter, offen nur der erste gefuellte
Block. Nach ZEIT und nicht nach Titel: Auf dem Bild waren fast alle
"Community-Talk" -- nach Titel gruppiert haette man zwei Ueberschriften
und darunter dieselbe lange Liste.

DER FUND, DER DIE FUNKTION GERETTET HAT: Ein zugeklappter Block zeigte
seinen Inhalt trotzdem -- 367 Pixel Hoehe bei open=false. <details>
versteckt seinen Inhalt naemlich nur, solange niemand dem Inhalt eine
eigene display-Angabe gibt, und .gruppe__karten traegt display:grid.
Der Knopf haette sich bewegt und sonst nichts getan. Im Bildschirmfoto
fiel es nicht auf, weil der Ausschnitt genau darueber endete.

Ausserdem gefunden und behoben:
  * Ein Wettlauf beim Abschicken: War der Ereignisstrom schneller als
    die Antwort auf das POST, stand die eigene Nachricht zweimal im
    Verlauf.
  * Die Chatseite war zu hoch -- die Kopfleistenhoehe stand als fester
    Wert (62 px) im CSS, am Handy ist sie doppelt so hoch. Das
    Eingabefeld stand halb unter dem Bildschirmrand. Jetzt gemessen.
  * Der Platzhalter "Enter schickt ..." brach am Handy um UND stimmte
    dort nicht: Ohne Tastatur schickt Enter absichtlich nicht.
  * Der Dialog benutzte Klassen aus aufgaben.css, das chat.html nicht
    lud -- gemeldet von pruef-css-klassen, bevor jemand einen
    ungestylten Dialog zu sehen bekam.

Pruefungen: pruef-chat (44 Punkte, Rechte und Sichtbarkeit),
pruef-chat-optik (24, zwei echte Browser schreiben sich),
pruef-call-kategorien (16).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 16:28:58 +02:00

227 lines
8.4 KiB
CSS

/* ===================================================================
Calls & Meeting-Protokolle.
Nur eine Gruppe wird optisch hervorgehoben: die, in der etwas fehlt.
Alles andere bleibt bewusst ruhig -- sonst schreit die ganze Seite
und man sieht nicht mehr, was wirklich dran ist.
=================================================================== */
.gruppe { margin-bottom: 32px; }
.stufe__kopf { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.stufe__titel {
margin: 0; font-size: .78rem; font-weight: 700;
letter-spacing: .08em; text-transform: uppercase; color: var(--akzent);
}
.stufe__zahl {
min-width: 22px; padding: 1px 7px; text-align: center;
font-size: .74rem; font-weight: 700; color: var(--text-still);
background: rgba(255, 255, 255, .06); border-radius: 999px;
}
.block__frage { margin: 0 0 12px; font-size: .85rem; color: var(--text-still); }
.gruppe__karten { display: grid; gap: 10px; margin-top: 12px; }
.gruppe[data-gruppe="offen"] .stufe__titel { color: #f0c48a; }
.gruppe[data-gruppe="offen"] .stufe__zahl {
color: #f0c48a; background: rgba(240, 196, 138, .13);
}
/* ---------- Karte --------------------------------------------------------- */
.call {
position: relative;
padding: 15px 18px 15px 21px;
background: var(--flaeche);
border: 1px solid var(--rand);
border-radius: var(--radius-klein);
transition: border-color .18s ease;
}
.call::before {
content: ""; position: absolute; left: 0; top: 10px; bottom: 10px; width: 3px;
border-radius: 0 3px 3px 0;
background: var(--rand-stark, rgba(255, 255, 255, .18));
}
.call[data-gruppe="offen"] {
border-color: rgba(240, 196, 138, .32);
background: rgba(240, 196, 138, .045);
}
.call[data-gruppe="offen"]::before { background: #f0c48a; }
.call[data-gruppe="anstehend"]::before { background: var(--akzent); }
.call[data-gruppe="erledigt"] { opacity: .82; }
.call[data-gruppe="erledigt"]:hover { opacity: 1; }
.call[data-gruppe="erledigt"]::before { background: #79d1a2; opacity: .55; }
.call:hover { border-color: rgba(255, 255, 255, .18); }
.call__kopf { display: flex; gap: 14px; align-items: flex-start; justify-content: space-between; flex-wrap: wrap; }
.call__titel { margin: 0; font-size: 1.02rem; font-weight: 650; }
.call__zeit { margin: 2px 0 0; font-size: .82rem; color: var(--text-still); }
.call__text, .call__ort { margin: 10px 0 0; font-size: .86rem; color: var(--text-leise); }
.call__ort::before { content: "Ort: "; color: var(--text-still); }
.marke-fehlt {
padding: 3px 9px; border-radius: 999px;
font-size: .71rem; font-weight: 600;
color: #f0c48a; background: rgba(240, 196, 138, .12);
border: 1px solid rgba(240, 196, 138, .35);
}
/* ---------- Aufgeklappt ---------------------------------------------------- */
.call__detail { margin-top: 15px; padding-top: 15px; border-top: 1px solid var(--rand); }
.call__abschnitt {
margin: 2px 0 14px; font-size: .88rem; color: var(--text-leise);
white-space: pre-wrap; max-width: 78ch;
}
.call__quelle { margin: 12px 0 0; font-size: .77rem; color: var(--text-still); }
.call__verweis { color: var(--akzent); }
.todo-liste { margin: 4px 0 0; padding: 0; list-style: none; }
.todo-liste li {
padding: 9px 13px; margin-bottom: 6px;
background: var(--flaeche);
border: 1px solid var(--rand); border-radius: var(--radius-klein);
font-size: .87rem;
}
.todo-liste li[data-status="erledigt"] { opacity: .6; }
.todo-liste li[data-status="erledigt"] .todo-liste__titel { text-decoration: line-through; }
.todo-liste__titel { display: block; }
.todo-liste .kk__still { display: block; margin-top: 2px; font-size: .76rem; color: var(--text-still); }
/* ---------- Protokollbogen -------------------------------------------------- */
.bogen__hinweis { margin: 1px 0 6px; font-size: .78rem; color: var(--text-still); max-width: 70ch; }
.bogen__feld {
width: 100%; margin-bottom: 16px; padding: 10px 12px;
background: rgba(255, 255, 255, .04);
border: 1px solid var(--rand); border-radius: var(--radius-klein);
color: var(--text); font: inherit; resize: vertical;
}
.bogen__zeit {
margin-bottom: 14px; padding: 9px 11px;
background: rgba(255, 255, 255, .05);
border: 1px solid var(--rand); border-radius: var(--radius-klein);
color: var(--text); font: inherit;
}
.todos { display: grid; gap: 7px; margin: 4px 0 10px; }
.todo { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 7px; }
.todo__titel, .todo__frist {
padding: 9px 11px;
background: rgba(255, 255, 255, .05);
border: 1px solid var(--rand); border-radius: var(--radius-klein);
color: var(--text); font: inherit;
}
.bogen__feld:focus, .bogen__zeit:focus, .todo__titel:focus, .todo__frist:focus {
outline: none; border-color: rgba(63, 189, 245, .65);
box-shadow: 0 0 0 3px rgba(63, 189, 245, .14);
}
@media (max-width: 560px) {
.todo { grid-template-columns: minmax(0, 1fr) auto; }
.todo__frist { grid-column: 1 / 2; }
.todo__weg { grid-row: 1 / 3; }
}
@media (prefers-reduced-motion: reduce) {
.call, .todo__weg { transition: none; }
}
/* ---------- Aufklappbare Zeitraum-Blöcke --------------------------------
Wunsch Filipe, 06.09.2026, mit einem Bild von "STEHT AN 27":
*"die liste da soll kategorisiert sein und werden mit einem button
zum auf und zu machen."*
Siebenundzwanzig Karten am Stück beantworten keine Frage -- man
scrollt an allem vorbei, was diese Woche ansteht, um zu sehen, dass
der Rest im November liegt. Jetzt: Heute · Diese Woche · Nächste
Woche · Später, und offen ist nur der erste gefüllte Block.
Gestaltet wie der Bereich "Abgebrochen" auf dem Aufgabenbrett --
dasselbe Bauteil soll auch gleich aussehen. */
.zeitblock {
margin-top: 10px;
border: 1px solid var(--rand);
border-radius: 13px;
background: rgba(255, 255, 255, .018);
}
.zeitblock + .zeitblock { margin-top: 8px; }
.zeitblock__kopf {
display: flex; align-items: center; gap: 10px;
padding: 12px 15px;
cursor: pointer;
font-size: .8rem; font-weight: 700;
letter-spacing: .05em; text-transform: uppercase;
color: var(--text-still);
/* Das eigene Dreieck von <summary> weg -- es wird unten selbst
gezeichnet, damit es links steht und sich beim Öffnen dreht. */
list-style: none;
}
.zeitblock__kopf::-webkit-details-marker { display: none; }
.zeitblock__kopf:hover { color: var(--text-leise); }
.zeitblock__kopf:focus-visible {
outline: 2px solid var(--akzent); outline-offset: -2px; border-radius: 13px;
}
.zeitblock__kopf::before {
content: ""; flex: none;
width: 0; height: 0;
border-left: 5px solid currentColor;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
transition: transform .18s ease;
}
.zeitblock[open] .zeitblock__kopf::before { transform: rotate(90deg); }
.zeitblock__name { flex: none; }
.zeitblock__zahl {
padding: 2px 9px; border-radius: 999px;
font-size: .75rem; font-weight: 700; letter-spacing: 0;
color: var(--text-leise);
background: rgba(255, 255, 255, .06);
border: 1px solid var(--rand);
font-variant-numeric: tabular-nums;
}
/* "ab 23. Sep" -- ohne das sagt "Später" nicht, ob morgen oder im
November. Steht rechts und tritt zurück. */
.zeitblock__ab {
margin-left: auto;
font-size: .74rem; font-weight: 500;
letter-spacing: 0; text-transform: none;
color: var(--text-still);
}
/* Der offene Block hebt sich ab -- sonst sieht man beim Scrollen nicht,
wo man gerade ist. */
.zeitblock[open] { border-color: var(--rand-hell); }
.zeitblock[open] .zeitblock__kopf { color: var(--text-leise); }
.zeitblock .gruppe__karten { padding: 4px 13px 14px; }
/* ZUGEKLAPPT HEISST WIRKLICH WEG (06.09.2026, gefunden von
pruef-call-kategorien).
<details> versteckt seinen Inhalt von selbst -- aber nur, solange
niemand dem Inhalt eine eigene `display`-Angabe gibt. `.gruppe__karten`
traegt `display: grid` (aus dem gemeinsamen Stil der Gruppen), und
damit war der Block zwar ZUGEKLAPPT, seine Karten standen aber
weiterhin darunter: gemessen 367 Pixel Hoehe bei open=false.
Die ganze Funktion waere damit wirkungslos gewesen -- der Knopf
haette sich bewegt und sonst nichts getan. Im Bildschirmfoto fiel es
nicht auf, weil der Ausschnitt genau darueber endete.
Deshalb hier ausdruecklich: zu ist zu. */
.zeitblock:not([open]) .gruppe__karten { display: none; }
@media (prefers-reduced-motion: reduce) {
.zeitblock__kopf::before { transition: none; }
}
@media (forced-colors: active) {
.zeitblock { border: 1px solid CanvasText; }
.zeitblock__zahl { border: 1px solid CanvasText; }
}