Compare commits

..
3 Commits
Author SHA1 Message Date
DogFatherGitandClaude Opus 5 18a5231b70 Die Tuer ohne Klingel, der Anruf ohne Ende, die Glocke ohne Liste
Siebter Durchgang durch die Team-Dogi-Seiten aus Benutzersicht. Alles am
Code belegt, die Groessenangaben nachgerechnet.

=== DIE ANMELDUNG ===

WOHER BEKOMMT MAN EINEN CODE? Stand nirgends. Vier Kacheln, ein Feld,
ein Knopf -- wer keinen Code hatte, fand keinen einzigen Satz dazu. Fuer
die Community besonders teuer: Sie ist die einzige Rolle, die von
aussen kommt und niemanden im Haus kennt.

"BITTE KURZ WARTEN" SIND BIS ZU ZEHN MINUTEN (VERSUCHE_FENSTER_MIN).
Wer nach zwanzig Sekunden nachsieht und dieselbe Meldung liest, haelt
die Seite fuer kaputt. Jetzt steht die Zahl da.

EIN RICHTIGER CODE AN DER FALSCHEN KACHEL fiel in dieselbe Absage wie
ein erfundener -- der Server sucht nur unter der gewaehlten Rolle. Wer
aus der Community kommt und auf "Modi" tippt (das klingt ja danach),
tippt seinen richtigen Code dreimal Buchstabe fuer Buchstabe. Ab dem
zweiten Fehlversuch kommt jetzt ein Hinweis auf die Auswahl -- ohne zu
sagen, welche die richtige waere.

=== DER ANRUF: VIER FEHLER, EINE ERFAHRUNG ===

DAS FREIZEICHEN LIEF UNENDLICH. Es gab keinen Zeitgeber, der aufhoert.
Wer niemanden erreichte, sass vor einem piependen Kasten mit "02:47"
darauf, als telefoniere er laengst. Der Server schickt den Verfallszeit-
punkt sogar mit (`laeuft_bis`) -- benutzt hat ihn nie jemand.

DER KLINGELKASTEN VERSCHWAND NACH 46 SEKUNDEN, waehrend der Server 120
gibt. Wer beim Klingeln das Handy aus der Tasche holt und in Sekunde 50
hinsieht, fand nichts mehr -- kein Kasten, keine Knoepfe -- waehrend der
Anrufer noch siebzig Sekunden Freizeichen hoerte. Die 120 Sekunden sind
im Server ausdruecklich damit begruendet, dass man Zeit zum Entsperren
braucht; eine Zeile im Browser hat diese Entscheidung aufgehoben.

DAS FREIZEICHEN PIEPTE WEITER, waehrend daneben "Keine Verbindung."
stand. Ton sagte "es klingelt noch", Text sagte "vorbei".

DIE UHR ZAEHLTE AB DEM WAEHLEN. Wer 30 Sekunden klingeln liess und 12
Sekunden sprach, las im Kasten "00:42" und danach im Chat "Anruf . 12
Sekunden". Der Server rechnet es richtig; der Kasten hat es wieder
kaputtgemacht.

Dazu: Die Mikrofon-Absage verwies auf "das Schloss-Symbol neben der
Adresse" -- in der installierten App gibt es weder Adresszeile noch
Schloss. Das Haus kennt diesen Fehler und hat ihn beim Weg zur
Anruf-Probe schon behoben; hier stand er noch.

Und die Anruf-Probe: Ihr einziger Punkt ohne Handlungsanweisung war
"Antwort 503." -- auf einer Seite, die ausdruecklich fuer jemanden
gebaut ist, der nicht technisch ist. Ihr Rueckweg fuehrte ausserdem
immer in den Chat, auch wenn man von der Startseite kam.

=== DER TREFF ===

EIN EINZEILER MACHTE SECHS ERKLAERTEXTE UNSICHTBAR. bewerben.js las
`g.unter`, der Server schickt `g.text` -- also immer undefined. Damit
fehlten ALLE SECHS Gruppeneinleitungen des Fragebogens ("Kein Test mit
richtigen Antworten"), und die CSS-Regel dafuer lief ins Leere. Uebrig
blieben sechs nackte Ueberschriften ueber fuenfzehn Fragen.

DIE EINZIGE "SO LAEUFT DAS HIER"-SEITE WAR FAST UNERREICHBAR. Die
Kachel "Regeln & Hilfe" zeigte auf ein Brett, das am ersten Tag leer
ist. Die Regeln, die es wirklich gibt (Begruessung, fuenf Regeln, drei
Stufen, Mindestalter), stehen auf treff-regeln.html -- erreichbar ueber
genau einen kleinen Textlink auf einer Brettseite. Die Kachel zeigt
jetzt dorthin.

FIEL /api/treff/lage AUS, versprach die Seite einem Zuschauer
Schreibrechte, die der Server ablehnt: Die Pruefung fiel bei `lage ===
null` auf `true` durch -- von "darf auf keinem Brett" auf "darf
ueberall". Formular auf, getippt, 403.

Dazu: hilfe.js hatte eine eigene kleine Fehlertabelle fuer zwei
Kennungen und endete sonst bei "Ging nicht." -- ausgerechnet die Seite,
auf der jemand mit einem Problem sitzt, hatte die inhaltsleerste Absage
im Haus. Und der gesperrte "Neue Meldung"-Knopf erklaerte sich nur im
Maus-Tooltip; die Community kommt von TikTok, also vom Handy.

=== DIE STARTSEITE ===

DIE COMMUNITY BEKAM EIN WORT. Unter dem Titel steht bei jeder Rolle ein
Satz -- beim Community-Mitglied stand "Community". Woertlich derselbe
Mangel, den der Kommentar bei MODI_ROLLENTEXT beschreibt und fuer den
Modi behebt.

DER RING MELDETE 100 %, bevor man irgendetwas getan hat. Am ersten Tag,
ohne eine einzige Aufgabe, stand das groesste Element der Seite auf
"100 % -- HEUTE NICHTS OFFEN". Gelesen wird zuerst die Zahl, und 100 %
heisst fuer jeden "fertig", nicht "leer".

UND BEI EINER STOERUNG BLIEB "wird geladen" FUER IMMER STEHEN -- der
fruehe `return` liess den Platzhalter unberuehrt.

=== DIE ZUGANGSVERWALTUNG ===

Der Code-Kasten sagte "jetzt weitergeben" und gab die Haelfte nicht
mit, die man weitergeben muss: WO sich die Person anmeldet. DogFather
ruft den Code durchs Zimmer, der andere fragt "und wo?". Die Adresse
kommt jetzt vom Server (anmeldeAdresseFuer, abgeleitet aus derselben
Weiche), dazu ein Knopf "Code und Adresse kopieren".

Er verschwieg ausserdem den Rettungsweg: "danach nicht mehr abrufbar"
stimmt fuer DIESEN Code -- man kann jederzeit einen neuen vergeben. Wer
das nicht weiss, glaubt, er habe einen Menschen angelegt, der nie
hereinkommt. Und das Fenster liess sich wortlos schliessen, waehrend
der Code offen stand.

=== OPTIK: NACHGERECHNET, NICHT VERMUTET ===

DIE GLOCKE STAND NICHT IN DER LISTE. Sie ist ein echtes <button> und
wurde von der 44-px-Regel erfasst, waehrend ihre vier Nachbarn durch
einen staerkeren Selektor auf 36 px gehen. Zwischen 401 und 560 px --
also bei 412 px (haeufigste Android-Breite) und 430 px (iPhone Pro Max)
-- stand eine 44 px hohe runde Pille zwischen fuenf 36-px-Quadraten.
Der 400er-Block listet sie korrekt; genau dieses Band fiel durch beide
Rechnungen. Dasselbe Muster wie am 06.09.

DIE 9,6-px-REPARATUR IM KALENDER WAR SEIT WOCHEN WIRKUNGSLOS.
kalender.css wird NACH start.css geladen, beide Selektoren sind gleich
stark -- also gewann `.6rem` auf jeder Breite unter 760 px. Genau der
Wert, den start.css als behobenen Fehler protokolliert.

Und die Hebung selbst unterschritt ihre eigene Grenze: Die Regel, die
zu kleine Schrift hochziehen soll, zog `.k-pille` auf 11,2 px -- 0,3 px
unter die 11,5, die 33 Zeilen darueber aufgestellt werden.

Weitere nachgerechnete Groessen: Spaltenkopf und KW-Spalte im Kalender
(10,56 px), Kalenderwoche am Balken (9,92 px), Wecker-Knopf (30 px --
der kleinste im Haus, 14 unter der Vorgabe), Chat-Zurueck auf dem
Tablet (34x34, 40 % weniger Flaeche als noetig, und der einzige Weg
zurueck in die Liste), Emoji-Knoepfe (34 px bei 2 px Abstand -- wer
danebentippt, verschickt ein anderes Zeichen, und das ist sofort
abgeschickt).

IM WINDOWS-KONTRASTMODUS HATTE DIE STARTSEITE KEINE UEBERSCHRIFT.
`.ztitel` und der Team-Dogi-Schriftzug sind mit `background-clip: text`
gesetzt; faellt der Verlauf weg, bleibt durchsichtiger Text. Beide
haben jetzt den Rueckfall, den gate.css schon lange hat.

UND MEIN EIGENES STREIFENRASTER rechnete die Spaltenzahl aus der Breite
statt aus den Daten (`auto-fit`): Bei 390 px passten sechs, alles
darueber fiel in eine zweite, unbeschriftete Zeile. Das `overflow-x`
daneben konnte nie greifen -- ein auto-fit-Raster wird nie breiter als
sein Kasten. Der Kommentar beschrieb ein Verhalten, das es nicht gab.

GEMESSEN: pruef-meldungen 8/0, pruef-css-klassen ALLES IN ORDNUNG,
pruef-rechtetafel 19/0, alle Module laden, keine doppelten
Kachelnamen/-ziele/-farben in allen vier Rollen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-19 15:07:31 +02:00
DogFatherGitandClaude Opus 5 273318dd9c Vier Sackgassen, zwei Kacheln namens "Chat" -- und eine Seite ohne Kopf
Durchgang durch die Team-Dogi-Seiten aus der Sicht von jemandem, der
sie zum ersten Mal sieht. Alles hier ist am Code belegt und nachgemessen.

1. "KLINGELT NICHTS?" WARF ALLE DREI TEAM-ROLLEN AUF DIE STARTSEITE.

anruf-probe.html erklaert, WARUM das Telefon stumm bleibt. Sie steht in
der Rechtetafel offen, und aus dem Chat zeigen zwei Knoepfe darauf. Nur
hatte sie nie eine Kachel -- absichtlich, sie ist kein Bereich. Seit die
Adressregel vom 15.09. eine Kachel VERLANGT, landete jeder Klick wortlos
auf start.html.

Das ist die schlimmste Sorte Sackgasse: Man ruft sie auf, WEIL schon
etwas nicht geht, und sie wirft einen hinaus.

Mit derselben Zeile geheilt: Die Hinweisleiste wirft jeden Hinweis weg,
dessen Ziel es auf dieser Adresse nicht gibt -- "Bei dir klingelt nichts"
wurde auf crew deshalb NIE angezeigt. Ausgerechnet der Hinweis, der acht
von elf Leuten betrifft.

2. UND DIESE SEITE HATTE GAR KEINE GESTALTETE KOPFLEISTE.

.kopfleiste, __zurueck, __mitte, __titel stehen in KEINER Stilvorlage --
sie leben in start.css, die diese Seite absichtlich nicht laedt. Die
Leiste war nackter Text. Auf einer Seite, die man im Stoerfall aufruft,
sieht unfertig aus wie kaputt. Jetzt im <style>-Block der Seite, aus
demselben Grund wie ihr uebriges CSS.

3. EIN MODI KAM NICHT AN SEINE EIGENE AUSWERTUNG.

entwicklung.html und werdegang.html haben BEIDE eine ausgearbeitete
Eigensicht, und die Rechtetafel laesst ihn hinein. In seiner Kachelliste
stand keine von beiden -- der Weg existierte fuer ihn nicht. Die ganze
Eigensicht war toter Code, ausgerechnet fuer den, fuer den sie ist.
Mein eigener Fehler vom Vortag.

Zwei eigene Kacheln in "Fuer dich", aus den Leitungskacheln abgeleitet.
Sie loesen zugleich eine Namenskollision: Bis heute setzten BEIDE Seiten
fuer ihn die Ueberschrift "Deine Entwicklung".

4. DER KALENDER-LINK WAR FUER DIE COMMUNITY EINE SACKGASSE.

Auf jeder Brettkarte mit Termin stand "Im Kalender am 24.09." als Link.
kalender.html steht auf ALLE -- und ALLE ist ausdruecklich ohne "gast".
Jetzt fragt der SERVER die beiden Schranken (kalender_offen) und die
Karte zeigt den Termin als Text statt als Link. Die Auskunft bleibt, nur
der Weg ins Leere faellt weg. Die Regel im Browser nachzubauen waere die
vierte Stelle fuer dieselbe Frage gewesen.

5. EIN MODI KAM AN DIE TALENTE-NOTIZEN, DIE IHM VERWEHRT SIND.

BRETTER_UEBER_SEITEN war eine feste Liste ("wer die Seite darf, muss
auch dorthin kommen") und fragte nie, WER fragt. Ueber
bereich.html?b=talente kam er an Notizen zu Zuschauern, obwohl
talente.html fuer ihn gesperrt ist -- Begruendung dort: "Hier stehen
Namen von Menschen, die nichts davon wissen."

Aus der Liste wurde eine Zuordnung Brett -> Seite, gefragt wird
darfSeite(). Nachgemessen: modi/talente 404, modi/entwicklung darf,
hand+admin unveraendert.

Dazu stand in workspace-bereiche.js ein Kommentar, der das GEGENTEIL des
Codes behauptete ("entwicklung, talente bleiben 404"). Ein falscher
Kommentar an einer Rechtestelle ist schlimmer als keiner.

6. ZWEI KACHELN "CHAT", BEIDE AUF DIESELBE SEITE.

Seit dem Treff-Chat von gestern sahen Modi und rechte Hand zweimal
"Chat" mit demselben Ziel. Jetzt "Treff-Chat" mit ?raum=treff, das den
Raum direkt oeffnet -- gesucht an denselben zwei Merkmalen, an denen ihn
der Rest der Datei erkennt.

7. VERTRAUEN: "WIE GEHT'S DIR?" VERSPRACH MEHR, ALS ES HALTEN KANN.

Dort stand: "Niemand aus dem Team sieht deine Antworten -- auch
DogFather nicht." Punkt. Fast wahr und deshalb gefaehrlich: Aus
denselben Antworten entsteht eine namenlose Ampel fuer die Leitung.

Der ehrliche Satz war laengst geschrieben und wurde vom Server sogar
mitgeliefert (block.text) -- nur hat ihn nie jemand angezeigt. Er steht
jetzt im HTML, nicht im Skript: Wer die Seite oeffnet, soll ihn lesen,
BEVOR er tippt.

8. DIE NEUE AUSWERTUNG, AUS BENUTZERSICHT NACHGEBESSERT.

- Der Streifen hatte keine Zeichenerklaerung. Im Dateikopf stand "wer
  Farben schlecht unterscheidet, liest dasselbe Zeichen" -- das stimmt
  nur, wenn irgendwo steht, was die Zeichen heissen. Jetzt eine Legende,
  aus `stufen` gebaut statt abgeschrieben.
- "nie gesetzt" war ein Mittelpunkt, "Kann ich nicht sagen" ein
  Halbgeviertstrich. Zwei kurze Striche nebeneinander fuer den
  Unterschied zwischen "niemand hat hingesehen" und "jemand hat
  hingesehen und konnte es nicht sagen". Leer ist jetzt wirklich leer,
  mit gestricheltem Rahmen.
- Die Aufschluesselung der Balken stand nur im Mausueberfahren -- am
  Handy also nie, und dort steht jemand damit im Stream. Jetzt als Text.
- Die Modi-Sicht bekam Balken ohne Erklaerung, ein "Frag danach" ohne
  Adressaten und bei fehlender Bewegung ein LEERES Element. Jetzt
  Lesehilfe, ein Leerkasten mit Satz und zwei Wege weiter.

9. NEBENBEFUNDE, DIE DIE PRUEFUNG GEFUNDEN HAT.

pruef-css-klassen war schon VOR dieser Arbeit rot (nachgemessen mit
git stash). Alle drei Befunde behoben:
  - hilfe.html und unsere-seiten.html luden ihre Seitendatei NACH
    module.css und haus.css und ueberschrieben damit das Haus.
  - Zwei Schriftgroessen unter 11,5 px aus dem gestrigen Bau
    (.71rem Monatsbeschriftung, .68rem Marke) auf .75 und .72 gehoben.
  - Die Pruefung selbst war blind fuer <style>-Bloecke in der Seite und
    meldete deshalb dauerhaft drei Fehler an einer Seite, die ihre
    Stile mit Absicht selbst traegt. Eine Warnung, die immer kommt, ist
    keine Warnung mehr -- jetzt sieht sie hin, statt die Seite
    auszunehmen.

Kleinkram nebenbei: "die Antworten sieht nur du" -> "siehst nur du" auf
der Kachel zur empfindlichsten Seite des Hauses. "Bewertet wird dort" ->
"Eingeschaetzt wird dort" in teamlage.html, zwei Zeilen unter "keine
Bewertung von Menschen". "1 Aufgabe(n) angelegt" ausgeschrieben.

GEMESSEN: pruef-css-klassen ALLES IN ORDNUNG (vorher 3 Fehler),
pruef-meldungen 8/0, pruef-rechtetafel 19/0, alle Module laden,
keine doppelten Kachelnamen/-ziele/-farben mehr in allen vier Rollen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-19 14:57:55 +02:00
DogFatherGitandClaude Opus 5 461d41943a Keine Maschinensprache mehr auf dem Bildschirm
Filipe: "keine kryptischen oder technischen fehlermeldungen, sondern
klare aussagen darueber, was passiert ist und wie man das problem
loesen kann."

DAS PROBLEM, NACHGEMESSEN.

Der Server antwortet im Fehlerfall mit { fehler: "..." }. Darin stehen
ZWEI verschiedene Dinge, und von aussen sehen sie gleich aus:
Maschinenkennungen (nicht_verfuegbar, nicht_gefunden, ungueltig -- 43
verschiedene) und fertige deutsche Saetze.

An 84 Stellen stand `textContent = d.fehler` -- ungefiltert. Wer beim
Hochladen einer Datei Pech hatte, las woertlich "nicht_verfuegbar" auf
dem Bildschirm und wusste nicht einmal, ob er selbst schuld war.

Gezaehlt: 474 Antworten mit "nicht_verfuegbar", 392 mit
"nicht_gefunden", 125 mit "ungueltig".

DREI AUSGAENGE, NICHT ZWEI.

`sagWas()` in workspace/assets/js/meldung.js:
  bekannte Kennung   -> ihr Satz
  unbekannte Kennung -> der Ersatzsatz der Stelle, NIE die Kennung
                        (sie geht in die Konsole, wo sie jemandem
                        auffaellt, der sie beheben kann)
  fertiger Satz      -> unveraendert durch

Erkannt wird eine Kennung daran, dass sie nur aus Kleinbuchstaben,
Ziffern und Unterstrichen besteht. Ein deutscher Satz hat immer
Leerzeichen; eine Kennung nie. Die Unterscheidung ist entschieden,
nicht geraten.

UND WARUM DIE TABELLE NICHT ALTERN KANN.

Eine von Hand gepflegte Liste ist hier schon zweimal teuer geworden
(die abgeschriebene Spaltenliste, die feste Umbruchschwelle). Deshalb
rechnet pruef-meldungen.mjs die Kennungen AUS DEM SERVER aus statt sie
zu kennen -- eine neue ohne Satz macht sie rot. Man kann es nicht mehr
vergessen.

Sie prueft drei Dinge und beweist zu jedem, dass sie auch "nicht in
Ordnung" sagen kann:
  1. vollstaendig -- jede Kennung hat einen Satz
  2. angeschlossen -- keine Rohanzeige, und jede Seite, deren Skript
     sagWas benutzt, laedt auch meldung.js
  3. lesbar -- kein Satz enthaelt Kennung, Zahl oder englisches Wort

GEFUNDEN HAT SIE SOFORT EINEN ECHTEN FEHLER: leistung.html laedt seine
Skripte ohne `defer` und fiel damit durch meinen Einbau. Dort waere
sagWas nicht definiert gewesen -- aus einer Fehlermeldung waere ein
Absturz geworden, also schlimmer als vorher.

NEBENBEFUND, DER MICH FAST EINE FALSCHE MELDUNG GEKOSTET HAETTE:
"alter_offen" heisst NICHT "etwas Aelteres ist offen", sondern
"Altersbestaetigung steht noch aus" (workspace.js:4864). Geraten
haette ich es falsch uebersetzt.

GEMESSEN: pruef-meldungen 8/0, alle 46 Skripte syntaktisch in Ordnung.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-19 14:46:13 +02:00
91 changed files with 2138 additions and 608 deletions
+17
View File
@@ -460,3 +460,20 @@ export function crewWeiche(req, res, next) {
if (istWorkspacePfad(req.path) || req.path.startsWith("/assets/")) return next();
return res.redirect(302, "/workspace/");
}
/** WO SICH JEMAND MIT DIESER ROLLE ANMELDET (19.09.2026).
*
* ANLASS: Beim Anlegen eines Zugangs zeigt die Verwaltung Name und
* Code -- aber nirgends, WELCHEN Link man dazuschicken muss. DogFather
* ruft den Code durchs Zimmer und der andere fragt "und wo?".
* Ausgerechnet der Kasten, der sagt "jetzt weitergeben", gab die
* Haelfte nicht mit, die man weitergeben muss.
*
* ABGELEITET AUS DENSELBEN MENGEN wie die Weiche selbst -- nicht aus
* einer zweiten Liste. Wer eine Rolle nach TEAM_DOGI_ROLLEN oder
* AUSSEN_ROLLEN verschiebt, verschiebt die Adresse mit. */
export function anmeldeAdresseFuer(rolle) {
return (TEAM_DOGI_ROLLEN.has(rolle) || AUSSEN_ROLLEN.has(rolle))
? CREW_ADRESSE
: "https://workspace.dogfather-universe.com";
}
+23 -1
View File
@@ -198,7 +198,29 @@ const ungestaltet = []; // (a) -- nur zur Ansicht
for (const seite of seiten) {
const html = readFileSync(join(WS, seite), "utf8");
const css = stylesheetsVon(html).map((p) => cssMitImporten(p)).join("\n");
/* DIE SEITE KANN IHRE STILE AUCH SELBST TRAGEN (ergaenzt 19.09.2026).
Bis heute las diese Pruefung nur die verlinkten .css-Dateien. Ein
<style>-Block in der Seite war fuer sie unsichtbar -- und damit
galt alles, was dort gestaltet ist, als "gestaltet, aber nicht auf
ihrer Seite".
Das traf genau EINE Seite, und zwar eine, die es mit Absicht so
macht: anruf-probe.html haelt ihre Stile in der Seite, damit sie
auch dann noch etwas anzeigt, wenn genau das schiefgeht, was sie
untersucht (eine Stilvorlage aus dem Zwischenspeicher). Die
Pruefung meldete diese Entscheidung als Fehler -- drei Eintraege
lang, bei jedem Lauf.
Eine Warnung, die immer kommt, ist keine Warnung mehr: Man
gewoehnt sich an die drei roten Zeilen und uebersieht ab da auch
die vierte, die echt ist. Deshalb sieht die Pruefung jetzt hin,
statt die Seite auszunehmen -- eine Ausnahmeliste waere die
naechste Stelle, die veraltet. */
const inline = [...html.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/g)]
.map((m) => m[1]).join("\n");
const css = stylesheetsVon(html).map((p) => cssMitImporten(p)).join("\n")
+ "\n" + inline;
const definiert = definierteKlassen(css);
const benutzt = klassenAusHtml(html);
+219
View File
@@ -0,0 +1,219 @@
/* =====================================================================
pruef-meldungen.mjs — Kein Benutzer liest je eine Maschinenkennung.
Filipe, 19.09.2026: „keine kryptischen oder technischen
Fehlermeldungen, sondern klare Aussagen darüber, was passiert ist
und wie man das Problem lösen kann."
---------------------------------------------------------------------
WARUM DIESE PRÜFUNG DIE EIGENTLICHE ARBEIT IST
Die Übersetzungstabelle in `workspace/assets/js/meldung.js` zu
schreiben war eine Stunde Arbeit. Sie in einem halben Jahr noch
vollständig zu haben, ist die schwierigere Aufgabe — und zwar aus
einem Grund, den dieses Haus teuer gelernt hat:
„Eine abgeschriebene Liste ist dieselbe Falle wie eine feste Zahl."
(11.09.2026, als die Umstellung drei Spalten verlor, obwohl ein
Kommentar direkt daneben davor warnte.)
Wer morgen `fehler: "zu_frueh"` erfindet, wird nicht daran denken,
hier einen Satz nachzutragen. Er merkt es auch nicht: Die Oberfläche
zeigt dann klaglos den Ersatzsatz. Es sieht aus, als funktioniere es.
Deshalb rechnet diese Prüfung die Liste der Kennungen AUS DEM SERVER
aus, statt sie zu kennen. Eine neue Kennung ohne Satz macht sie rot.
Man KANN es nicht mehr vergessen.
---------------------------------------------------------------------
DREI DINGE WERDEN GEPRÜFT
1. VOLLSTÄNDIG — jede Kennung, die der Server schicken kann, hat
einen Satz.
2. ANGESCHLOSSEN — keine Seite zeigt `.fehler` noch roh an, und
jede Seite, deren Skript `sagWas` benutzt, lädt auch
`meldung.js`. (Der zweite Teil ist der wichtigere: Ohne die
Datei wäre `sagWas` nicht definiert, und aus einer Fehlermeldung
würde ein Absturz — schlimmer als vorher.)
3. LESBAR — kein Satz enthält eine Kennung, eine nackte Zahl oder
ein englisches Wort.
Dazu drei GEGENPROBEN. Eine Prüfung, die immer bestätigt, bestätigt
nichts (01.09.2026): Jede der drei muss beweisen, dass sie auch
„nicht in Ordnung" sagen kann.
AUFRUF: node server/pruef-meldungen.mjs
Läuft in unter einer Sekunde, ohne Server, ohne Browser.
===================================================================== */
import { readFileSync, readdirSync, existsSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const HIER = dirname(fileURLToPath(import.meta.url));
const WURZEL = join(HIER, "..");
const JS = join(WURZEL, "workspace", "assets", "js");
const SEITEN = join(WURZEL, "workspace");
let ok = 0, fehl = 0;
const pruef = (name, bedingung, zusatz = "") => {
if (bedingung) { ok++; console.log(` OK ${name}`); }
else { fehl++; console.log(` FEHL ${name}${zusatz ? " — " + zusatz : ""}`); }
};
/* ---------------------------------------------------------------------
Die Tabelle aus meldung.js lesen — ohne einen Browser zu bauen.
`meldung.js` hängt sich an `window`. Statt eine halbe Browserwelt
nachzustellen, wird der Block zwischen `var SAETZE = {` und der
schließenden Klammer ausgeschnitten und als JavaScript ausgewertet.
Das ist ehrlicher als ein zweiter Auszug der Namen: Es ist dieselbe
Quelle, kein abgeschriebener Zwilling.
------------------------------------------------------------------- */
function saetzeLesen() {
const quelle = readFileSync(join(JS, "meldung.js"), "utf8");
const start = quelle.indexOf("var SAETZE = {");
if (start < 0) return null;
const auf = quelle.indexOf("{", start);
let tiefe = 0, ende = -1;
for (let i = auf; i < quelle.length; i++) {
if (quelle[i] === "{") tiefe++;
else if (quelle[i] === "}") { tiefe--; if (tiefe === 0) { ende = i; break; } }
}
if (ende < 0) return null;
// eslint-disable-next-line no-new-func
return Function(`"use strict"; return (${quelle.slice(auf, ende + 1)});`)();
}
/* ---------------------------------------------------------------------
Alle Kennungen, die der Server schicken kann.
ABGELEITET, NICHT ABGESCHRIEBEN. Gesucht wird `fehler: "…"` in allen
Server-Dateien; als Kennung gilt, was nur aus Kleinbuchstaben,
Ziffern und Unterstrichen besteht. Alles andere ist ein fertiger
Satz und braucht keine Übersetzung.
------------------------------------------------------------------- */
function kennungenAusServer() {
const gefunden = new Map();
for (const datei of readdirSync(HIER)) {
if (!datei.endsWith(".js")) continue;
if (datei.startsWith("pruef-") || datei.startsWith("test-")) continue;
const text = readFileSync(join(HIER, datei), "utf8");
for (const m of text.matchAll(/fehler:\s*"([a-z0-9_]+)"/g)) {
if (!gefunden.has(m[1])) gefunden.set(m[1], datei);
}
}
return gefunden;
}
console.log("\n=== Meldungen: sagt das Haus ganze Sätze? ===\n");
const SAETZE = saetzeLesen();
/* DER DRITTE AUSGANG. Lässt sich die Tabelle nicht lesen, ist das
weder „in Ordnung" noch „ein Satz fehlt" — es heißt, die Prüfung
kann ihre Frage nicht beantworten. Dann bricht sie ab, statt grün
zu melden. Genau dieser Fall hat am 03.09. wochenlang eine
Pflichtprüfung wertlos gemacht. */
if (!SAETZE) {
console.log(" KONNTE NICHT NACHSEHEN — in meldung.js ist kein Block `var SAETZE = {` zu finden.");
console.log(" Grund: entweder wurde die Datei umgebaut oder sie fehlt.");
console.log(" Ohne sie lässt sich nicht sagen, ob Sätze fehlen. Abbruch.\n");
process.exit(2);
}
const kennungen = kennungenAusServer();
console.log(` (${kennungen.size} Kennungen im Server, ${Object.keys(SAETZE).length} Sätze in der Tabelle)\n`);
/* ---- 1. Vollständig ------------------------------------------------- */
const ohneSatz = [...kennungen.keys()].filter((k) => !SAETZE[k]);
pruef("Jede Server-Kennung hat einen Satz",
kennungen.size > 0 && ohneSatz.length === 0,
ohneSatz.length ? `ohne Satz: ${ohneSatz.map((k) => `${k} (${kennungen.get(k)})`).join(", ")}` : "");
/* Die Zahl gehört in die BEDINGUNG, nicht nur in den Meldetext.
`every()` auf einem leeren Feld liefert `true` — eine Prüfung, die
nichts gefunden hat, wäre sonst grün (01.09.2026, Shop). */
pruef("Es wurden überhaupt Kennungen gefunden", kennungen.size >= 30,
`nur ${kennungen.size} — findet der Suchausdruck noch, was er soll?`);
/* ---- 2. Angeschlossen ----------------------------------------------- */
const skripte = readdirSync(JS).filter((f) => f.endsWith(".js"));
/* 2a: Keine Rohanzeige mehr. Gesucht wird `.fehler` in derselben Zeile
wie eine Ausgabe — aber nur dort, wo KEIN `sagWas` und kein
Vergleich (`===`) steht. Ein Vergleich übersetzt selbst. */
const roh = [];
for (const f of skripte) {
if (f === "meldung.js") continue;
const zeilen = readFileSync(join(JS, f), "utf8").split("\n");
zeilen.forEach((z, i) => {
if (!/\.fehler/.test(z)) return;
if (!/(textContent\s*=|melde[n]?\(|alert\(|innerHTML\s*=)/.test(z)) return;
if (/sagWas/.test(z) || /\.fehler\s*===/.test(z) || /\[\s*\w+\.fehler\s*\]/.test(z)) return;
roh.push(`${f}:${i + 1}`);
});
}
pruef("Keine Seite zeigt eine Kennung roh an", roh.length === 0,
roh.length ? roh.join(", ") : "");
/* 2b: Wer sagWas benutzt, muss meldung.js laden. DAS IST DIE
WICHTIGERE HÄLFTE: Fehlt die Datei, ist `sagWas` nicht definiert,
und aus einer Fehlermeldung wird ein Absturz. */
const benutzt = new Set(skripte.filter(
(f) => f !== "meldung.js" && /\bsagWas\(/.test(readFileSync(join(JS, f), "utf8"))));
const luecken = [];
for (const seite of readdirSync(SEITEN).filter((f) => f.endsWith(".html"))) {
const html = readFileSync(join(SEITEN, seite), "utf8");
const geladen = [...html.matchAll(/src="assets\/js\/([a-z0-9-]+)\.js/g)].map((m) => m[1] + ".js");
const braucht = geladen.filter((g) => benutzt.has(g));
if (braucht.length && !geladen.includes("meldung.js")) {
luecken.push(`${seite} (braucht es wegen ${braucht.join(", ")})`);
}
}
pruef("Jede Seite, die sagWas braucht, lädt meldung.js",
benutzt.size > 0 && luecken.length === 0,
luecken.length ? luecken.join("; ") : (benutzt.size ? "" : "keine Datei benutzt sagWas — stimmt das?"));
/* ---- 3. Lesbar ------------------------------------------------------ */
const schlecht = [];
for (const [k, satz] of Object.entries(SAETZE)) {
if (/[a-z]+_[a-z]+/.test(satz)) schlecht.push(`${k}: enthält eine Kennung`);
else if (/\b(?:400|401|403|404|409|429|500|503)\b/.test(satz)) schlecht.push(`${k}: enthält eine Fehlernummer`);
else if (/\b(error|failed|invalid|denied|request|server error)\b/i.test(satz)) schlecht.push(`${k}: englisches Wort`);
else if (satz.length < 15) schlecht.push(`${k}: zu kurz ("${satz}")`);
}
pruef("Jeder Satz ist deutsch, ohne Kennung und ohne Zahl",
Object.keys(SAETZE).length > 0 && schlecht.length === 0,
schlecht.length ? schlecht.join("; ") : "");
/* =====================================================================
DIE GEGENPROBEN
Zu jeder Prüfung, die „alles in Ordnung" sagen kann, gehört der
Beweis, dass sie auch „nicht in Ordnung" sagen kann. Sonst weiß
niemand, ob sie überhaupt hinsieht.
===================================================================== */
console.log("\n --- Gegenproben (jede MUSS anschlagen) ---");
const g1 = (() => { const t = { ...SAETZE }; delete t.nicht_verfuegbar;
return [...kennungen.keys()].filter((k) => !t[k]).length > 0; })();
pruef("Gegenprobe: fehlt ein Satz, schlägt Prüfung 1 an", g1);
const g2 = (() => {
const z = " $('x').textContent = d.fehler || 'Ging nicht.';";
return /\.fehler/.test(z)
&& /(textContent\s*=|melde[n]?\(|alert\(|innerHTML\s*=)/.test(z)
&& !/sagWas/.test(z) && !/\.fehler\s*===/.test(z);
})();
pruef("Gegenprobe: eine Rohanzeige würde Prüfung 2a finden", g2);
const g3 = (() => {
const s = "Fehler 503: request denied";
return /[a-z]+_[a-z]+/.test(s)
|| /\b(?:400|401|403|404|409|429|500|503)\b/.test(s)
|| /\b(error|failed|invalid|denied|request)\b/i.test(s);
})();
pruef("Gegenprobe: ein unlesbarer Satz würde Prüfung 3 finden", g3);
console.log(`\n ${ok} bestanden, ${fehl} fehlgeschlagen\n`);
process.exit(fehl ? 1 : 0);
+32 -3
View File
@@ -25,6 +25,20 @@ import { ARBEIT_START } from "./workspace-arbeit-start.js";
derselben Regel laufen auseinander, und dann behauptet die Karte
etwas, das der Kalender nicht zeigt. */
import { terminVon } from "./workspace-termin-regel.js";
/* OB EIN WEG UEBERHAUPT OFFEN STEHT -- gefragt werden die BEIDEN
Schranken, die es gibt, und zwar hier am Server statt im Browser.
Anlass (19.09.2026): Auf jeder Brettkarte mit Termin stand "Im
Kalender am 24.09." als Link. Fuer die Community fuehrt er ins
Leere -- `kalender.html` steht in der Rechtetafel auf ALLE, und
ALLE ist ausdruecklich OHNE "gast". Ein Klick landete wortlos auf
der Startseite.
Die Regel im Browser nachzubauen waere die vierte Stelle, an der
dieselbe Frage beantwortet wird (Kachel, Rechtetafel, Adressregel
-- und dann der Link). Die vierte ist immer die, die etwas
durchlaesst. */
import { darfSeite } from "./rechte.js";
/* EIN KATALOG AUS ZWEI QUELLEN (17.09.2026)
@@ -46,9 +60,10 @@ const STARTKATALOG = { ...ARBEIT_START, ...TREFF_START };
import {
db, protokolliere, echteIp, sitzungLesen, betreutWo, darfCreator, betreuteIds, istLeitung, istDogFather, siehtAlles, istSpicy, ohneDogFather,
externPruefen, externSql,
ohneTeamDogi, ohneAgentur, siehtModis, fuehrtTeamDogi, MODI_BEREICHE_ERLAUBT, TEAM_DOGI_ROLLEN,
ohneTeamDogi, ohneAgentur, siehtModis, fuehrtTeamDogi, MODI_BEREICHE_ERLAUBT, brettHinterSeiteOffen, TEAM_DOGI_ROLLEN,
TREFF_BRETTER, TREFF_ROLLEN, AUSSEN_ROLLEN,
heuteLokal,
gehoertAufDieseAdresse,
} from "./workspace.js";
import { nachrichtSchicken } from "./workspace-chat.js";
import {
@@ -767,7 +782,13 @@ bereicheRouter.use("/workspace/api/bereich/:bereich", (req, res, next) => {
if (TREFF_BRETTER.includes(b) && !TREFF_ROLLEN.has(req.person?.rolle)) return nein();
if (!TEAM_DOGI_ROLLEN.has(req.person?.rolle)) return next();
/* Bretter mit eigener Kachel: wer die Kachel hat, darf hinein. */
if (MODI_BEREICHE_ERLAUBT.has(b)) return next();
/* Bretter HINTER einer Seite (entwicklung, talente): nur fuer den,
der die Seite selbst oeffnen darf. Bis zum 19.09.2026 stand hier
eine feste Liste ohne Rollenfrage -- ein Modi kam damit an die
Notizen zu Kandidaten, obwohl talente.html fuer ihn gesperrt ist. */
if (brettHinterSeiteOffen(req.person, b)) return next();
return nein();
});
@@ -970,7 +991,8 @@ function sichtbarRoh(person) {
um Sichtbarkeit von Eintraegen geht -- und nirgends sonst.
UND ES OEFFNET NICHTS: Welche Bereiche ein Modi ueberhaupt
aufrufen darf, entscheidet MODI_BEREICHE_ERLAUBT weiter oben
aufrufen darf, entscheidet der Riegel weiter oben
(MODI_BEREICHE_ERLAUBT plus brettHinterSeiteOffen)
(content, schutz, agentur, entwicklung, talente bleiben 404).
Die Agentur-Trennung haengt an ohneAgentur() und AGENTUR_ROLLEN
-- dort steht "admin" nicht drin, an ihr aendert sich also
@@ -1399,6 +1421,13 @@ bereicheRouter.get("/workspace/api/bereich/:bereich", (req, res) => {
res.json({
bereich, einstellung, treff: treffBrett, gehoert, weiter,
/* DARF DIESER MENSCH IN DEN KALENDER? Beantwortet von den zwei
Stellen, die es entscheiden -- nicht von einer dritten Regel
im Browser. Die Karte zeigt ihren Termin dann als Text statt
als Link; die Auskunft bleibt, nur der Weg ins Leere faellt
weg. */
kalender_offen: darfSeite(req.person, "/workspace/kalender.html")
&& gehoertAufDieseAdresse(req.person, "/workspace/kalender.html"),
eintraege: treffBrett ? eintraege.map(mitRollenname) : eintraege,
});
} catch (fehler) {
@@ -1498,7 +1527,7 @@ function pruefe(bereich, körper, { neu }) {
if (neu || körper.titel !== undefined) {
const t = String(körper.titel ?? "").trim();
if (t.length < 2) fehler.push("Titel fehlt.");
if (t.length < 2) fehler.push("Die Überschrift fehlt.");
else if (t.length > TITEL_MAX) fehler.push("Titel ist zu lang.");
else aus.titel = t;
}
+10 -3
View File
@@ -14,6 +14,10 @@
===================================================================== */
import express from "express";
/* WO SICH DIESE ROLLE ANMELDET -- aus derselben Datei, die auch die
Weiche steuert. Eine zweite Liste von Adressen waere die, die beim
naechsten Umzug stehen bleibt. */
import { anmeldeAdresseFuer } from "./crew-adresse.js";
import {
db, protokolliere, echteIp, sitzungLesen, personAnlegen, codeNeu, sitzungToken, personSperren, betreuungSetzen, scoutZuteilungSetzen, istLeitung, istDogFather, siehtModis, siehtAlles, ROLLEN_SORTIERUNG, ROLLEN_REIHE, istSpicy, verborgeneIds, TEAM_DOGI_ROLLEN, darfAnlegen, darfRollenWechseln, hausBedingung,
} from "./workspace.js";
@@ -605,6 +609,7 @@ personenRouter.post("/workspace/api/creator-anlegen", gleicheHerkunft, nurLeitun
/* Der Code steht NUR hier in der Antwort. Danach ist er weg. */
res.status(201).json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code,
adresse: anmeldeAdresseFuer(neu.rolle),
betreuer_id: betreuerId });
} catch (fehler) {
console.error("[workspace] Creator anlegen:", fehler?.message);
@@ -675,7 +680,8 @@ function teamTuer(rolle) {
if (vorhanden) return res.status(409).json({ fehler: "Diesen Namen gibt es schon." });
const neu = personAnlegen(name, rolle, { ...person, ip: echteIp(req) });
res.status(201).json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code });
res.status(201).json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code,
adresse: anmeldeAdresseFuer(neu.rolle) });
} catch (fehler) {
console.error(`[workspace] ${rolle} anlegen:`, fehler?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
@@ -785,7 +791,8 @@ personenRouter.post("/workspace/api/verwaltung/personen", gleicheHerkunft, (req,
const neu = personAnlegen(name, rolle, { ...req.person, ip: echteIp(req) });
/* Der Code steht NUR hier in der Antwort. Danach ist er weg. */
res.status(201).json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code });
res.status(201).json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code,
adresse: anmeldeAdresseFuer(neu.rolle) });
} catch (fehler) {
console.error("[workspace] Person anlegen:", fehler?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
@@ -813,7 +820,7 @@ personenRouter.post("/workspace/api/verwaltung/personen/:id/code", gleicheHerkun
}
const neu = codeNeu(id, { ...req.person, ip: echteIp(req) },
eigener ? sitzungToken(req) : null);
res.json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code });
res.json({ id: neu.id, name: neu.name, rolle: neu.rolle, code: neu.code, adresse: anmeldeAdresseFuer(neu.rolle) });
} catch (fehler) {
if (/Keine Person/.test(fehler?.message || "")) {
return res.status(404).json({ fehler: "nicht_gefunden" });
+15 -1
View File
@@ -345,7 +345,21 @@ zentraleRouter.get("/workspace/api/zentrale", (req, res) => {
return res.json({
art: "tag",
titel: dran ? "Heute geschafft" : "Heute nichts offen",
prozent: dran ? Math.round((heuteFertig / dran) * 100) : 100,
/* KEINE 100 %, WENN ES NICHTS ZU TUN GAB (19.09.2026).
Hier stand `prozent: 100` fuer den Fall "nichts offen". Der
Titel sagt es zwar in Worten -- gelesen wird aber zuerst die
Zahl in der Mitte, und 100 % heisst fuer jeden Menschen
"fertig", nicht "leer". Am ersten Tag, ohne eine einzige
zugeteilte Aufgabe, stand damit das groesste Element der
Startseite auf "100 % -- HEUTE NICHTS OFFEN".
Derselbe Fehler wurde am 17.09. schon einmal behoben, nur
andersherum ("0 % TAG GESCHAFFT"). Und die Community-Antwort
zwanzig Zeilen weiter oben macht es bereits richtig: `mitte`
mit einem Strich statt einer Zahl, die nichts misst. */
mitte: dran ? undefined : "–",
prozent: dran ? Math.round((heuteFertig / dran) * 100) : 0,
segmente,
zahlen: [
/* "TERMINE HEUTE", NICHT NUR "HEUTE" (17.09.2026).
+163 -18
View File
@@ -910,7 +910,7 @@ const RECHTE_KACHEL = {
einfacher -- sie tut nur noch eine Sache. */
const BEFINDEN_KACHEL = {
gruppe: "Für dich", gruppeUnter: "Dein Platz im Team",
name: "Wie geht's dir?", unter: "Ein paar Fragen – die Antworten sieht nur du",
name: "Wie geht's dir?", unter: "Ein paar Fragen – die Antworten siehst nur du",
zeichen: "steckbrief", ton: 37, ziel: "befinden.html", szene: "lounge",
};
@@ -1006,8 +1006,23 @@ const TREFF_BEREICHE = [
TON 41: gezaehlt in start.css, nicht in dieser Datei. 40 war die
hoechste (siehe die Warnung an WERDEGANG_KACHEL, dort ist genau
dieser Fehler heute schon einmal passiert). */
{ ...TREFF_GRUPPE, name: "Chat", unter: "Miteinander reden – nachts schläft er",
zeichen: "chat", ton: 41, ziel: "chat.html", szene: "lounge" },
/* „TREFF-CHAT“ UND NICHT NUR „CHAT“ (nachgeschärft 19.09.2026).
Gefunden beim Durchgehen aus Benutzersicht: Ein Modi und die
rechte Hand sahen ZWEI Kacheln mit demselben Namen „Chat“, beide
mit demselben Ziel `chat.html` -- eine unter „Täglich“ (das Team),
eine hier unter „Community“. Sie taten auch dasselbe: Man landete
in der Gesprächsliste und musste den richtigen Raum suchen.
Zwei gleich benannte Wege zum selben Ort sind schlimmer als ein
fehlender -- man klickt den falschen und sucht den Unterschied.
Jetzt trägt sie ihren eigenen Namen UND ihr eigenes Ziel:
`?raum=treff` öffnet den Raum der Community direkt. Für die
Community selbst ändert sich am Namen nichts Wesentliches -- sie
hat nur diese eine. */
{ ...TREFF_GRUPPE, name: "Treff-Chat", unter: "Miteinander reden – nachts schläft er",
zeichen: "chat", ton: 41, ziel: "chat.html?raum=treff", szene: "lounge" },
{ ...TREFF_GRUPPE, name: "Anschlagbrett", unter: "Was gerade gilt",
zeichen: "reports", ton: 33, ziel: "bereich.html?b=anschlag", szene: "halle" },
{ ...TREFF_GRUPPE, name: "Was ansteht", unter: "Die n\u00e4chsten Streams und Events",
@@ -1017,7 +1032,26 @@ const TREFF_BEREICHE = [
{ ...TREFF_GRUPPE, name: "Highlights", unter: "Eure Clips und Bilder",
zeichen: "live", ton: 29, ziel: "bereich.html?b=highlight", szene: "arena" },
{ ...TREFF_GRUPPE, name: "Regeln & Hilfe", unter: "Wie es hier l\u00e4uft",
zeichen: "schutz", ton: 28, ziel: "bereich.html?b=regeln", szene: "studio" },
/* ZEIGT JETZT AUF DIE SEITE, DIE DIE ANTWORT HAT (19.09.2026).
Sie fuehrte auf `bereich.html?b=regeln` -- ein Brett, auf das die
Leitung Regeln eintragen KANN und das am ersten Tag leer ist
("Hier stehen die Regeln des Treffs -- sobald sie eingetragen
sind.").
Die Regeln, die es WIRKLICH gibt, stehen auf `treff-regeln.html`:
Begruessung, die fuenf Regeln, die drei Stufen, das Mindestalter,
was bei Aerger zu tun ist. Diese Seite war von genau EINER Stelle
aus erreichbar -- einem kleinen Textlink unter der Unterzeile
einer Brettseite. Keine der elf Kacheln fuehrte hin.
Damit war die einzige "So laeuft das hier"-Seite des Hauses fuer
einen neuen Zuschauer praktisch unsichtbar, waehrend die Kachel,
die ihren Namen trug, auf eine leere Liste zeigte.
Das Brett gibt es weiter; treff-regeln.html verlinkt darauf. Ein
Ort fuer die Frage, nicht zwei. */
zeichen: "schutz", ton: 28, ziel: "treff-regeln.html", szene: "studio" },
{ ...TREFF_GRUPPE, name: "Mitmachen", unter: "Wie du dazugeh\u00f6ren kannst",
zeichen: "personen", ton: 32, ziel: "bereich.html?b=mitmachen", szene: "garage" },
/* UNSERE SEITEN (19.09.2026) -- die einzige Kachel, die HINAUSFUEHRT.
@@ -1116,7 +1150,7 @@ export const TREFF_ROLLEN = new Set(["gast", "modi", "hand", "admin"]);
sich aus der Liste, die Sortierung in start.js fasst beide ans Ende. */
const HILFE_KACHEL = {
gruppe: "Für dich", gruppeUnter: "Deine eigene Seite",
name: "Vertraulich melden", unter: "Probleme, die niemand sonst sieht",
name: "Vertraulich melden", unter: "Nur DogFather und die rechte Hand",
zeichen: "schutz", ton: 39, ziel: "hilfe.html", szene: "studio",
};
@@ -1129,7 +1163,54 @@ const HILFE_KACHEL = {
Zusammengesetzt und nicht in MODI_BEREICHE hineingeschrieben, weil
diese Liste weiter oben steht als die Kacheln des Treffs. Eine Kopie
dort waere die Stelle, an der beim naechsten neuen Brett eines fehlt. */
const MODI_MIT_TREFF = [...MODI_BEREICHE, BEFINDEN_KACHEL, ...TREFF_BEREICHE,
/* =====================================================================
WAS DER MODI UEBER SICH SELBST SIEHT (19.09.2026)
GEFUNDEN BEIM DURCHGEHEN DER SEITE AUS BENUTZERSICHT, und es war
mein eigener Fehler vom Vortag: `entwicklung.html` und
`werdegang.html` haben BEIDE eine ausgearbeitete Eigensicht --
entwicklung.js zeigt die eigene Karte, werdegang.js die eigene
Bewegung. Die Rechtetafel laesst den Modi auf beide Seiten
(rechte.js).
Nur: In seiner Kachelliste stand keine von beiden. Der Weg dorthin
existierte fuer ihn nicht -- er haette die Adresse von Hand tippen
muessen. Die ganze Eigensicht war damit toter Code, ausgerechnet
fuer den Menschen, fuer den sie geschrieben wurde.
ZWEI KACHELN UND NICHT DIE DER LEITUNG. Dieselben Ziele, aber die
Leitungstexte passen nicht: "Wie sich jemand ueber die Zeit bewegt"
liest sich, als ginge es um andere Leute. Aus seiner Sicht geht es
um ihn.
UND SIE LOESEN EINE NAMENSKOLLISION. Bis heute setzten BEIDE Seiten
fuer ihn die Ueberschrift "Deine Entwicklung" -- wer aus der Glocke
kam, konnte nicht sagen, auf welcher er stand. Jetzt heisst der
Katalog "Deine Aufgaben" (passend zur Kachel "Eure Aufgaben") und
die Auswertung "Deine Entwicklung". Ein Name, eine Sache.
ABGELEITET, NICHT ABGESCHRIEBEN: Ziel, Zeichen und Ton kommen aus
den Kacheln der Leitung. Wer dort das Ziel aendert, aendert es hier
mit -- zwei Kachelobjekte mit demselben Ziel waeren sonst zwei
Gelegenheiten, eines davon zu vergessen. */
const FUER_DICH = {
gruppe: "Für dich", gruppeUnter: "Deine Seite im Team",
};
const MEINE_AUFGABEN_KACHEL = {
...FUER_DICH,
zeichen: ENTWICKLUNG_KACHEL.zeichen, ton: ENTWICKLUNG_KACHEL.ton,
ziel: ENTWICKLUNG_KACHEL.ziel, szene: ENTWICKLUNG_KACHEL.szene,
name: "Deine Aufgaben", unter: "Was du kannst – und was als Nächstes kommt",
};
const MEINE_ENTWICKLUNG_KACHEL = {
...FUER_DICH,
zeichen: WERDEGANG_KACHEL.zeichen, ton: WERDEGANG_KACHEL.ton,
ziel: WERDEGANG_KACHEL.ziel, szene: WERDEGANG_KACHEL.szene,
name: "Deine Entwicklung", unter: "Was sich bei dir bewegt hat – ohne Note",
};
const MODI_MIT_TREFF = [...MODI_BEREICHE, BEFINDEN_KACHEL,
MEINE_AUFGABEN_KACHEL, MEINE_ENTWICKLUNG_KACHEL, ...TREFF_BEREICHE,
TREFF_MODERATION_KACHEL];
const HAND_MIT_TREFF = [...HAND_BEREICHE, ...TREFF_BEREICHE,
TREFF_MODERATION_KACHEL, HILFE_KACHEL];
@@ -1283,20 +1364,49 @@ const AGENTUR_LISTE = new Set(["spicy", "admin", "manager", "scout", "creator"])
Bretter, zu denen eine erlaubte SEITE verlinkt. Wer ein drittes
dazunimmt, ohne es einzutragen, bekommt dasselbe 404 und findet
diesen Absatz. */
const BRETTER_UEBER_SEITEN = [
/* Beide sind von entwicklung.html bzw. talente.html aus verlinkt --
das Notizbrett neben dem Katalog. Wer die Seite oeffnen darf,
muss auch dorthin kommen. */
"entwicklung",
"talente",
];
/* Bretter, die kein eigenes Kachelziel haben, sondern von einer SEITE
aus verlinkt sind -- das Notizbrett neben dem Katalog.
export const MODI_BEREICHE_ERLAUBT = new Set([
...HAND_MIT_TREFF
AUS EINER LISTE WURDE EINE ZUORDNUNG (19.09.2026), und das war ein
echtes Loch, kein Schoenheitsfehler:
Der Satz darueber lautete "Wer die Seite oeffnen darf, muss auch
dorthin kommen." Der Gedanke ist richtig -- nur stand er als feste
Liste da und fragte nie, WER gerade fragt. Ergebnis: Ein Modi kam
ueber `bereich.html?b=talente` an die Notizen zu Kandidaten, obwohl
`talente.html` fuer ihn ausdruecklich gesperrt ist. Die Begruendung
der Sperre steht in rechte.js und gilt hier genauso: "Hier stehen
Namen von Menschen, die nichts davon wissen."
Schlimmer noch: In workspace-bereiche.js stand ein Kommentar, der
das Gegenteil des Codes behauptete ("entwicklung, talente bleiben
404"). Ein falscher Kommentar an einer Rechtestelle ist schlimmer
als gar keiner -- der Naechste liest ihn und prueft nicht nach.
Jetzt steht neben jedem Brett die Seite, von der es abhaengt, und
gefragt wird `darfSeite()` -- dieselbe Tafel, die auch die Seite
selbst bewacht. Eine zweite Regel kann damit nicht auseinanderlaufen. */
const BRETTER_UEBER_SEITEN = {
entwicklung: "/workspace/entwicklung.html",
talente: "/workspace/talente.html",
};
/** Bretter mit eigener Kachel. Diese darf jeder aus dem Team, der die
* Kachel sieht -- sie sind daraus abgeleitet, nicht abgeschrieben. */
export const MODI_BEREICHE_ERLAUBT = new Set(
HAND_MIT_TREFF
.map((k) => (/bereich\.html\?b=([a-z]+)/.exec(k.ziel) || [])[1])
.filter(Boolean),
...BRETTER_UEBER_SEITEN,
]);
.filter(Boolean));
/** Und die Bretter hinter einer Seite -- nur fuer den, der die Seite darf.
*
* DER DRITTE AUSGANG FEHLT HIER ABSICHTLICH: Ein Brett, das nicht in
* der Zuordnung steht, ist keine offene Frage, sondern schlicht nicht
* gemeint. `false` ist die richtige Antwort. */
export function brettHinterSeiteOffen(person, brett) {
const seite = BRETTER_UEBER_SEITEN[brett];
return !!seite && darfSeite(person, seite);
}
/* Der Satz unter der Begruessung. Die fuenf bekannten Rollen haben ihn
in assets/js/start.js stehen ("Creator · dein eigener Bereich"); fuer
@@ -1562,6 +1672,18 @@ const HAND_ROLLENTEXT = "Rechte Hand · du hast alles im Blick und entscheidest
export function rollentextFuer(person) {
if (person?.rolle === "modi") return MODI_ROLLENTEXT;
if (person?.rolle === "hand") return HAND_ROLLENTEXT;
/* DIE COMMUNITY HATTE NUR EIN WORT (nachgetragen 19.09.2026).
Bei jeder anderen Rolle steht unter dem Titel ein Satz. Beim
Community-Mitglied stand dort das nackte Wort "Community" -- und
das ist woertlich derselbe Mangel, den der Kommentar bei
MODI_ROLLENTEXT beschreibt und fuer den Modi behebt: "das sieht
nicht verborgen aus, sondern unfertig."
Fuer den Menschen, der von aussen kommt und niemanden hier kennt,
ist der erste Satz nach dem Anmelden der wichtigste im ganzen
Haus. */
if (person?.rolle === "gast") return "Community · dein Platz im Treff";
return null;
}
@@ -4533,6 +4655,29 @@ const OHNE_KACHEL_UEBERALL = new Set([
ein Hinweis, kein Grund.) */
"/workspace/bewerben.html",
"/workspace/bewerbungen.html",
/* „KLINGELT NICHTS?“ (nachgetragen 19.09.2026).
Gefunden beim Durchgehen der Seite aus Benutzersicht, und es war
der peinlichste Fund des Tages: `anruf-probe.html` ist die Seite,
die erklaert, WARUM das Telefon stumm bleibt. Sie steht in der
Rechtetafel fuer alle offen, und aus dem Chat zeigen zwei Knoepfe
darauf (chat.js, anruf.js).
Nur hatte sie nie eine Kachel -- absichtlich, sie ist kein
Bereich. Seit die Adressregel vom 15.09. eine Kachel VERLANGT,
landete damit jeder Klick auf der Startseite. Wortlos.
Das ist die schlimmste Sorte Sackgasse: Man ruft sie auf, WEIL
schon etwas nicht geht, und sie wirft einen hinaus. Wer das
erlebt, schliesst daraus, dass die ganze App kaputt ist -- und
genau das ist sie in dem Moment fuer ihn auch.
Die Hinweisleiste haengt mit dran: workspace-hinweise.js wirft
jeden Hinweis weg, dessen Ziel auf dieser Adresse nicht existiert.
Der Hinweis „Bei dir klingelt nichts -- kein Geraet angemeldet"
wurde auf crew deshalb NIE angezeigt. Ausgerechnet der, der acht
von elf Leuten betrifft. Eine Zeile hier heilt beides. */
"/workspace/anruf-probe.html",
]);
/** Gibt es diese Seite auf der Adresse, auf der die Person gerade steht? */
+51 -5
View File
@@ -16,20 +16,64 @@
verdeckt. Gefunden hat es pruef-struktur.
Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. -->
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
<!-- Ohne diese drei nimmt ein iPhone einen Bildschirmausschnitt als
Symbol, Android zeichnet die Leiste weiss, und die Seite oeffnet
ausserhalb der App -- auf einer Seite, die genau erklaeren soll,
WARUM das Telefon nicht klingelt, ist das besonders unpassend. -->
<link rel="manifest" href="/workspace/crew.webmanifest?v=202609191018" />
<meta name="theme-color" content="#06090f" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<style>
/* Bewusst in der Seite und nicht in einer eigenen Datei: Diese Seite
soll auch dann noch etwas anzeigen, wenn genau das schiefgeht,
was sie untersucht -- eine Stilvorlage, die aus dem
Zwischenspeicher kommt. */
/* DIE KOPFLEISTE WAR NIE GESTALTET (nachgetragen 19.09.2026).
Gefunden von pruef-css-klassen: `.kopfleiste`, `.kopfleiste__zurueck`,
`.kopfleiste__mitte` und `.kopfleiste__titel` stehen in KEINER
Stilvorlage des Hauses -- sie leben in start.css bzw. crew-haus.css,
und genau die lädt diese Seite absichtlich nicht.
Ergebnis war eine Leiste aus nacktem Text: der Zurück-Weg ein
blasser Link irgendwo links oben, der Titel daneben in Fließtext.
Ausgerechnet auf der Seite, die man aufruft, WEIL schon etwas nicht
funktioniert -- da sieht unfertig aus wie kaputt.
Sie stehen hier und nicht in einer Datei, aus demselben Grund wie
alles andere auf dieser Seite: Sie soll auch dann noch etwas
anzeigen, wenn genau das schiefgeht, was sie untersucht. */
.kopfleiste {
display: flex; align-items: center; gap: .6rem;
padding: .55rem .9rem;
border-bottom: 1px solid var(--rand);
background: color-mix(in srgb, var(--tinte) 92%, #ffffff 8%);
}
.kopfleiste__zurueck {
/* 44 px hoch: Das ist die Größe, die ein Daumen sicher trifft.
Auf einem Handy ist dieser Knopf der einzige Weg zurück. */
display: inline-flex; align-items: center;
min-height: 44px; padding: 0 .7rem 0 .2rem;
color: var(--text-leise); text-decoration: none;
font-size: .92rem; border-radius: .5rem;
}
.kopfleiste__zurueck:hover,
.kopfleiste__zurueck:focus-visible { color: var(--text); }
.kopfleiste__mitte { flex: 1; min-width: 0; text-align: center; }
.kopfleiste__titel {
font-size: .92rem; font-weight: 600; color: var(--text);
letter-spacing: .01em;
}
/* Gleich breit wie der Zurück-Weg, damit der Titel wirklich mittig
steht statt nur ungefähr. */
.kopfleiste__rechts { flex: 0 0 4.5rem; }
@media (max-width: 420px) {
.kopfleiste__rechts { flex-basis: 0; }
.kopfleiste__mitte { text-align: right; }
}
.probe { max-width: 46rem; margin: 0 auto; padding: 1.4rem 1rem 3rem; }
.probe h1 { margin: .2rem 0 .3rem; }
.probe__unter { color: var(--text-leise); margin: 0 0 1.4rem; }
@@ -56,7 +100,7 @@
</head>
<body class="haus">
<header class="kopfleiste">
<a class="kopfleiste__zurueck" href="chat.html">‹ Zum Chat</a>
<a class="kopfleiste__zurueck" id="zurueck-weg" href="start.html">‹ Zurück</a>
<div class="kopfleiste__mitte"><span class="kopfleiste__titel">Anruf-Probe</span></div>
<div class="kopfleiste__rechts"></div>
</header>
@@ -75,6 +119,8 @@
<p class="probe__ende" id="rat"></p>
</main>
<script src="assets/js/anruf-probe.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/anruf-probe.js?v=202609191506" defer></script>
</body>
</html>
+4 -4
View File
@@ -22,25 +22,25 @@
},
"icons": [
{
"src": "/assets/img/app-symbole/workspace-192.png?v=202609191147",
"src": "/assets/img/app-symbole/workspace-192.png?v=202609191506",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/img/app-symbole/workspace-512.png?v=202609191147",
"src": "/assets/img/app-symbole/workspace-512.png?v=202609191506",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609191147",
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609191506",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609191147",
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609191506",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
+14 -4
View File
@@ -104,9 +104,14 @@
.chat__zurueck {
display: none;
flex: none;
width: 34px; height: 34px;
/* 44px statt 34px (19.09.2026): Zwischen 761 und 860 px -- also auf
jedem Tablet hochkant -- greift die 44-px-Hausregel nicht, und
dieser Knopf war dort 34x34, also 40 Prozent weniger Flaeche als
noetig. Er ist der EINZIGE Weg von einem Gespraech zurueck in die
Liste; wer ihn verfehlt, sitzt fest. */
width: 44px; height: 44px;
align-items: center; justify-content: center;
border: 1px solid var(--rand); border-radius: 10px;
border: 1px solid var(--rand); border-radius: var(--radius-klein);
background: rgba(255, 255, 255, .04);
color: var(--text-leise); cursor: pointer;
}
@@ -904,7 +909,7 @@
position: absolute;
right: 14px; bottom: calc(100% - 6px);
z-index: 5;
display: grid; grid-template-columns: repeat(10, 1fr); gap: 2px;
display: grid; grid-template-columns: repeat(10, 1fr); gap: 4px;
padding: 10px;
border-radius: 14px;
background: rgba(8, 12, 20, .97);
@@ -913,7 +918,12 @@
}
.emoji-tafel__knopf {
appearance: none; border: 0; background: none;
width: 34px; height: 34px; border-radius: 8px;
/* FUELLT SEINE SPALTE, STATT 34 px BREIT ZU SEIN (19.09.2026).
Emojis stehen dicht an dicht: Bei 34 px Knopf und 2 px Abstand
liegt der Nachbar 36 px vom Mittelpunkt entfernt. Wer danebentippt,
verschickt ein anderes Zeichen -- und anders als ein Fehlgriff in
einem Menue ist das sofort abgeschickt. */
width: 100%; min-height: 44px; border-radius: var(--radius-klein);
font-size: 1.15rem; line-height: 1; cursor: pointer;
transition: background var(--tempo);
}
+12
View File
@@ -582,3 +582,15 @@
Sie sind die Form, und die soll vor dem Himmel nicht verschwinden. */
--modul-schleier: .30;
}
/* Der Schriftzug von Team Dogi trägt denselben Verlauf wie die
Überschrift der Zentrale — und brauchte denselben Rückfall. Ohne ihn
steht im Windows-Kontrastmodus oben links nichts. Diese Datei liegt
auf allen 21 Seiten der Team-Dogi-Adresse. */
@media (forced-colors: active) {
.kopfleiste .marke__haupt {
background: none;
-webkit-text-fill-color: CanvasText;
color: CanvasText;
}
}
+5 -1
View File
@@ -383,7 +383,11 @@
}
.e-punkt__marke {
display: inline-block; margin: 2px 0 0;
font-size: .68rem; letter-spacing: .06em; text-transform: uppercase;
/* .72rem statt .68rem (19.09.2026): 10,88 px sind auch fuer ein
Schild zu klein -- Grossbuchstaben und Sperrung machen es nicht
lesbarer, nur breiter. .72rem ist die Groesse, die das Haus an
seinen uebrigen Marken benutzt (b-verlauf__datum). */
font-size: .72rem; letter-spacing: .06em; text-transform: uppercase;
color: var(--text-still);
border: 1px solid var(--rand); border-radius: 999px;
padding: 1px 8px;
+29
View File
@@ -406,6 +406,24 @@ body.gate {
.unterzeile { margin: 0 0 26px; color: var(--text-leise); font-size: .94rem; }
/* DER NACHSATZ, DER EINE EINSCHRÄNKUNG TRÄGT (19.09.2026).
Eine Zeile unter der Unterzeile, ruhiger gesetzt und mit einem
Strich links: Sie widerspricht dem Satz darüber nicht, sie
vervollständigt ihn. Deshalb tritt sie zurück, statt zu warnen —
augenschonend heißt hier ausdrücklich auch: keine Signalfarbe für
etwas, das keine Warnung ist. */
.unterzeile--zusatz {
margin: -18px 0 26px;
padding-left: 11px;
border-left: 2px solid var(--rand);
max-width: 62ch;
font-size: .86rem;
line-height: 1.6;
color: var(--text-still);
}
.unterzeile--zusatz em { font-style: normal; color: var(--text-leise); font-weight: 600; }
/* ---------- Rollen --------------------------------------------------- */
.feldschild {
@@ -2877,3 +2895,14 @@ body.gate--crew .rollen .feldschild::before {
font-size: .88rem; line-height: 1.5; color: var(--text-leise);
}
.alterfrage__text strong { color: var(--text); }
/* DER SATZ, DER SAGT, WOHER MAN EINEN CODE BEKOMMT (19.09.2026).
Er steht ÜBER der Fußzeile mit der Verschlüsselungszusage, nicht
darunter: Wer keinen Code hat, soll ihn lesen, bevor er aufgibt.
Etwas heller gesetzt als der Fuß — er ist eine Auskunft, kein
Kleingedrucktes. */
.fuss--woher {
color: var(--text-leise);
margin-bottom: 6px;
}
+30
View File
@@ -1151,3 +1151,33 @@
}
/* =====================================================================
DIE ÜBERSCHRIFT MUSS AUCH IM KONTRASTMODUS DA SEIN (19.09.2026)
`.ztitel` ist mit `background-clip: text` gesetzt — der Schriftzug ist
also ein Farbverlauf, der durch die Buchstaben scheint, und die
Buchstaben selbst sind durchsichtig.
Zwei Sicherungen fehlten, die das Haus sonst überall setzt:
1. KEIN @supports. Wo `background-clip: text` nicht greift, wirkt
`-webkit-text-fill-color: transparent` trotzdem — der Text ist
dann einfach weg. Alle anderen Stellen im Haus (gate.css,
leistung.css, crew-haus.css) kapseln denselben Griff.
2. KEIN forced-colors-RÜCKFALL. Im Windows-Kontrastmodus wird der
Verlauf verworfen, `-webkit-text-fill-color` aber nicht
zurückgesetzt. Übrig bleibt durchsichtiger Text — die Seite hat
dann keine Überschrift.
Dasselbe gilt für den Schriftzug von Team Dogi in crew-haus.css.
===================================================================== */
@media (forced-colors: active) {
.ztitel {
background: none;
-webkit-text-fill-color: CanvasText;
color: CanvasText;
filter: none;
}
}
+9
View File
@@ -217,3 +217,12 @@
.fall-zeile { transition: none; }
.fall-zeile:hover { transform: none; }
}
/* Der Grund, warum „Neue Meldung" gerade gesperrt ist — sichtbar,
nicht nur im Maus-Tooltip. Ruhig gesetzt: Es ist eine Auskunft,
keine Warnung. */
.h-gesperrt {
margin: 8px 0 0; max-width: 56ch;
font-size: .85rem; line-height: 1.55;
color: var(--text-still);
}
+21 -5
View File
@@ -594,7 +594,9 @@
.k-strahl__sym { position: absolute; top: 2px; font-size: .8rem; }
.k-strahl__kw {
position: absolute; bottom: 0;
font-size: .62rem; color: var(--text-still);
/* .75rem statt .62rem (9,92 px): Platz ist da -- die Spalte ist bei
390 px rund 52 px breit. */
font-size: .75rem; color: var(--text-leise);
font-variant-numeric: tabular-nums;
}
.k-strahl__fuss {
@@ -706,13 +708,23 @@
.k-raster { grid-template-columns: 32px repeat(7, minmax(0, 1fr)); }
.k-tag { min-height: 76px; padding: 6px 5px 7px; }
.k-tag__monat, .k-tag__heute { display: none; }
/* .75rem statt .6rem (19.09.2026). Hier standen 9,6 px -- genau der
Wert, den start.css als BEHOBENEN Fehler protokolliert. Der Haken:
kalender.css wird NACH start.css geladen, beide Selektoren sind
gleich stark, also gewann diese Zeile auf jeder Breite unter
760 px. Die Reparatur war seit dem Tag wirkungslos, ohne dass es
jemand sehen konnte. Die Uhrzeit ist die einzige Auskunft in der
Zelle. */
.k-anlass, .k-pille {
font-size: .6rem; padding: 1px 4px;
font-size: .75rem; padding: 3px 6px;
border-left-width: 2px;
}
.k-pille__zeit { margin-right: 3px; }
.k-spaltenkopf { padding: 8px 4px; font-size: .66rem; letter-spacing: .06em; }
.k-kw { font-size: .66rem; padding: 7px 2px; }
/* .75rem statt .66rem: 10,56 px, zusaetzlich gesperrt -- und von
keiner Hebungsliste erfasst. Die Wochentagskoepfe sind das, woran
man sich im Monatsraster orientiert. */
.k-spaltenkopf { padding: 8px 4px; font-size: .75rem; letter-spacing: .04em; }
.k-kw { font-size: .75rem; padding: 7px 2px; }
.k-woche { grid-template-columns: 1fr; }
.k-wochentag { min-height: 0; }
.k-strahl { height: 110px; }
@@ -1088,7 +1100,11 @@
.wecker-knopf {
--w-ton: #e3b341;
display: inline-flex; align-items: center; gap: 4px;
min-height: 30px; padding: 3px 9px 3px 7px;
/* 38px statt 30px (19.09.2026): 30 px waren 14 px unter der
Hausvorgabe und 8 px unter dem kleinsten Zugestaendnis, das
sonst ueberall gilt. In der Terminzeile stand er damit neben
Knoepfen von 38 und 44 px -- drei Hoehen in einer Reihe. */
min-height: 38px; padding: 5px 9px 5px 7px;
border: 1px solid var(--rand); border-radius: 999px;
background: transparent; color: var(--text-still);
font: inherit; font-size: .78rem; cursor: pointer;
+43
View File
@@ -737,3 +737,46 @@ body[data-nur-lesen="ja"] select { display: none; }
.liste-kachel > .gruppe::after { background: CanvasText; }
.gruppe__kopf-zeichen { border: 1px solid CanvasText; }
}
/* =====================================================================
DER CODE-KASTEN, VERVOLLSTÄNDIGT (19.09.2026)
Er sagte „jetzt weitergeben" und gab die Adresse nicht mit, ohne die
ein Code nutzlos ist. Und er verschwieg, dass sich ein verlorener
Code jederzeit neu vergeben lässt – wer das nicht weiß, glaubt, er
habe einen Menschen angelegt, der nie hereinkommt.
===================================================================== */
.code-kasten__zeile {
margin: 10px 0 0;
display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
}
.code-kasten__was {
font-size: .72rem; letter-spacing: .06em; text-transform: uppercase;
color: var(--text-still);
}
.code-kasten__adresse {
/* 44 px Tippziel: Auf dem Handy führt dieser Link zur Anmeldeseite,
die man der anderen Person zeigen will. */
display: inline-flex; align-items: center; min-height: 44px;
font-size: .95rem; font-weight: 600;
color: var(--text); text-decoration: underline;
text-underline-offset: 3px;
word-break: break-all;
}
.code-kasten__adresse:hover { color: var(--text-hell, #ffffff); }
/* Der Rettungsweg. Ruhig gesetzt – er ist eine Beruhigung, keine
Warnung, und darf den Satz darüber nicht überstrahlen. */
.code-kasten__rettung {
margin: 10px 0 0; max-width: 60ch;
padding-left: 11px;
border-left: 2px solid var(--rand);
font-size: .84rem; line-height: 1.6; color: var(--text-still);
}
.code-kasten__rettung strong { color: var(--text-leise); font-weight: 640; }
/* Drei Knöpfe statt zwei: Auf schmalen Bildschirmen untereinander,
damit keiner auf 50 px gequetscht wird. */
@media (max-width: 460px) {
.code-kasten .neu__knoepfe { flex-direction: column; align-items: stretch; }
}
+17 -1
View File
@@ -2991,7 +2991,12 @@ body.start #protokoll {
.feldschild, .marke, .gruppe__unter, .kachel__unter {
font-size: .75rem;
}
.k-pille, .k-anlass { font-size: .7rem; }
/* .75rem statt .7rem (19.09.2026): .7rem sind 11,2 px -- also 0,3 px
UNTER der Grenze, die 33 Zeilen weiter oben aufgestellt wird.
Die Regel, die zu kleine Schrift hochziehen soll, zog sie auf
einen Wert, der selbst zu klein ist. team.css hat genau diesen
Fehler fuer .tampel__tag schon einmal korrigiert. */
.k-pille, .k-anlass { font-size: .75rem; }
.k-tag__heute, .k-anlasskarte__rest small,
.k-anlasskarte__rest, .zahl__schild, .karte__zeile {
font-size: .72rem;
@@ -7969,6 +7974,17 @@ body.start .k-leiste .feldschild::after {
.kopfleiste__rechts > .teilen,
.kopfleiste__rechts > .chat-knopf,
.kopfleiste__rechts > .suche-knopf,
/* DIE GLOCKE STAND HIER NICHT (nachgetragen 19.09.2026).
Sie ist ein echtes <button> und wurde damit von der 44-px-Regel
erfasst, waehrend ihre vier Nachbarn durch diesen staerkeren
Selektor auf 36 px gehen. Zwischen 401 und 560 px -- also bei
412 px (haeufigste Android-Breite) und 430 px (iPhone Pro Max) --
stand damit eine 44 px hohe runde Pille zwischen fuenf 36-px-
Quadraten. Der 400er-Block darunter listet sie korrekt; genau
dieses Band fiel durch beide Rechnungen.
Dasselbe Muster wie am 06.09.: eine Liste fuer vier Elemente,
und das fuenfte kam spaeter dazu. */
.kopfleiste__rechts > .glocke,
.kopfleiste__rechts > .abmelden {
width: 36px; min-width: 36px; height: 36px; min-height: 36px;
padding: 0; gap: 0;
+127 -4
View File
@@ -193,7 +193,19 @@
}
.w-streifen__zeile {
display: grid;
grid-template-columns: minmax(14ch, 1fr) repeat(auto-fit, minmax(2.4rem, 2.4rem));
/* `var(--monate)` statt `auto-fit` (19.09.2026).
`auto-fit` legt so viele Spuren an, wie in die BREITE passen --
nicht so viele, wie Zellen da sind. Bei 390 px waren das sechs;
die restlichen Monatszellen wanderten in eine zweite,
unbeschriftete Rasterzeile. Kopf- und Datenzeile rechneten das
ausserdem unabhaengig voneinander und konnten sich gegenseitig
verschieben.
Und das `overflow-x: auto` am Elternteil konnte nie greifen: Ein
Raster mit `auto-fit` wird nie breiter als sein Kasten, es faltet
sich um. Der Kommentar daneben beschrieb ein Verhalten, das es
nicht gab. Jetzt kommt die Zahl aus den Daten -- so wie es das
Bewegungsdiagramm nebenan schon richtig macht. */
grid-template-columns: minmax(14ch, 1fr) repeat(var(--monate, 6), 2.4rem);
align-items: center; gap: 6px;
}
.w-streifen__zeile--kopf { margin-bottom: 4px; }
@@ -202,7 +214,11 @@
padding-right: 8px;
}
.w-streifen__monat {
font-size: .71rem; color: var(--text-still); text-align: center;
/* .75rem statt .71rem (19.09.2026): Die Monatsbeschriftung ist
kein Schild, sondern etwas, das man liest -- 11,4 px sind dafuer
zu klein. 12 px ist die Untergrenze, die das Haus fuer Fliesstext
und Beschriftungen einhaelt. */
font-size: .75rem; color: var(--text-still); text-align: center;
font-variant-numeric: tabular-nums;
}
.w-streifen__zelle {
@@ -217,7 +233,64 @@
.w-streifen__zelle[data-stufe="laeuft"] { color: #7fb08a; border-color: color-mix(in srgb, #668e6e 55%, transparent); }
.w-streifen__zelle[data-stufe="waechst"] { color: #8ec9ff; border-color: color-mix(in srgb, #8ec9ff 45%, transparent); }
.w-streifen__zelle[data-stufe="hakt"] { color: #d8a76a; border-color: color-mix(in srgb, #cc9451 55%, transparent); }
.w-streifen__zelle[data-stufe="leer"] { opacity: .32; }
/* LEER IST WIRKLICH LEER (19.09.2026).
Vorher stand hier ein Mittelpunkt bei 32 % Deckkraft -- und daneben
trägt „Kann ich nicht sagen" einen Halbgeviertstrich. Zwei kurze
waagerechte Striche nebeneinander, und dazwischen liegt der
Unterschied zwischen „niemand hat hingesehen" und „jemand hat
hingesehen und konnte es nicht sagen".
Jetzt trägt die leere Zelle kein Zeichen, sondern einen
gestrichelten Rahmen: Man SIEHT das Fehlen, statt es zu lesen --
und es lässt sich mit keinem Zeichen mehr verwechseln. */
.w-streifen__zelle[data-stufe="leer"] {
background: none;
border-style: dashed;
border-color: color-mix(in srgb, var(--rand) 70%, transparent);
}
.w-streifen__zelle[data-stufe="unklar"] { color: var(--text-still); }
/* Die Zeichenerklärung unter dem Streifen. Dieselben Farben wie die
Zellen -- sie wird daneben gelesen, nicht auswendig gelernt. */
.w-legende--streifen { margin-top: 12px; }
.w-legende__zeichen {
display: grid; place-items: center;
width: 1.55rem; height: 1.4rem;
font-size: .82rem; font-weight: 700;
border: 1px solid var(--rand);
border-radius: var(--radius);
color: var(--text-still);
background: rgba(0, 0, 0, .25);
}
.w-legende__zeichen[data-stufe="laeuft"] { color: #7fb08a; border-color: color-mix(in srgb, #668e6e 55%, transparent); }
.w-legende__zeichen[data-stufe="waechst"] { color: #8ec9ff; border-color: color-mix(in srgb, #8ec9ff 45%, transparent); }
.w-legende__zeichen[data-stufe="hakt"] { color: #d8a76a; border-color: color-mix(in srgb, #cc9451 55%, transparent); }
.w-legende__zeichen[data-stufe="leer"] {
background: none; border-style: dashed;
border-color: color-mix(in srgb, var(--rand) 70%, transparent);
}
/* DIE ZAHLEN IN WORTEN, unter dem Bewegungsdiagramm.
Es gibt sie, weil die Aufschlüsselung vorher nur im Mausüberfahren
stand -- und wer dieses Haus benutzt, steht oft mit dem Handy im
Stream. Dort gibt es kein Überfahren. */
.w-bewegung__worte {
margin: 14px 0 0;
display: grid; gap: 4px;
}
.w-bewegung__wortzeile {
margin: 0;
display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
font-size: .82rem; line-height: 1.55;
}
.w-bewegung__wortmonat {
min-width: 4.5ch;
color: var(--text-leise); font-weight: 640;
font-variant-numeric: tabular-nums;
}
.w-bewegung__worttext { color: var(--text-still); }
/* Älter als 90 Tage: sichtbar, aber sichtbar alt. Nicht weg — man soll
sehen, DASS etwas alt ist, statt dass es verschwindet. */
.w-streifen__zelle[data-alt="ja"] { opacity: .45; border-style: dashed; }
@@ -274,7 +347,7 @@
------------------------------------------------------------------- */
@media (max-width: 560px) {
.w-streifen__zeile {
grid-template-columns: minmax(10ch, 1fr) repeat(auto-fit, minmax(2rem, 2rem));
grid-template-columns: minmax(10ch, 1fr) repeat(var(--monate, 6), 2rem);
}
.w-streifen__zelle { width: 1.9rem; }
.w-bewegung__gitter { gap: 5px; }
@@ -298,3 +371,53 @@
.w-legende__fleck { background: CanvasText; }
.w-streifen__zelle { border-color: CanvasText; }
}
/* =====================================================================
DIE EIGENE SICHT — Lesehilfe, leerer Zustand, Wege weiter
(19.09.2026)
Alle drei sind dazugekommen, weil beim Durchgehen aus Benutzersicht
auffiel: Die Seite, die dem Beurteilten etwas geben soll, gab ihm
weniger als dem Beurteilenden — keine Erklärung zum Diagramm, keinen
Satz wenn nichts da ist, und eine Aufforderung ohne Ort.
===================================================================== */
.w-lesehilfe {
margin: 0 0 16px;
max-width: 62ch;
font-size: .84rem; line-height: 1.6;
color: var(--text-still);
}
/* Der leere Zustand. Kein Diagramm über nichts — ein Satz.
Ruhig gesetzt: „noch nichts da" ist keine Warnung. */
.w-leer {
padding: 20px 22px;
border: 1px dashed color-mix(in srgb, var(--rand) 80%, transparent);
border-radius: var(--radius);
background: color-mix(in srgb, var(--tinte) 96%, #ffffff 4%);
}
.w-leer__titel {
margin: 0 0 6px;
font-size: .95rem; font-weight: 640; color: var(--text-leise);
}
.w-leer__satz {
margin: 0; max-width: 58ch;
font-size: .86rem; line-height: 1.6; color: var(--text-still);
}
/* Die Wege weiter. Zwei, nicht fünf: einer führt zum Katalog, einer
zum Gespräch. Mehr wäre eine Auswahl statt eines nächsten Schritts. */
.w-wege {
margin: 20px 0 0;
display: flex; flex-wrap: wrap; gap: 10px;
}
/* Der zweite Weg tritt zurück: Der Katalog ist der naheliegende
nächste Schritt, das Gespräch der zweite. Eine eigene Knopfart
dafür anzulegen wäre zu viel — es reicht, die Fläche wegzunehmen. */
.w-weg--leise {
background: none;
border: 1px solid var(--rand);
color: var(--text-leise);
}
.w-weg--leise:hover { color: var(--text); border-color: var(--rand-hell); }
+2 -2
View File
@@ -122,7 +122,7 @@
body: JSON.stringify({ stufe: wert, begruendung: grund }),
});
if (!a.ok) {
alert((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
alert(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'));
k.disabled = false;
return;
}
@@ -169,7 +169,7 @@
body: JSON.stringify({ text: feld.value }),
});
if (!a.ok) {
alert((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
alert(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'));
return;
}
const { nachrichten } = await a.json();
+34 -1
View File
@@ -168,7 +168,17 @@
return;
}
if (!a.ok) {
punkt('warn', 'Der Server antwortet ungewöhnlich', `Antwort ${a.status}.`);
/* NICHT NUR DIE ZAHL (19.09.2026). Alle zwoelf anderen Punkte
sagen, was zu tun ist -- dieser sagte "Antwort 503.". Und
er kann als Erster im Schlussrat landen, also ganz unten
als das, was offen bleibt. Diese Seite ist ausdruecklich
fuer jemanden gebaut, der nicht technisch ist; eine
dreistellige Zahl ist fuer ihn keine Auskunft, sondern die
Bestaetigung, dass er hier nichts ausrichten kann. */
punkt('warn', 'Der Server antwortet gerade ungewöhnlich',
'Wahrscheinlich wird er gerade neu gestartet oder ist kurz '
+ 'überlastet. Warte eine Minute und öffne diese Seite noch '
+ `einmal. Bleibt es dabei, sag DogFather Bescheid – Kennzahl ${a.status}.`);
return;
}
const d = await a.json();
@@ -238,3 +248,26 @@
ratGeben();
})();
})();
/* ---------------------------------------------------------------------
DER RÜCKWEG FÜHRT DORTHIN, WO MAN HERKAM (19.09.2026)
Hier stand fest „‹ Zum Chat". Erreicht wird diese Seite aber auch von
der STARTSEITE aus -- über den Hinweis „Bei dir klingelt nichts".
Wer von dort kam, landete im Chat statt zurück, und das ist auf einer
Hilfeseite die falscheste Richtung überhaupt.
Jede andere Unterseite bekommt über kopf.js einen Zurück-Knopf, der
wirklich zurückführt. Diese Seite lädt kopf.js bewusst nicht (sie
soll auch dann noch stehen, wenn anderes klemmt) -- also hier
dieselbe Rechnung in vier Zeilen: Gibt es einen Verlauf, geh zurück;
sonst bleibt der Link auf die Startseite, wie er im HTML steht.
------------------------------------------------------------------- */
(function () {
var weg = document.getElementById('zurueck-weg');
if (!weg || history.length <= 1) return;
weg.addEventListener('click', function (e) {
e.preventDefault();
history.back();
});
})();
+85 -9
View File
@@ -175,12 +175,20 @@
/* Ab hier redet man miteinander -- ein Freizeichen daneben
waere das, was man an einer Telefonanlage hasst. */
tonAus();
klingelWache(); // niemand-geht-ran-Wecker abbestellen
if (anruf && !anruf.beginn) anruf.beginn = Date.now();
lage('Verbunden');
}
/* `failed` heisst: Es kommt nichts mehr. Das gehoert gesagt --
ein Kasten, der ewig „Verbindung …" zeigt, laesst einen warten,
wo nichts mehr kommt. */
if (v.connectionState === 'failed') {
/* TON AUS. Bis heute piepte das Freizeichen weiter, waehrend
daneben "Keine Verbindung." stand -- der Ton sagte "es
klingelt noch", der Text "vorbei". Man weiss dann nicht,
welchem man glauben soll, und legt vorsichtshalber nicht auf. */
tonAus();
klingelWache();
lage(nurGleichesNetz
? 'Keine Verbindung – dafür fehlt noch ein Vermittlungsserver.'
: 'Keine Verbindung.');
@@ -252,13 +260,27 @@
Ein „ging nicht" wäre hier die unbrauchbarste Auskunft von
allen -- man sucht dann am Gerät statt an der Berechtigung. */
if (f?.name === 'NotAllowedError') {
throw new Error('Der Browser lässt das Mikrofon nicht zu. '
+ 'Im Schloss-Symbol neben der Adresse freigeben.');
/* NICHT "im Schloss-Symbol neben der Adresse" (geaendert
19.09.2026). In der installierten App gibt es weder
Adresszeile noch Schloss -- die Anweisung fuehrte genau dort
ins Leere, wo die meisten sie lesen. Das Haus kennt diesen
Fehler und hat ihn beim Weg zur Anruf-Probe schon einmal
behoben; hier stand er noch.
Der neue Satz traegt in beiden Faellen, weil er den Ort
nennt, den es immer gibt: die Geraeteeinstellungen. */
throw new Error('Das Mikrofon ist gesperrt. Erlaube es in den '
+ 'Einstellungen deines Geräts (Apps → Team Dogi → Mikrofon) '
+ 'und versuch es noch einmal. Unter „Klingelt nichts?" steht, '
+ 'wie das geht.');
}
if (f?.name === 'NotFoundError') {
throw new Error('Kein Mikrofon gefunden.');
throw new Error('Kein Mikrofon gefunden. Steckt ein Headset '
+ 'halb drin oder ist es anderweitig in Benutzung?');
}
throw new Error('Das Mikrofon ließ sich nicht öffnen.');
throw new Error('Das Mikrofon ließ sich nicht öffnen. Schließ '
+ 'andere Programme, die es benutzen könnten, und versuch es '
+ 'noch einmal.');
}
}
@@ -280,8 +302,18 @@
return;
}
await adressenFertig;
/* `beginn: null` UND NICHT Date.now() (19.09.2026).
Die Uhr zaehlte ab dem Waehlen. Wer 30 Sekunden klingeln liess
und dann 12 Sekunden sprach, las im Kasten "00:42" -- und danach
im Chat "Anruf . 12 Sekunden". Zwei Zahlen fuer dieselbe Sache,
eine halbe Minute auseinander. Der Server rechnet es richtig
(gespraechSeit); der Kasten hat es wieder kaputtgemacht.
Bis jemand rangeht steht jetzt "Es klingelt ..." statt einer
Uhr, die etwas misst, das noch nicht stattfindet. */
anruf = { raumId, video: mitVideo, verbindungen: new Map(),
stroeme: new Map(), eigener, beginn: Date.now() };
stroeme: new Map(), eigener, beginn: null };
$('anruf-video').hidden = !mitVideo;
if (mitVideo) $('anruf-video').setAttribute('aria-pressed', 'true');
lage('Es klingelt …');
@@ -289,6 +321,16 @@
dass etwas passiert -- nicht, was. */
freiTon();
uhrLaufen();
/* UND EIN ENDE (19.09.2026). Bis heute lief das Freizeichen
UNENDLICH. Es gab keinen Zeitgeber, der aufhoert: Wer niemanden
erreichte, sass vor einem piependen Kasten mit "02:47" darauf,
als telefoniere er laengst. Genau das wird als "geht einfach
nicht" gemeldet.
Der Server laesst einen Anruf nach KLINGELT_SEKUNDEN verfallen
und schickt den Zeitpunkt sogar mit (`laeuft_bis`) -- benutzt
hat ihn nie jemand. */
klingelWache(KLINGELT_MS);
const a = await fetch(`/workspace/api/anruf/${raumId}/start`, {
method: 'POST', credentials: 'same-origin',
@@ -297,7 +339,7 @@
}).catch(() => null);
if (!a || !a.ok) {
const d = a ? await a.json().catch(() => ({})) : {};
lage(d.fehler || 'Der Anruf ließ sich nicht starten.');
lage(sagWas(d.fehler, 'Der Anruf ließ sich nicht starten.'));
setTimeout(() => auflegen(false), 5000);
return;
}
@@ -315,7 +357,7 @@
}).catch(() => null);
if (!a || !a.ok) {
const d = a ? await a.json().catch(() => ({})) : {};
window.anrufMeldung?.(d.fehler || 'Der Anruf ist schon vorbei.');
window.anrufMeldung?.(sagWas(d.fehler, 'Der Anruf ist schon vorbei.'));
klingelnAus();
return;
}
@@ -416,6 +458,8 @@
uhrStoppen();
uhr = setInterval(() => {
if (!anruf) return uhrStoppen();
/* Solange niemand rangegangen ist, gibt es nichts zu zaehlen. */
if (!anruf.beginn) return;
const s = Math.floor((Date.now() - anruf.beginn) / 1000);
const z = $('anruf-zeit');
if (z) z.textContent = `${String(Math.floor(s / 60)).padStart(2, '0')}:`
@@ -569,8 +613,17 @@
k.append(reihe);
document.body.append(k);
/* Nach der Klingelzeit von selbst weg -- sonst steht die Einladung
zu einem Gespräch da, an dem längst niemand mehr teilnimmt. */
setTimeout(klingelnAus, 46000);
zu einem Gespräch da, an dem längst niemand mehr teilnimmt.
ABER SO LANGE WIE DER ANRUF WIRKLICH LAEUFT (19.09.2026). Hier
standen 46 Sekunden, waehrend der Server 120 gibt. Wer beim
Klingeln das Handy aus der Tasche holt und in Sekunde 50
hinsieht, fand nichts mehr -- kein Kasten, keine Knoepfe --,
waehrend der Anrufer noch siebzig Sekunden das Freizeichen
hoerte. Die 120 Sekunden sind im Server ausdruecklich damit
begruendet, dass man Zeit zum Entsperren braucht; diese Zeile
hat genau diese Entscheidung wieder aufgehoben. */
setTimeout(klingelnAus, KLINGELT_MS);
}
/* ---------- DER KLINGELTON (18.09.2026) --------------------------
@@ -647,6 +700,29 @@
tonWerk = setInterval(takt, 3000);
}
/* Wie lange es hoechstens klingelt, bevor der Browser selbst
aufgibt. Der Server schickt `laeuft_bis` mit; solange das nicht
ueberall ankommt, ist dies der Rueckfall -- dieselbe Zahl wie
KLINGELT_SEKUNDEN in workspace-anruf.js, plus zwei Sekunden Luft,
damit der Server zuerst dran ist. */
const KLINGELT_MS = 122000;
let klingelWecker = null;
/** Ohne Zahl: abbestellen. Mit Zahl: nach so vielen Millisekunden
* aufgeben, falls bis dahin niemand rangegangen ist. */
function klingelWache(ms) {
if (klingelWecker) { clearTimeout(klingelWecker); klingelWecker = null; }
if (!ms) return;
klingelWecker = setTimeout(() => {
klingelWecker = null;
/* Ist inzwischen jemand dabei, war es kein vergeblicher Anruf. */
if (!anruf || anruf.beginn) return;
tonAus();
lage('Niemand geht ran.');
setTimeout(() => { if (anruf && !anruf.beginn) auflegen(); }, 2500);
}, ms);
}
function tonAus() {
if (tonWerk) { clearInterval(tonWerk); tonWerk = null; }
try { navigator.vibrate?.(0); } catch { /* egal */ }
+7 -7
View File
@@ -160,7 +160,7 @@
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
});
if (!r.ok) { melde((await r.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; }
if (!r.ok) { melde(sagWas((await r.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; }
await laden();
} catch { /* umgeleitet */ } finally { senden.disabled = false; }
});
@@ -346,7 +346,7 @@
});
if (!antwort.ok) {
const d = await antwort.json().catch(() => ({}));
$('abbruch-fehler').textContent = d.fehler || 'Hat nicht geklappt.';
$('abbruch-fehler').textContent = sagWas(d.fehler, 'Hat nicht geklappt.');
return;
}
dlg.close();
@@ -362,7 +362,7 @@
{ method: 'POST' });
if (!antwort.ok) {
const d = await antwort.json().catch(() => ({}));
melde(d.fehler || 'Hat nicht geklappt.');
melde(sagWas(d.fehler, 'Hat nicht geklappt.'));
b.disabled = false;
return;
}
@@ -921,7 +921,7 @@
});
if (!antwort.ok) {
const d = await antwort.json().catch(() => ({}));
$('neu-fehler').textContent = d.fehler || 'Anlegen hat nicht geklappt.';
$('neu-fehler').textContent = sagWas(d.fehler, 'Anlegen hat nicht geklappt.');
return;
}
$('neu').reset();
@@ -988,7 +988,7 @@
body: JSON.stringify(daten),
});
if (!a.ok) {
$('b-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Speichern ging nicht.';
$('b-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Speichern ging nicht.');
return;
}
dialog.close();
@@ -1624,7 +1624,7 @@ Das laesst sich nicht rueckgaengig machen. Erledigte Aufgaben sollten normalerwe
});
if (!a.ok) {
const f = await a.json().catch(() => ({}));
melde(f.fehler || 'Das hat nicht geklappt.');
melde(sagWas(f.fehler, 'Das hat nicht geklappt.'));
knopf.disabled = false; knopf.textContent = vorher;
return;
}
@@ -1729,7 +1729,7 @@ Das laesst sich nicht rueckgaengig machen. Erledigte Aufgaben sollten normalerwe
const j = await a.json().catch(() => ({}));
sagen(j.fehler === 'zu_gross'
? 'Die Datei ist zu groß.'
: (j.fehler || 'Das hat nicht geklappt.'));
: (sagWas(j.fehler, 'Das hat nicht geklappt.')));
return;
}
sagen('Angehängt.');
+2 -2
View File
@@ -112,7 +112,7 @@
body: JSON.stringify({ an }),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'));
$('ki-schalter').checked = !an;
return;
}
@@ -305,7 +305,7 @@
try {
const a = await hole('/workspace/api/zustand/sichern', { method: 'POST' });
const d = await a.json().catch(() => ({}));
if (!a.ok) { melde(d.fehler || 'Die Sicherung ist fehlgeschlagen.'); return; }
if (!a.ok) { melde(sagWas(d.fehler, 'Die Sicherung ist fehlgeschlagen.')); return; }
melde('');
await zustand();
} catch { /* umgeleitet */ } finally {
+46 -13
View File
@@ -43,6 +43,15 @@
wäre ein Loch. */
const darfEintragen = () => {
if (!ich || ich.rolle === 'creator') return false;
/* FAELLT DIE LAGE AUS, HEISST DAS NICHT "DARF ALLES"
(berichtigt 19.09.2026). `lageHolen()` schluckt jeden Fehler;
war `lage` null, fiel die Pruefung hier durch auf `true` --
fuer einen Zuschauer also von "darf auf keinem Brett" auf
"darf ueberall". Der Knopf erschien, das Formular ging auf,
man tippte -- und der Server lehnte mit 403 ab. Ein Formular,
das der Server ablehnt, laesst den Menschen davor sich selbst
fuer den Fehler halten. */
if (treffBrett && !lage) return false;
if (treffBrett && lage && lage.schreiben) return lage.schreiben[bereich] === true;
return true;
};
@@ -130,6 +139,12 @@
noch einmal steht, ist die, die auseinanderlaeuft; und die Fassung
im Browser kann man aendern. */
let treffBrett = false;
/* Darf ich den Kalender oeffnen? Kommt vom Server (kalender_offen).
Vorgabe `true`: Antwortet ein aelterer Server das Feld noch nicht,
bleibt es wie bisher -- ein fehlender Link waere fuer das Team ein
Rueckschritt, ein ueberzaehliger fuer die Community nur der alte
Zustand. */
let kalenderOffen = true;
let lage = null;
/* WELCHE BRETTER NACH ART GEGLIEDERT WERDEN.
Stand frueher mitten in der Zeichenfunktion -- dort konnte
@@ -509,7 +524,7 @@
const a = await hole(`/workspace/api/bereich/entwicklung/${e.id}/senden`,
{ method: 'POST', headers: { 'Content-Type': 'application/json' } });
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'));
b.disabled = false;
return;
}
@@ -750,9 +765,25 @@
ES IST EIN LINK, keine Marke. Wer liest „steht im Kalender",
will als Naechstes nachsehen, was noch an dem Tag ist. */
if (e.termin) {
const zeile = el('a', 'im-kalender');
/* LINK NUR, WENN DER KALENDER FUER MICH OFFEN IST (19.09.2026).
Fuer die Community ist er es nicht -- `kalender.html` steht in
der Rechtetafel auf ALLE, und ALLE ist ausdruecklich ohne
"gast". Bis heute stand hier trotzdem ein Link, und ein Klick
landete wortlos auf der Startseite: sichtbar, beschriftet,
vielversprechend -- und eine Sackgasse.
GEFRAGT WIRD DER SERVER (`kalender_offen`), nicht die Rolle.
Eine Rollenabfrage hier waere eine vierte Fassung derselben
Regel; die vierte ist die, die beim naechsten Umbau etwas
durchlaesst.
DIE AUSKUNFT BLEIBT in jedem Fall stehen -- "Im Kalender am
24.09." ist auch ohne Weg dorthin richtig und nuetzlich. Es
faellt nur der Klick weg, der nirgendwohin fuehrt. */
const zeile = el(kalenderOffen ? 'a' : 'span', 'im-kalender');
zeile.dataset.grund = e.termin.grund;
zeile.href = 'kalender.html';
if (kalenderOffen) zeile.href = 'kalender.html';
zeile.append(el('span', 'im-kalender__zeichen', '\u{1F5D3}'));
zeile.append(el('span', 'im-kalender__text',
(e.termin.grund === 'zugesagt' ? 'Zugesagt für ' : 'Im Kalender am ')
@@ -874,7 +905,7 @@
body: JSON.stringify({ grund }),
});
melde(a.ok ? 'Danke – wir sehen uns das an.'
: ((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'));
: (sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')));
}));
}
@@ -889,8 +920,8 @@
? { method: 'DELETE' }
: { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: '{}' });
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler
|| ('Es hängen schon ' + lage.anschlag_max + ' Ansagen.'));
melde(sagWas((await a.json().catch(() => ({}))).fehler,
'Es hängen schon ' + lage.anschlag_max + ' Ansagen.'));
return;
}
await laden();
@@ -931,7 +962,7 @@
const j = await a.json().catch(() => ({}));
melde(a.ok ? 'Steht jetzt bei den Talenten.'
: (j.fehler === 'schon_uebernommen' ? 'Steht dort schon.'
: (j.fehler || 'Ging nicht.')));
: (sagWas(j.fehler, 'Ging nicht.'))));
}));
}
@@ -951,7 +982,7 @@
const a = await hole('/workspace/api/bereich/' + bereich + '/' + e.id
+ '?grund=' + encodeURIComponent(grund), { method: 'DELETE' });
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'));
return;
}
await laden();
@@ -1031,7 +1062,7 @@
body: JSON.stringify({ text }),
});
if (!r.ok) {
melde((await r.json().catch(() => ({}))).fehler || 'Ging nicht.');
melde(sagWas((await r.json().catch(() => ({}))).fehler, 'Ging nicht.'));
ab.disabled = false; return;
}
feld.value = '';
@@ -1167,7 +1198,7 @@
if (!a.ok) {
melde(j.fehler === 'schon_uebernommen'
? 'Daraus gibt es schon eine Aufgabe.'
: (j.fehler || 'Ging nicht.'));
: (sagWas(j.fehler, 'Ging nicht.')));
return;
}
melde('Steht jetzt im Aufgabenbrett – bei dir.');
@@ -1513,7 +1544,7 @@
if (!a.ok) {
/* Der Grund des Servers, nicht „Ging nicht" -- er weiss, ob
der Account fremd war oder die Adresse keine ist. */
sagt.textContent = r.fehler || 'Ging nicht.';
sagt.textContent = sagWas(r.fehler, 'Ging nicht.');
senden.disabled = false;
return;
}
@@ -1561,7 +1592,7 @@
});
const r = await a.json().catch(() => ({}));
if (!a.ok) {
melde(r.fehler || 'Ging nicht.');
melde(sagWas(r.fehler, 'Ging nicht.'));
if (hinweis) hinweis.textContent = alterText;
knopf.disabled = false;
return;
@@ -1677,6 +1708,7 @@
const daten = await a.json();
einstellung = daten.einstellung;
treffBrett = !!daten.treff;
kalenderOffen = daten.kalender_offen !== false;
weiterZeigen(daten.weiter);
await lageHolen();
filterBauen(daten.eintraege || []);
@@ -2081,7 +2113,7 @@
body: JSON.stringify(daten),
});
if (!a.ok) {
$('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
$('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
$('neu').reset(); umschalten(false);
@@ -2154,6 +2186,7 @@
ausgerechnet an der Stelle, an der jemand gerade überlegt, ob er
schreiben darf. */
treffBrett = !!daten.treff;
kalenderOffen = daten.kalender_offen !== false;
await lageHolen();
/* DER WEG ZU DEN REGELN STEHT AUF JEDEM BRETT DES TREFFS
+9 -2
View File
@@ -144,7 +144,14 @@
const block = el('section', 'bw-gruppe');
const kopf = el('div', 'bw-gruppe__kopf');
kopf.append(el('h2', 'bw-gruppe__titel', g.name));
if (g.unter) kopf.append(el('p', 'bw-gruppe__unter', g.unter));
/* `g.text` und NICHT `g.unter` (berichtigt 19.09.2026): Der Server
schickt das Feld `text`, gelesen wurde `unter` -- also immer
`undefined`. Damit fehlten ALLE SECHS Einleitungen, und die
CSS-Regel dafuer lief ins Leere. Genau diese Saetze nehmen dem
Bogen den Verhoer-Charakter ("Kein Test mit richtigen
Antworten"); uebrig blieben sechs nackte Ueberschriften ueber
fuenfzehn Fragen. */
if (g.text) kopf.append(el('p', 'bw-gruppe__unter', g.text));
block.append(kopf);
for (const f of fragen) {
@@ -246,7 +253,7 @@
});
const d = await a.json().catch(() => ({}));
if (!a.ok) {
melde(d.fehler || 'Das hat nicht geklappt.');
melde(sagWas(d.fehler, 'Das hat nicht geklappt.'));
knopf.disabled = false;
knopf.textContent = 'Anfrage abschicken';
return;
+2 -2
View File
@@ -152,7 +152,7 @@
});
if (!a2.ok) {
const d = await a2.json().catch(() => ({}));
melde(d.fehler || 'Ging nicht.');
melde(sagWas(d.fehler, 'Ging nicht.'));
knopf.disabled = false;
return;
}
@@ -268,7 +268,7 @@
});
const d = await a.json().catch(() => ({}));
if (!a.ok) {
melde(d.fehler || 'Das hat nicht geklappt.');
melde(sagWas(d.fehler, 'Das hat nicht geklappt.'));
senden.disabled = false;
senden.textContent = gewaehlt.name + ' – übernehmen';
return;
+3 -3
View File
@@ -227,7 +227,7 @@
}),
});
if (!a.ok) {
meldung.textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
meldung.textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
const r = await a.json();
@@ -375,7 +375,7 @@
+ 'Bereits daraus entstandene Aufgaben bleiben ebenfalls stehen.')) return;
const a = await hole('/workspace/api/calls/' + c.id + '/protokoll', { method: 'DELETE' });
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Löschen ging nicht.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Löschen ging nicht.'));
return;
}
offen.delete(c.id);
@@ -763,7 +763,7 @@
body: JSON.stringify({ link: $('call-raum-feld').value.trim() }),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Speichern ging nicht.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Speichern ging nicht.'));
return;
}
$('call-raum-form').hidden = true;
+28 -7
View File
@@ -674,7 +674,7 @@
body: JSON.stringify({ an }),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'));
return;
}
/* Neu holen statt hier nachzurechnen: Der Server kennt die Grenze
@@ -746,7 +746,7 @@
if (!a.ok) {
/* DIE ABSAGE STEHT IN DER TAFEL, nicht irgendwo anders: Wer
gerade Favoriten festlegt, schaut genau dorthin. */
if (h) h.textContent = d.fehler || 'Ging nicht.';
if (h) h.textContent = sagWas(d.fehler, 'Ging nicht.');
return;
}
auswahlDaten.favoriten = d.favoriten;
@@ -1391,7 +1391,7 @@
});
if (!a.ok) {
const d = await a.json().catch(() => ({}));
markiereAlsGescheitert(vorlaeufig.id, d.fehler || 'Nicht angekommen.');
markiereAlsGescheitert(vorlaeufig.id, sagWas(d.fehler, 'Nicht angekommen.'));
return;
}
const { nachricht } = await a.json();
@@ -1749,7 +1749,7 @@
body: JSON.stringify({ mit: ids }),
});
if (!a.ok) {
$('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
$('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
const raumId = kanalUmbau;
@@ -1765,7 +1765,7 @@
body: JSON.stringify({ kategorie, mit: ids }),
});
if (!a.ok) {
$('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
$('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
const raumId = (await a.json()).raum_id;
@@ -1787,7 +1787,7 @@
body: JSON.stringify({ mit: ids[0] }),
});
if (!a.ok) {
$('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
$('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
raumId = (await a.json()).raum_id;
@@ -1803,7 +1803,7 @@
body: JSON.stringify({ name, mit: ids }),
});
if (!a.ok) {
$('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
$('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
raumId = (await a.json()).raum_id;
@@ -2303,6 +2303,27 @@
/* Kommt man aus einer Benachrichtigung, steht das Gespräch in der
Adresse -- dann direkt hinein. */
const suche = new URLSearchParams(location.search);
/* ?raum=treff -- der Weg von der Treff-Kachel (19.09.2026).
ANLASS: Ein Modi sah ZWEI Kacheln namens "Chat", beide auf
dieselbe Seite -- eine in "Taeglich" (Team), eine in "Community"
(Treff). Sie taten auch dasselbe: Man landete in der
Gespraechsliste und musste den richtigen Raum suchen. Zwei Wege
zum selben Ziel verwirren mehr, als ein fehlender fehlt.
Die Nummer des Treff-Raums steht nicht fest (er wird beim ersten
Start angelegt), deshalb das Wort statt einer Zahl. Gesucht wird
er in der Liste, die ohnehin schon geladen ist -- an denselben
zwei Merkmalen, an denen ihn der Rest der Datei erkennt. */
if (suche.get('raum') === 'treff') {
const t = raeume.find((r) => r.art === 'kanal' && r.kategorie === 'treff');
if (t) { await raumOeffnen(t.id); return; }
/* DRITTER AUSGANG: Gibt es ihn (noch) nicht, bleibt die Liste
stehen -- das ist die richtige Antwort und kein Fehler. Ein
leerer Verlauf waere schlimmer als eine Liste. */
}
const wunsch = Number(suche.get('raum'));
if (Number.isInteger(wunsch) && wunsch > 0) { await raumOeffnen(wunsch); return; }
+2 -2
View File
@@ -436,7 +436,7 @@
creator_id: daten.creator_id }),
});
if (!a.ok) {
alert((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
alert(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'));
b.disabled = false;
return;
}
@@ -503,7 +503,7 @@
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: feld.value, creator_id: daten.creator_id }),
});
if (!a.ok) { alert((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; }
if (!a.ok) { alert(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; }
const r = await a.json();
feld.value = '';
zeichne(verlauf, r.nachrichten, n.ich);
+4 -4
View File
@@ -244,7 +244,7 @@
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(daten),
});
if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; }
if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; }
melde('');
await laden();
}
@@ -256,7 +256,7 @@
body: JSON.stringify({ ziel }),
});
const d = await a.json().catch(() => ({}));
if (!a.ok) { melde(d.fehler || 'Ging nicht.'); return; }
if (!a.ok) { melde(sagWas(d.fehler, 'Ging nicht.')); return; }
melde('');
/* Sagen, wohin es gewandert ist -- und den Weg dorthin anbieten.
Eine stille Uebernahme laesst einen ratlos zurueck. */
@@ -510,7 +510,7 @@
body: JSON.stringify(daten),
});
if (!a.ok) {
$('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
$('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
$('neu').reset(); umschalten(false);
@@ -532,7 +532,7 @@
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(daten),
});
if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; }
if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; }
melde('');
$('saeule-neu').reset();
await laden();
+3 -3
View File
@@ -122,7 +122,7 @@
const a = await hole('/workspace/api/dateien', { method: 'POST', headers: kopf, body: datei });
if (!a.ok) {
const d = await a.json().catch(() => ({}));
melde(d.fehler || 'Hochladen hat nicht geklappt.');
melde(sagWas(d.fehler, 'Hochladen hat nicht geklappt.'));
return;
}
/* Auswahl nach dem Hochladen loeschen -- sonst bekaeme die
@@ -179,7 +179,7 @@
body: JSON.stringify({ status: neu }),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Ändern hat nicht geklappt.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ändern hat nicht geklappt.'));
return;
}
await laden();
@@ -330,7 +330,7 @@
body: JSON.stringify({ freigaben: ids }),
});
speichern.disabled = false;
if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; }
if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; }
await laden();
});
const abbrechen = el('button', 'knopf-still', 'Abbrechen');
+7 -2
View File
@@ -597,7 +597,7 @@
});
if (!a.ok) {
const j = await a.json().catch(() => ({}));
melde(j.fehler || 'Ging nicht.');
melde(sagWas(j.fehler, 'Ging nicht.'));
feld.focus();
return;
}
@@ -840,7 +840,12 @@
jetzt ihre eigene Seite, also stimmte der Titel nicht mehr.
Ein Titel, der etwas anderes verspricht als die Seite zeigt,
ist derselbe Fehler wie der an der Kachel. */
$('titel').textContent = 'Deine Entwicklung';
/* NICHT 'Deine Entwicklung' (geaendert 19.09.2026): So hiess bis
heute AUCH werdegang.html fuer denselben Menschen. Wer aus der
Glocke oder einem Link kam, konnte nicht sagen, auf welcher der
beiden Seiten er stand. Jetzt traegt jede den Namen ihrer
Kachel: hier 'Deine Aufgaben', dort 'Deine Entwicklung'. */
$('titel').textContent = 'Deine Aufgaben';
$('unterzeile').textContent =
'Was im Team über deine Arbeit notiert ist – und was du selbst dazu sagst. '
+ 'Keine Noten, keine Punktzahl.';
+31 -4
View File
@@ -4,6 +4,10 @@
=================================================================== */
(() => {
'use strict';
/* Zählt die Fehlversuche DIESER Sitzung -- nur im Speicher,
nirgends gespeichert. Ab dem zweiten kommt der Kachel-Hinweis. */
let fehlversuche = 0;
const $ = (id) => document.getElementById(id);
const rollen = Array.from(document.querySelectorAll('.rolle'));
@@ -209,10 +213,33 @@
}
}
// Bewusst keine Auskunft darüber, ob Rolle oder Code falsch war.
melde(antwort.status === 429
? 'Zu viele Versuche. Bitte kurz warten.'
: 'Zugangscode stimmt nicht.');
/* KEINE AUSKUNFT DARÜBER, OB ROLLE ODER CODE FALSCH WAR -- das
bleibt so. Aber zwei Dinge waren unnötig hart:
(1) „Bitte kurz warten" sind in Wahrheit bis zu ZEHN MINUTEN
(VERSUCHE_FENSTER_MIN in workspace.js). Wer nach zwanzig
Sekunden nachsieht und dieselbe Meldung liest, hält die
Seite für kaputt. Eine Zahl beruhigt, ein Adjektiv nicht.
(2) Ein RICHTIGER Code an der FALSCHEN Kachel fällt in
dieselbe Absage wie ein erfundener -- der Server sucht nur
unter den Personen der gewählten Rolle. Wer aus der
Community kommt und auf „Modi" tippt (das klingt ja
danach), tippt seinen richtigen Code dreimal Buchstabe für
Buchstabe ein.
Ab dem zweiten Fehlversuch kommt deshalb ein Hinweis auf
die Kachel dazu -- ohne zu sagen, WELCHE die richtige
wäre. Das verrät nichts und erspart die Schleife. */
fehlversuche++;
if (antwort.status === 429) {
melde('Zu viele Versuche. In etwa 10 Minuten geht es wieder.');
} else if (fehlversuche >= 2) {
melde('Zugangscode stimmt nicht. Stimmt die Auswahl oben? '
+ 'Ein Code gehört immer zu genau einer davon.');
} else {
melde('Zugangscode stimmt nicht.');
}
code.select();
} catch {
melde('Keine Verbindung zum Server. Bitte erneut versuchen.');
+36 -11
View File
@@ -149,10 +149,28 @@
if (!knopf || leitung) return;
const voll = offeneEigene >= offenMax;
knopf.disabled = voll;
knopf.title = voll
? `Du hast ${offeneEigene} offene Meldungen. Warte auf eine Antwort, `
+ 'bevor du die nächste aufmachst.'
/* DER GRUND STAND NUR IM `title` (geändert 19.09.2026).
Auf dem Handy gibt es kein Überfahren mit der Maus -- und die
Community kommt von TikTok, also vom Handy. Ein grauer Knopf
ohne Text sieht kaputt aus; wer ein akutes Problem hat, hält das
für eine Panne und geht wieder.
Ein Vorleseprogramm liest einen gesperrten Knopf ohnehin nicht
vor. Der Satz muss also sichtbar daneben stehen, nicht darin. */
const satz = voll
? `Du hast ${offeneEigene} offene Meldungen. Sobald eine davon `
+ 'beantwortet ist, kannst du eine neue aufmachen.'
: '';
knopf.title = satz;
let hinweis = document.getElementById('neu-gesperrt');
if (voll && !hinweis) {
hinweis = document.createElement('p');
hinweis.id = 'neu-gesperrt';
hinweis.className = 'h-gesperrt';
knopf.insertAdjacentElement('afterend', hinweis);
}
if (hinweis) { hinweis.textContent = satz; hinweis.hidden = !voll; }
}
/* =====================================================================
@@ -247,10 +265,16 @@
});
const j = await a.json().catch(() => ({}));
if (!a.ok) {
/* ÜBER sagWas (19.09.2026). Hier stand eine eigene kleine
Tabelle für zwei Kennungen -- alles andere endete bei „Ging
nicht.". Ausgerechnet auf der Seite, auf der jemand mit einem
Problem sitzt, war das die inhaltsleerste Absage im Haus.
Die Sätze für `nicht_dran` und `geschlossen` stehen jetzt in
meldung.js, zusammen mit allen anderen. Zwei Tabellen
nebeneinander laufen beim nächsten Nachschärfen auseinander. */
$('antwort-stand').textContent =
j.fehler === 'nicht_dran' ? 'Die andere Seite ist gerade am Zug.'
: j.fehler === 'geschlossen' ? 'Dieser Fall ist schon abgeschlossen.'
: 'Ging nicht.';
sagWas(j.fehler, 'Die Antwort ließ sich nicht abschicken. Dein Text steht noch da.');
$('antwort-senden').disabled = false;
return;
}
@@ -309,11 +333,12 @@
});
const j = await a.json().catch(() => ({}));
if (!a.ok) {
stand.textContent =
j.fehler === 'zu_viele_offen'
? `Du hast schon ${j.offen} offene Meldungen. Warte auf eine Antwort.`
: j.fehler === 'text_zu_kurz' ? 'Schreib bitte ein paar Sätze mehr.'
: 'Ging nicht.';
/* Die Zahl der offenen Meldungen kennt nur der Server -- der
eine Fall bleibt deshalb hier. Alles andere über sagWas. */
stand.textContent = j.fehler === 'zu_viele_offen'
? `Du hast schon ${j.offen} offene Meldungen. Warte auf eine Antwort, `
+ 'bevor du die nächste aufmachst.'
: sagWas(j.fehler, 'Die Meldung ließ sich nicht abschicken. Dein Text steht noch da.');
$('neu-senden').disabled = false;
return;
}
+2 -2
View File
@@ -1195,7 +1195,7 @@
});
if (!a.ok) {
const f = await a.json().catch(() => ({}));
melde(f.fehler || 'Das hat nicht geklappt.');
melde(sagWas(f.fehler, 'Das hat nicht geklappt.'));
return;
}
const d = await a.json().catch(() => ({}));
@@ -1896,7 +1896,7 @@
body: JSON.stringify(koerper),
});
if (!a.ok) {
$('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
$('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
$('neu').reset();
+1 -1
View File
@@ -39,7 +39,7 @@ window.KI = (() => {
});
if (a.status === 401) { location.assign('/workspace/'); throw new Error('abgemeldet'); }
const d = await a.json().catch(() => ({}));
if (!a.ok) throw new Error(d.fehler || 'Die KI konnte nicht antworten.');
if (!a.ok) throw new Error(sagWas(d.fehler, 'Die KI konnte nicht antworten.'));
return d;
}
+7 -7
View File
@@ -394,7 +394,7 @@
}),
});
if (!a.ok) {
$('tag-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
$('tag-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
d.close();
@@ -506,7 +506,7 @@
});
const d = await a.json().catch(() => ({}));
if (!a.ok) {
$('import-fehler').textContent = d.fehler || 'Die Excel-Datei ließ sich nicht lesen.';
$('import-fehler').textContent = sagWas(d.fehler, 'Die Excel-Datei ließ sich nicht lesen.');
$('datei').value = '';
return;
}
@@ -555,7 +555,7 @@
});
const v = await a.json();
if (!a.ok) {
$('import-fehler').textContent = v.fehler || 'Die Datei konnte nicht gelesen werden.';
$('import-fehler').textContent = sagWas(v.fehler, 'Die Datei konnte nicht gelesen werden.');
return;
}
importSpalten = v.spalten;
@@ -621,7 +621,7 @@
body: JSON.stringify({ text: importText, spalten: importSpalten }),
});
const r = await a.json();
if (!a.ok) { $('import-fehler').textContent = r.fehler || 'Ging nicht.'; return; }
if (!a.ok) { $('import-fehler').textContent = sagWas(r.fehler, 'Ging nicht.'); return; }
$('import-dialog').close();
$('datei').value = '';
importText = '';
@@ -882,7 +882,7 @@
if (!a.ok) {
$('netz-vorschau').hidden = true;
$('netz-los').disabled = true;
$('netz-fehler').textContent = d.fehler || 'Die Tabelle konnte nicht gelesen werden.';
$('netz-fehler').textContent = sagWas(d.fehler, 'Die Tabelle konnte nicht gelesen werden.');
/* Fehlt nur der Tag, ist das kein Fehler, sondern eine Frage --
also wird das Feld hervorgehoben statt nur gemeckert. */
if (d.braucht_tag) $('netz-tag').focus();
@@ -960,7 +960,7 @@
if (!a) { $('netz-los').disabled = false; return; }
const d = await a.json().catch(() => ({}));
if (!a.ok) {
$('netz-fehler').textContent = d.fehler || 'Übernehmen ging nicht.';
$('netz-fehler').textContent = sagWas(d.fehler, 'Übernehmen ging nicht.');
$('netz-los').disabled = false;
return;
}
@@ -1016,7 +1016,7 @@
body: await datei.arrayBuffer(),
});
const d = await a.json().catch(() => ({}));
if (!a.ok) { $('netz-fehler').textContent = d.fehler || 'Ging nicht.'; return; }
if (!a.ok) { $('netz-fehler').textContent = sagWas(d.fehler, 'Ging nicht.'); return; }
text = d.text;
ausExcel = d;
} else if (roh[0] === 0xd0 && roh[1] === 0xcf) {
+182
View File
@@ -0,0 +1,182 @@
/* =====================================================================
WAS DER SERVER SAGT, IN SÄTZEN — 19.09.2026
Filipe: „keine kryptischen oder technischen Fehlermeldungen, sondern
klare Aussagen darüber, was passiert ist und wie man das Problem
lösen kann."
---------------------------------------------------------------------
DAS PROBLEM, NACHGEMESSEN
Der Server antwortet im Fehlerfall mit `{ fehler: "…" }`. Darin
stehen ZWEI verschiedene Dinge, und sie sehen von außen gleich aus:
1. MASCHINENKENNUNGEN — `nicht_verfuegbar`, `nicht_gefunden`,
`ungueltig`. Sie sind fürs Protokoll gedacht, nicht für Augen.
Gezählt am 19.09.2026: 43 verschiedene, an 1 300 Stellen.
2. FERTIGE SÄTZE — z. B. „Woran hast du das gemerkt? Ein Satz
genügt – ohne ihn ist es beim Gespräch nur noch ein Gefühl."
Die sind genau richtig so und sollen unverändert durch.
An 75 Stellen in der Oberfläche stand `textContent = d.fehler` —
ungefiltert. Wer beim Hochladen einer Datei Pech hatte, las also
wörtlich `nicht_verfuegbar` auf dem Bildschirm. Ein Mensch ohne
Vorwissen weiß dann nicht einmal, ob er selbst etwas falsch gemacht
hat.
---------------------------------------------------------------------
DREI AUSGÄNGE, NICHT ZWEI
Das ist dieselbe Regel wie überall im Haus — eine Auskunft, die ihre
Frage nicht beantworten kann, muss das SAGEN und darf nicht schweigen:
BEKANNTE KENNUNG → ihr Satz aus der Tabelle unten.
UNBEKANNTE KENNUNG → der Ersatzsatz des Aufrufers. Sie wird NIE
angezeigt. Eine neue Kennung, die noch
niemand übersetzt hat, ist ein Fehler im
Haus — nicht etwas, das der Benutzer
ausbaden soll.
FERTIGER SATZ → unverändert durch.
Woran „Kennung" erkannt wird: nur Kleinbuchstaben, Ziffern und
Unterstriche, kein Leerzeichen. Ein deutscher Satz hat immer
Leerzeichen und Großbuchstaben; eine Kennung nie. Die Unterscheidung
ist damit nicht geraten, sondern entschieden.
---------------------------------------------------------------------
UND WARUM DIE TABELLE NICHT ALTERN KANN
Eine von Hand gepflegte Liste ist im Haus schon zweimal teuer
geworden (die abgeschriebene Spaltenliste, die feste Umbruchschwelle).
Deshalb steht daneben `pruef-meldungen.mjs`: Sie liest ALLE
Kennungen aus dem Server und wird rot, sobald eine hier fehlt.
Wer morgen eine neue erfindet, kann sie nicht vergessen.
===================================================================== */
(function () {
'use strict';
/* Zu jeder Kennung ein Satz, der zwei Dinge tut: sagen, WAS ist —
und, wo es etwas zu tun gibt, was der nächste Schritt ist.
KEIN „Fehler 400". Keine Zahl, kein englisches Wort, keine
Entschuldigung ohne Inhalt. */
var SAETZE = {
/* ---- Die drei häufigsten ------------------------------------- */
nicht_verfuegbar:
'Der Server antwortet gerade nicht. Nichts wurde gespeichert – '
+ 'versuch es in einer Minute noch einmal.',
nicht_gefunden:
'Das gibt es nicht mehr. Wahrscheinlich hat es jemand gelöscht – '
+ 'lade die Seite neu.',
ungueltig:
'Mit der Eingabe stimmt etwas nicht. Sieh die Felder noch einmal durch.',
/* ---- Anmeldung und Zugang ------------------------------------ */
nicht_angemeldet:
'Du bist nicht mehr angemeldet. Melde dich noch einmal an.',
zu_viele_versuche:
'Zu viele Versuche hintereinander. Warte einen Moment, dann geht es wieder.',
/* Nicht „etwas Älteres ist offen" — es geht um die Bestätigung des
eigenen ALTERS beim ersten Anmelden. Beim ersten Hinsehen hatte
ich es falsch verstanden; der Blick in workspace.js:4864 hat es
geklärt. */
alter_offen:
'Bitte bestätige zuerst dein Alter, dann geht es weiter.',
eigener_code:
'Das ist dein eigener Zugang – den kannst du hier nicht neu vergeben.',
fremde_herkunft:
'Das wurde aus Sicherheitsgründen abgelehnt. Lade die Seite neu '
+ 'und versuch es noch einmal.',
/* ---- Darf ich das? ------------------------------------------- */
nicht_erlaubt: 'Das darfst du hier nicht ändern.',
nur_leitung: 'Das kann nur DogFather oder die rechte Hand.',
nur_gruender: 'Das kann nur, wer das Gespräch angelegt hat.',
hier_nicht: 'An dieser Stelle geht das nicht – abgestimmt wird auf der Wunschliste.',
gesperrt: 'Dafür ist die Abstimmung zu.',
geschlossen: 'Der Fall ist abgeschlossen. Hier kann nichts mehr dazukommen.',
nicht_dran: 'Du bist gerade nicht dran – warte auf die Antwort.',
leitung_meldet_nicht:
'Diesen Weg gibt es für das Team. Du liest die Meldungen, du schreibst sie nicht.',
umgezogen:
'Diese Seite ist umgezogen. Lade sie neu, dann bist du am richtigen Ort.',
/* ---- Da fehlt oder stimmt etwas ------------------------------ */
unvollstaendig: 'Da fehlt noch etwas. Die offenen Stellen sind markiert.',
leer: 'Da steht noch nichts drin.',
betreff_fehlt: 'Der Betreff fehlt noch.',
text_zu_kurz: 'Der Text ist zu kurz – ein ganzer Satz genügt.',
zu_lang: 'Das ist zu lang. Kürze es etwas.',
unbrauchbar: 'Damit kann der Server nichts anfangen. Prüf die Eingabe.',
unbekannte_stufe:
'Diese Auswahl kennt der Server nicht. Lade die Seite neu.',
unbekanntes_zeichen:
'Dieses Zeichen gibt es nicht. Lade die Seite neu.',
keine_datumsspalte:
'In der Datei fehlt eine Spalte mit dem Datum. Ohne sie lässt sich nichts zuordnen.',
keine_zuordnung:
'Die Zeilen lassen sich niemandem zuordnen. Prüf die Überschriften in der Datei.',
/* ---- Gab es schon / gibt es nicht ---------------------------- */
schon_da: 'Das gibt es schon.',
schon_uebernommen: 'Das hat schon jemand übernommen.',
schon_entschieden: 'Darüber wurde bereits entschieden.',
zurueckgenommen: 'Die Nachricht wurde zurückgenommen.',
nichts_zu_aendern: 'Es gab nichts zu ändern – es steht schon so da.',
nichts_zu_speichern:'Es gab nichts zu speichern.',
eintrag_nicht_gefunden: 'Den Eintrag gibt es nicht mehr. Lade die Seite neu.',
aufgabe_nicht_gefunden: 'Die Aufgabe gibt es nicht mehr. Lade die Seite neu.',
keine_fassung: 'Dazu gibt es noch keine gespeicherte Fassung.',
keine_daten: 'Dazu gibt es noch keine Daten.',
kein_protokoll: 'Dazu gibt es kein Protokoll.',
kein_block: 'Diesen Bereich gibt es nicht mehr. Lade die Seite neu.',
kein_hakt:
'Daraus lässt sich kein Schritt machen – dafür müsste der Punkt auf '
+ '„Da hakt es" stehen.',
/* ---- Erst das andere zu Ende -------------------------------- */
uebergang_offen:
'Es läuft schon ein Übergang. Schließ den erst ab.',
zu_viele_offen:
'Du hast schon mehrere Meldungen offen. Warte, bis eine davon beantwortet ist.',
nur_gruppe_oder_kanal:
'Das geht nur in einer Gruppe oder einem Kanal, nicht im Zweiergespräch.'
};
/* Sieht das wie eine Maschinenkennung aus?
Nur Kleinbuchstaben, Ziffern, Unterstriche — und kein Leerzeichen.
Ein Satz aus dem Server hat immer beides nicht. */
function istKennung(t) {
return typeof t === 'string' && t.length > 0 && /^[a-z0-9_]+$/.test(t);
}
/** Aus dem, was der Server schickt, einen Satz machen.
*
* @param {string|undefined} fehler was in `{ fehler: … }` stand
* @param {string} [ersatz] Satz für den Fall, dass nichts
* passt. Er soll zur Stelle
* passen („Anlegen hat nicht
* geklappt."), nicht allgemein
* sein.
* @returns {string} immer ein lesbarer Satz, nie eine Kennung.
*/
window.sagWas = function (fehler, ersatz) {
var standard = ersatz || 'Das hat nicht geklappt. Versuch es noch einmal.';
if (!fehler || typeof fehler !== 'string') return standard;
if (SAETZE[fehler]) return SAETZE[fehler];
/* UNBEKANNTE KENNUNG: der Ersatzsatz, niemals die Kennung selbst.
Sie taucht dafür in der Konsole auf — damit sie jemandem
auffällt, der sie beheben kann, statt dem, der sie nicht
versteht. */
if (istKennung(fehler)) {
try { console.warn('[meldung] Kennung ohne Satz:', fehler); } catch (e) { /* egal */ }
return standard;
}
/* Ein fertiger Satz aus dem Server — unverändert durch. */
return fehler;
};
/* Für die Prüfung, damit sie gegen DIESE Tabelle vergleicht und
nicht gegen eine abgeschriebene zweite. */
window.sagWas.SAETZE = SAETZE;
})();
+52 -6
View File
@@ -237,6 +237,15 @@
function zeigeCode(p) {
$('code-name').textContent = `${p.name} · ${ROLLENNAME[p.rolle] || p.rolle}`;
$('code-wert').textContent = p.code;
/* DIE ADRESSE GEHOERT DAZU (19.09.2026). Sie kommt vom Server, weil
dort die Weiche steht, die Rollen auf Adressen verteilt -- eine
zweite Liste im Browser waere die, die beim naechsten Umzug
stehen bleibt. */
const adr = p.adresse || '';
const a = $('code-adresse');
a.textContent = adr.replace(/^https:\/\//, '');
a.href = adr || '#';
a.parentElement.hidden = !adr;
$('code-kasten').hidden = false;
/* Ab jetzt springt die Seite nicht mehr von selbst weg -- siehe
die Begruendung bei hole(). */
@@ -310,7 +319,7 @@
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ rolle: gewaehlt }),
});
if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; }
if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; }
$('rolle-kasten').hidden = true;
rolleFuer = null;
await laden();
@@ -324,6 +333,43 @@
$('code-wert').textContent = '';
});
/* CODE UND ADRESSE ZUSAMMEN -- der Knopf, den man wirklich braucht.
Wer nur den Code kopiert, schickt eine Zeichenfolge ohne Tuer. */
$('code-beides').addEventListener('click', async () => {
const adr = $('code-adresse').href;
const text = `Dein Zugang zu Team Dogi
Adresse: ${adr}
Code: `
+ `${$('code-wert').textContent}
Der Code gilt einmal fuer die `
+ `Anmeldung. Danach legst du dir dort ein Passwort fest.`;
try {
await navigator.clipboard.writeText(text);
$('code-beides').textContent = 'Kopiert ✓';
setTimeout(() => { $('code-beides').textContent = 'Code und Adresse kopieren'; }, 1800);
} catch {
melde('Kopieren ging nicht — bitte von Hand markieren.');
}
});
/* DAS FENSTER SCHLIESSEN, WAEHREND DER CODE OFFEN STEHT.
Die Seite war gegen das Wegspringen schon geschuetzt (siehe
hole()) -- gegen den Menschen, der den Reiter zumacht, aber nicht.
Ein Code, der in dem Moment verschwindet, ist ein Mensch, der nie
hereinkommt, und niemand merkt es bis zur ersten Nachfrage.
Der Browser zeigt dazu seinen eigenen Text; man kann ihn nicht
bestimmen. Das ist hier auch nicht noetig: Es geht nur darum, dass
ueberhaupt jemand fragt. */
window.addEventListener('beforeunload', (e) => {
if (!codeOffen) return;
e.preventDefault();
e.returnValue = '';
});
$('code-kopieren').addEventListener('click', async () => {
try {
await navigator.clipboard.writeText($('code-wert').textContent);
@@ -432,7 +478,7 @@
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ betreuer_id: w.value || null }),
});
if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; }
if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; }
await laden();
} catch { /* umgeleitet */ } finally { w.disabled = false; }
});
@@ -494,7 +540,7 @@
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ manager_id: w2.value || null }),
});
if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; }
if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; }
await laden();
} catch { /* umgeleitet */ } finally { w2.disabled = false; }
});
@@ -561,7 +607,7 @@
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ aktiv: !p.aktiv }),
});
if (!a.ok) { melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.'); return; }
if (!a.ok) { melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.')); return; }
await laden();
}));
@@ -697,7 +743,7 @@
body: JSON.stringify({ name: eingabe.trim() }),
});
const d = await a.json().catch(() => ({}));
if (!a.ok) { melde(d.fehler || 'Löschen hat nicht geklappt.'); return; }
if (!a.ok) { melde(sagWas(d.fehler, 'Löschen hat nicht geklappt.')); return; }
/* Die Sicherung nennen. Wer gerade etwas Unumkehrbares getan hat,
soll sofort wissen, woraus es sich zurückholen lässt. */
melde(`„${v.person.name}" wurde gelöscht. Sicherung davor: ${d.sicherung}`);
@@ -1068,7 +1114,7 @@
: { name: $('f-name').value }),
});
if (!a.ok) {
$('neu-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
$('neu-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
$('neu').reset(); umschalten(false);
+1 -1
View File
@@ -88,7 +88,7 @@
body: JSON.stringify(daten),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Speichern ging nicht.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Speichern ging nicht.'));
$('stand').textContent = '';
return;
}
+1 -1
View File
@@ -121,7 +121,7 @@
body: JSON.stringify({ pfad: seite.pfad, rolle: rolle.schluessel, erlaubt }),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'));
return;
}
melde(`Gespeichert: „${rolle.name}" darf ${seite.datei} `
+1 -1
View File
@@ -321,7 +321,7 @@
}),
});
if (!a.ok) {
$('e-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
$('e-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
$('entscheidung').reset();
+2 -2
View File
@@ -179,7 +179,7 @@
});
if (!a.ok) {
const f = await a.json().catch(() => ({}));
alert(f.fehler || 'Ging nicht.');
alert(sagWas(f.fehler, 'Ging nicht.'));
b.disabled = false;
b.textContent = 'Durchgegangen und bestätigt';
return;
@@ -256,7 +256,7 @@
body: JSON.stringify({ titel: iTitel.value, wissen_id: Number(sDok.value) || null }),
});
if (!a.ok) {
fehler.textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
fehler.textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
window.schulungBlock(behaelter);
+2 -2
View File
@@ -77,7 +77,7 @@
body: körper ? JSON.stringify(körper) : undefined,
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Das hat nicht geklappt.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Das hat nicht geklappt.'));
return null;
}
melde('');
@@ -522,7 +522,7 @@
knopf.disabled = false;
if (!a) return;
if (!a.ok) {
$('n-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Ging nicht.';
$('n-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.');
return;
}
$('neu-zu').click();
+21 -2
View File
@@ -1103,13 +1103,32 @@
$('zring-unter').textContent = daten.titel || '';
}
/* WENN DER RING NICHTS ZU SAGEN HAT, SAGT ER DAS (19.09.2026).
Vorher stand hier ein frueher `return`. Der liess die Startwerte
aus dem HTML unberuehrt -- und die lauten "–" und "wird geladen".
Bei einer 503 blieb mitten auf der Startseite dauerhaft ein leerer
Ring mit "wird geladen" stehen.
Das ist genau der Fall, den dieselbe Datei an anderer Stelle schon
richtig macht ("Auch der leere Fall wird GESETZT, nicht
uebersprungen"). Ein Platzhalter, der nie ersetzt wird, ist eine
Luege mit Fortschrittsanzeige. */
function ringStumm() {
const p = $('zring-prozent'), u = $('zring-unter');
if (p) p.textContent = '–';
if (u) u.textContent = 'gerade nicht abrufbar';
const ring = $('zring');
if (ring) ring.dataset.stumm = 'ja';
}
async function zentraleLaden() {
let daten;
try {
const antwort = await fetch('/workspace/api/zentrale', { credentials: 'same-origin' });
if (!antwort.ok) return;
if (!antwort.ok) { ringStumm(); return; }
daten = await antwort.json();
} catch { return; } /* Der Rest der Kachel steht trotzdem. */
} catch { ringStumm(); return; } /* Der Rest der Kachel steht trotzdem. */
ringZeichnen(daten);
+2 -2
View File
@@ -46,7 +46,7 @@
body: JSON.stringify({ feld, punkt, ...aenderung }),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Konnte nicht gespeichert werden.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Konnte nicht gespeichert werden.'));
return false;
}
melde('');
@@ -182,7 +182,7 @@
}),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'));
return;
}
const r = await a.json();
+2 -2
View File
@@ -142,7 +142,7 @@
body: datei,
});
const antwort = await a.json().catch(() => ({}));
if (!a.ok) { melde(antwort.fehler || 'Das Hochladen ging nicht.'); sage(''); return; }
if (!a.ok) { melde(sagWas(antwort.fehler, 'Das Hochladen ging nicht.')); sage(''); return; }
bildZeigen(antwort.bild, stand?.name);
sage('Bild gespeichert.');
/* Auch oben rechts in der Kopfleiste soll sofort das neue Bild
@@ -187,7 +187,7 @@
body: JSON.stringify(koerper),
});
const antwort = await a.json().catch(() => ({}));
if (!a.ok) { melde(antwort.fehler || 'Speichern ging nicht.'); return; }
if (!a.ok) { melde(sagWas(antwort.fehler, 'Speichern ging nicht.')); return; }
/* Neu zeichnen, damit die "öffnen"-Verweise zu den frisch
eingetragenen Namen sofort erscheinen. */
zeichne(antwort.steckbrief, KANAELE);
+1 -1
View File
@@ -974,7 +974,7 @@
{ method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: '{}' });
if (!r.ok) {
const d = await r.json().catch(() => ({}));
melde(d.fehler || absage(d));
melde(sagWas(d.fehler, absage(d)));
knopf.disabled = false;
return;
}
+4 -4
View File
@@ -401,7 +401,7 @@
}),
});
if (!a.ok) {
melden((await a.json().catch(() => ({}))).fehler || 'Das hat nicht geklappt.');
melden(sagWas((await a.json().catch(() => ({}))).fehler, 'Das hat nicht geklappt.'));
ja.disabled = false; return;
}
melden('');
@@ -433,7 +433,7 @@
}),
});
if (!a.ok) {
melden((await a.json().catch(() => ({}))).fehler || 'Das hat nicht geklappt.');
melden(sagWas((await a.json().catch(() => ({}))).fehler, 'Das hat nicht geklappt.'));
nein.disabled = false; return;
}
melden('');
@@ -465,11 +465,11 @@
body: JSON.stringify(koerper),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Das hat nicht geklappt.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Das hat nicht geklappt.'));
knopf.disabled = false; return;
}
const d = await a.json().catch(() => ({}));
melde(d.aufgaben ? `${d.aufgaben} Aufgabe(n) angelegt.` : '');
melde(d.aufgaben ? (d.aufgaben === 1 ? 'Eine Aufgabe angelegt.' : `${d.aufgaben} Aufgaben angelegt.`) : '');
await laden();
} catch { knopf.disabled = false; }
}
+1 -1
View File
@@ -115,7 +115,7 @@
});
const daten = await a.json().catch(() => null);
if (!a.ok) {
melde(daten?.fehler || 'Das hat nicht geklappt.');
melde(sagWas(daten?.fehler, 'Das hat nicht geklappt.'));
knopf.disabled = false;
knopf.textContent = alterText;
return;
+3 -3
View File
@@ -191,7 +191,7 @@
body: JSON.stringify({ status, entscheid }),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'));
return;
}
await meldungen();
@@ -295,7 +295,7 @@
body: JSON.stringify({ person_id: m.id, art, grund, tage }),
});
if (!a.ok) {
melde((await a.json().catch(() => ({}))).fehler || 'Ging nicht.');
melde(sagWas((await a.json().catch(() => ({}))).fehler, 'Ging nicht.'));
return;
}
await mitglieder();
@@ -327,7 +327,7 @@
if (!confirm(`Maßnahme gegen ${m.person_name} aufheben?`)) return;
const r = await hole('/workspace/api/treff/massnahme/' + m.id, { method: 'DELETE' });
if (!r.ok) {
melde((await r.json().catch(() => ({}))).fehler || 'Ging nicht.');
melde(sagWas((await r.json().catch(() => ({}))).fehler, 'Ging nicht.'));
return;
}
await massnahmen();
+1 -1
View File
@@ -393,7 +393,7 @@
});
const antwort = await a.json().catch(() => ({}));
if (!a.ok) {
f('cn-fehler').textContent = antwort.fehler || 'Ging nicht.';
f('cn-fehler').textContent = sagWas(antwort.fehler, 'Ging nicht.');
return;
}
f('cn-code-name').textContent = antwort.name;
+1 -1
View File
@@ -122,7 +122,7 @@
});
if (!a.ok) {
const f = await a.json().catch(() => ({}));
nehmen.textContent = f.fehler || 'ging nicht';
nehmen.textContent = sagWas(f.fehler, 'ging nicht');
nehmen.disabled = false;
return;
}
+125 -4
View File
@@ -146,6 +146,31 @@
legende.append(p);
}
wurzel.append(legende);
/* DIE ZAHLEN IN WORTEN (ergänzt 19.09.2026).
Über dem Balken steht „4". Was die 4 ausmacht, stand nur im
`title` — also beim Mausüberfahren, also am Handy nie. Und genau
dort steht jemand damit: im Stream, zwischen zwei Szenen.
Nur Monate mit Inhalt, damit aus sechs Zeilen nicht fünf leere
werden. */
const worte = el('div', 'w-bewegung__worte');
for (const m of bewegung) {
const teile = [
m.vor ? `${m.vor}× nach vorn` : null,
m.geklaert ? `${m.geklaert}× geklärt` : null,
m.neu ? `${m.neu}× zum ersten Mal` : null,
m.zurueck ? `${m.zurueck}× zurückgesetzt` : null,
m.offen ? `${m.offen}× wieder offen` : null,
].filter(Boolean);
if (!teile.length) continue;
const zeile = el('p', 'w-bewegung__wortzeile');
zeile.append(el('strong', 'w-bewegung__wortmonat', monatKurz(m.monat)));
zeile.append(el('span', 'w-bewegung__worttext', teile.join(' · ')));
worte.append(zeile);
}
if (worte.childElementCount) wurzel.append(worte);
return wurzel;
}
@@ -154,6 +179,12 @@
===================================================================== */
function streifenBauen(streifen, spalten) {
const gitter = el('div', 'w-streifen__gitter');
/* DIE SPALTENZAHL KOMMT AUS DEN DATEN (19.09.2026). Vorher rechnete
das CSS sie aus der Breite (`auto-fit`) -- bei 390 px passten
sechs, und alles darueber hinaus fiel in eine zweite,
unbeschriftete Zeile. Dieselbe Loesung wie beim
Bewegungsdiagramm nebenan. */
gitter.style.setProperty('--monate', String(spalten.length));
const kopf = el('div', 'w-streifen__zeile w-streifen__zeile--kopf');
kopf.append(el('span', 'w-streifen__punkt-name', ''));
@@ -164,8 +195,20 @@
const zeile = el('div', 'w-streifen__zeile');
zeile.append(el('span', 'w-streifen__punkt-name', z.titel));
for (const c of z.zellen) {
/* LEER IST NICHT „KANN ICH NICHT SAGEN“ (getrennt 19.09.2026).
Beim Durchgehen aus Benutzersicht aufgefallen: Hier stand für
„nie etwas gesetzt“ ein Mittelpunkt `·` — und „Kann ich nicht
sagen“ trägt im Katalog den Halbgeviertstrich `–`. Zwei kleine
waagerechte Striche in Nachbarzellen, und der Unterschied
zwischen ihnen ist genau der, den dieses Haus sonst sehr ernst
nimmt: „niemand hat hingesehen“ gegen „jemand hat hingesehen
und konnte es nicht sagen“.
Leer ist jetzt WIRKLICH leer — kein Zeichen, dafür ein
gestrichelter Rahmen. Man sieht das Fehlen, statt es zu lesen. */
const zelle = el('span', 'w-streifen__zelle',
c.stufe ? zeichenVon(c.stufe) : '·');
c.stufe ? zeichenVon(c.stufe) : '');
if (c.stufe) {
zelle.dataset.stufe = c.stufe;
if (c.alt) zelle.dataset.alt = 'ja';
@@ -180,7 +223,35 @@
}
gitter.append(zeile);
}
return gitter;
/* DIE ZEICHENERKLÄRUNG — und sie ist der Grund, warum die Zeichen
überhaupt etwas taugen (ergänzt 19.09.2026).
Im Kopf dieser Datei steht: „Jede Zelle trägt das Zeichen ihrer
Stufe als Text. Wer Farben schlecht unterscheidet, liest genau
dasselbe." Das stimmt nur, wenn irgendwo steht, WAS die Zeichen
heißen. Tat es nicht — die Bedeutung steckte ausschließlich im
Mausüberfahren, und auf einem Handy gibt es das nicht.
Gebaut aus `stufen` vom Server, nicht abgeschrieben: Wer dort ein
Zeichen ändert, ändert die Erklärung mit. */
const legende = el('div', 'w-legende w-legende--streifen');
for (const st of stufen) {
const p = el('span', 'w-legende__punkt');
const z = el('span', 'w-legende__zeichen', st.zeichen);
z.dataset.stufe = st.schluessel;
p.append(z, el('span', 'w-legende__text', st.name));
legende.append(p);
}
const leerP = el('span', 'w-legende__punkt');
const leerZ = el('span', 'w-legende__zeichen', '');
leerZ.dataset.stufe = 'leer';
leerP.append(leerZ, el('span', 'w-legende__text', 'noch nichts gesetzt'));
legende.append(leerP);
const huelle = el('div');
huelle.append(gitter, legende);
return huelle;
}
/* =====================================================================
@@ -377,9 +448,59 @@
stufen = d.stufen || stufen;
$('mich-satz').textContent = d.satz;
$('mich-hinweis').textContent = d.hinweis;
/* DREI DINGE, DIE HIER GEFEHLT HABEN (19.09.2026).
Beim Durchgehen aus der Sicht dessen, dem diese Seite gehört,
fiel auf: Er bekam Balken ohne die Erklärung, die die Leitung
daneben bekommt („Nach oben: weitergekommen"). Er bekam eine
Aufforderung ohne Adressaten („Frag danach" — wen?). Und wenn
sich nichts bewegt hatte, bekam er ein leeres Element, also
nichts: eine Seite, die einlädt und dann schweigt.
Alle drei sind hier behoben. Es ist derselbe Gedanke wie überall
im Haus: Der leere Zustand ist kein Sonderfall, er ist der
häufigste Fall — vor allem am Anfang. */
const etwas = d.bewegung?.some((m) => m.vor || m.zurueck || m.neu || m.geklaert);
$('mich-bewegung').replaceChildren(
etwas ? bewegungBauen(d.bewegung, d.spalten) : el('span'));
if (etwas) {
$('mich-bewegung').replaceChildren(bewegungBauen(d.bewegung, d.spalten));
$('mich-lesehilfe').textContent =
'Nach oben: ein Punkt ist weitergekommen. Nach unten: er wird '
+ 'gerade anders gesehen als vorher – das ist kein Rückschritt, '
+ 'sondern meist nur ein genauerer Blick.';
$('mich-lesehilfe').hidden = false;
} else {
/* KEIN DIAGRAMM ÜBER NICHTS. Ein Raster aus leeren Spalten sieht
aus, als sei etwas kaputt. Ein Satz ist eine Auskunft. */
$('mich-bewegung').replaceChildren(leerKasten(
'Hier ist noch nichts zu sehen',
'Sobald jemand aus der Leitung eine Einschätzung setzt oder '
+ 'ändert, erscheint sie hier als Bewegung. Am Anfang ist diese '
+ 'Seite leer – das ist der Normalfall, kein Fehler.'));
$('mich-lesehilfe').hidden = true;
}
/* DER WEG WEITER. „Frag danach" ohne einen Ort ist eine Sackgasse
mit freundlichem Ton. */
const wege = $('mich-wege');
wege.replaceChildren();
const k1 = el('a', 'knopf knopf--klein', 'Deine Aufgaben ansehen');
k1.href = 'entwicklung.html';
const k2 = el('a', 'knopf knopf--klein w-weg--leise', 'Jemanden fragen');
k2.href = 'chat.html';
wege.append(k1, k2);
wege.hidden = false;
}
/** Ein Kasten für „hier ist noch nichts" — Überschrift plus Satz.
*
* ALS EIGENE FUNKTION, weil dieser Fall im Haus überall vorkommt und
* überall gleich aussehen soll. Ein leerer Zustand, der jedes Mal
* anders gebaut ist, liest sich wie ein Fehler. */
function leerKasten(titel, satz) {
const k = el('div', 'w-leer');
k.append(el('p', 'w-leer__titel', titel));
k.append(el('p', 'w-leer__satz', satz));
return k;
}
/* ---- Start ---------------------------------------------------------- */
+1 -1
View File
@@ -601,7 +601,7 @@
a = await hole('/workspace/api/wissen', { method: 'POST', headers: kopf, body: datei });
}
if (!a.ok) {
$('n-fehler').textContent = (await a.json().catch(() => ({}))).fehler || 'Das hat nicht geklappt.';
$('n-fehler').textContent = sagWas((await a.json().catch(() => ({}))).fehler, 'Das hat nicht geklappt.');
return;
}
const kat = aufbau.kategorien.find((k) => k.schluessel === angaben.kategorie);
+14 -12
View File
@@ -12,19 +12,19 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -291,10 +291,12 @@
</form>
</dialog>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/aufgaben.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/aufgaben.js?v=202609191506" defer></script>
</body>
</html>
+15 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/automation.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/automation.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -168,10 +168,12 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/automation.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/automation.js?v=202609191506" defer></script>
</body>
</html>
+41 -14
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -53,9 +53,34 @@
<div>
<p class="marke">Für dich</p>
<h1 class="titel">Wie geht's dir?</h1>
<!-- ================================================================
DIE EINE AUSNAHME GEHÖRT DAZU (ergänzt 19.09.2026).
Hier stand: „Niemand aus dem Team sieht deine Antworten – auch
DogFather nicht." Punkt. Das ist fast wahr und deshalb
gefährlich: Aus denselben Antworten entsteht eine Ampel für die
Leitung (workspace-entwicklung.js). Sie ist sauber gebaut – sie
nennt keine Namen, keinen Punkt, kein Datum, und unter drei
Personen schweigt sie ganz. Aber sie existiert.
Der ehrliche Satz war längst geschrieben und wurde vom Server
sogar mitgeliefert (`block.text`) – nur hat ihn nie jemand
angezeigt. Er steht jetzt hier im HTML und nicht im Skript:
Wer diese Seite öffnet, soll ihn lesen, BEVOR er tippt, nicht
nachdem ein Abruf zurückgekommen ist.
Wenn jemand später davon erfährt und die Seite hatte „niemand"
gesagt, ist das Vertrauen weg – und mit ihm die ganze Funktion.
============================================================= -->
<p class="unterzeile">
Ein paar Fragen über dich. Niemand aus dem Team sieht deine Antworten –
auch DogFather nicht.
Ein paar Fragen über dich. Deine Antworten liest niemand – auch
DogFather nicht.
</p>
<p class="unterzeile unterzeile--zusatz">
Das Einzige, was nach außen dringt: ob es im Team gerade
<em>jemandem</em> zu viel ist. Ohne Namen, ohne Frage, ohne Datum –
und erst ab drei Personen im Team, weil es darunter dasselbe wäre
wie ein Name.
</p>
</div>
</section>
@@ -105,9 +130,11 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/befinden.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/befinden.js?v=202609191506" defer></script>
</body>
</html>
+20 -18
View File
@@ -12,24 +12,24 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202609191506" />
<!-- Wegen des Blocks „Willst du dazugehoeren?" auf dem Brett
„Mitmachen". Eine eigene Kopie der Regel hier waere die zweite
Stelle, an der dieselbe Sache anders aussieht. -->
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -251,14 +251,16 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/checkliste.js?v=202609191147" defer></script>
<script src="assets/js/ampel.js?v=202609191147" defer></script>
<script src="assets/js/schulung.js?v=202609191147" defer></script>
<script src="assets/js/vorlagen.js?v=202609191147" defer></script>
<script src="assets/js/bereich.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/checkliste.js?v=202609191506" defer></script>
<script src="assets/js/ampel.js?v=202609191506" defer></script>
<script src="assets/js/schulung.js?v=202609191506" defer></script>
<script src="assets/js/vorlagen.js?v=202609191506" defer></script>
<script src="assets/js/bereich.js?v=202609191506" defer></script>
</body>
</html>
+16 -15
View File
@@ -7,17 +7,17 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -108,10 +108,11 @@
</main>
<!-- Der Fuß gehört zu jeder Seite - siehe pruef-css-klassen. -->
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/auskunft.js?v=202609191147" defer></script>
<script src="assets/js/bewerben.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/auskunft.js?v=202609191506" defer></script>
<script src="assets/js/bewerben.js?v=202609191506" defer></script>
</body>
</html>
+16 -14
View File
@@ -7,16 +7,16 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -80,10 +80,12 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/auskunft.js?v=202609191147" defer></script>
<script src="assets/js/bewerbungen.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/auskunft.js?v=202609191506" defer></script>
<script src="assets/js/bewerbungen.js?v=202609191506" defer></script>
</body>
</html>
+16 -14
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/calls.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/calls.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -89,11 +89,13 @@
</main>
<script src="assets/js/ki.js?v=202609191147" defer></script>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/calls.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/ki.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/calls.js?v=202609191506" defer></script>
</body>
</html>
+15 -14
View File
@@ -12,26 +12,26 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
Dialog zu sehen bekam. -->
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/chat.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/chat.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -367,15 +367,16 @@
</form>
</dialog>
<script src="assets/js/bereiche.js?v=202609191147"></script>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147"></script>
<script src="assets/js/glocke.js?v=202609191147"></script>
<script src="assets/js/bereiche.js?v=202609191506"></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506"></script>
<script src="assets/js/glocke.js?v=202609191506"></script>
<!-- ANRUF VOR CHAT (18.09.2026): anruf.js stellt `window.anrufStarten`
bereit, und chat.js baut die Knoepfe nur, wenn es das gibt.
Andersherum waere die Reihenfolge eine stille Abhaengigkeit --
die Knoepfe fehlten, und niemand wuesste warum. -->
<script src="assets/js/anruf.js?v=202609191147"></script>
<script src="assets/js/chat.js?v=202609191147"></script>
<script src="assets/js/anruf.js?v=202609191506"></script>
<script src="assets/js/chat.js?v=202609191506"></script>
</body>
</html>
+17 -15
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/content.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/content.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -179,12 +179,14 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/checkliste.js?v=202609191147" defer></script>
<script src="assets/js/vorlagen.js?v=202609191147" defer></script>
<script src="assets/js/content.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/checkliste.js?v=202609191506" defer></script>
<script src="assets/js/vorlagen.js?v=202609191506" defer></script>
<script src="assets/js/content.js?v=202609191506" defer></script>
</body>
</html>
+16 -5
View File
@@ -36,15 +36,15 @@
<link rel="preload" as="image" href="assets/img/buehne/crew-gate-hoch-580.webp"
type="image/webp" media="(max-width: 899px)" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<!-- Diese Wand steht NUR auf der Adresse von Team Dogi (ueberall sonst
404, siehe crewWeiche). Sie darf das Haus deshalb direkt nennen
statt ueber den Platzhalter haus.css zu gehen -- und sie MUSS es:
Eine Zugangswand, die sich erst nach dem Umbiegen faerbt, waere
die eine Seite, auf der man das Umfaerben sieht. -->
<link rel="stylesheet" href="assets/css/crew-haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/crew-haus.css?v=202609191506" />
</head>
<body class="gate gate--crew">
@@ -333,12 +333,23 @@
</button>
</form>
<!-- EINE TÜR BRAUCHT EINE KLINGEL (ergänzt 19.09.2026).
Hier standen vier Kacheln, ein Feld „Code eingeben" und ein
Knopf. Wer keinen Code hat, fand keinen einzigen Satz, der ihm
sagt, was jetzt zu tun ist. Für die Community ist das besonders
teuer: Sie ist die einzige Rolle, die von außen kommt und
niemanden im Haus kennt. -->
<p class="fuss fuss--woher">Noch keinen Code? Frag im Live nach – Team&nbsp;Dogi gibt ihn dir persönlich.</p>
<p class="fuss">
Verschlüsselte Verbindung · Zugriffe werden protokolliert
</p>
</main>
</div>
<script src="assets/js/gate.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/gate.js?v=202609191506" defer></script>
</body>
</html>
+4 -4
View File
@@ -22,25 +22,25 @@
},
"icons": [
{
"src": "/assets/img/app-symbole/crew-192.png?v=202609191147",
"src": "/assets/img/app-symbole/crew-192.png?v=202609191506",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/img/app-symbole/crew-512.png?v=202609191147",
"src": "/assets/img/app-symbole/crew-512.png?v=202609191506",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609191147",
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609191506",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609191147",
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609191506",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
+15 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/dateien.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/dateien.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -108,10 +108,12 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/dateien.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/dateien.js?v=202609191506" defer></script>
</body>
</html>
+14 -12
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -106,9 +106,11 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/entwicklung.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/entwicklung.js?v=202609191506" defer></script>
</body>
</html>
+13 -11
View File
@@ -7,13 +7,13 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/hilfe.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/hilfe.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -141,9 +141,11 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/hilfe.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/hilfe.js?v=202609191506" defer></script>
</body>
</html>
+15 -4
View File
@@ -30,9 +30,9 @@
<link rel="preload" as="image" href="assets/img/buehne/gate-hoch-580.webp"
type="image/webp" media="(max-width: 899px)" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
</head>
<body class="gate">
@@ -395,12 +395,23 @@
</button>
</form>
<!-- EINE TÜR BRAUCHT EINE KLINGEL (ergänzt 19.09.2026).
Hier standen vier Kacheln, ein Feld „Code eingeben" und ein
Knopf. Wer keinen Code hat, fand keinen einzigen Satz, der ihm
sagt, was jetzt zu tun ist. Für die Community ist das besonders
teuer: Sie ist die einzige Rolle, die von außen kommt und
niemanden im Haus kennt. -->
<p class="fuss fuss--woher">Noch keinen Code? Den bekommst du von deinem Manager oder von DogFather.</p>
<p class="fuss">
Verschlüsselte Verbindung · Zugriffe werden protokolliert
</p>
</main>
</div>
<script src="assets/js/gate.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/gate.js?v=202609191506" defer></script>
</body>
</html>
+16 -14
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/kalender.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/kalender.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -377,11 +377,13 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/kalender.js?v=202609191147" defer></script>
<script src="assets/js/kalender-abo.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/kalender.js?v=202609191506" defer></script>
<script src="assets/js/kalender-abo.js?v=202609191506" defer></script>
</body>
</html>
+14 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/leistung.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/leistung.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -444,10 +444,11 @@
</form>
</dialog>
<script src="assets/js/wahl.js?v=202609191147"></script>
<script src="assets/js/bereiche.js?v=202609191147"></script>
<script src="assets/js/kopf.js?v=202609191147"></script>
<script src="assets/js/glocke.js?v=202609191147"></script>
<script src="assets/js/leistung.js?v=202609191147"></script>
<script src="assets/js/meldung.js?v=202609191506"></script>
<script src="assets/js/wahl.js?v=202609191506"></script>
<script src="assets/js/bereiche.js?v=202609191506"></script>
<script src="assets/js/kopf.js?v=202609191506"></script>
<script src="assets/js/glocke.js?v=202609191506"></script>
<script src="assets/js/leistung.js?v=202609191506"></script>
</body>
</html>
+44 -16
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/personen.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/personen.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -355,12 +355,38 @@
<div class="code-kasten" id="code-kasten" hidden role="status" aria-live="polite">
<p class="code-kasten__schild">Zugangscode für <span id="code-name"></span></p>
<p class="code-kasten__code" id="code-wert"></p>
<!-- ==============================================================
WAS HIER FEHLTE (ergänzt 19.09.2026)
Der Kasten sagte „jetzt weitergeben" – und gab die Hälfte
nicht mit, die man weitergeben muss: WO sich die Person damit
anmeldet. DogFather ruft den Code durchs Zimmer, und der
andere fragt „und wo?".
Zweitens fehlte der Rettungsweg. „Danach nicht mehr abrufbar"
stimmt für DIESEN Code – aber man kann jederzeit einen neuen
vergeben. Wer das nicht weiß, glaubt, er habe einen Menschen
angelegt, der nie hereinkommt.
Drittens: „Bitwarden" ist ein Fachwort im wichtigsten Satz.
Es steht jetzt am Ende und in Klammern.
=========================================================== -->
<p class="code-kasten__zeile">
<span class="code-kasten__was">Anmelden unter</span>
<a class="code-kasten__adresse" id="code-adresse" href="#" target="_blank" rel="noopener">…</a>
</p>
<p class="code-kasten__hinweis">
Dieser Code wird <strong>nur jetzt</strong> angezeigt und ist danach nicht mehr
abrufbar. Sicher weitergeben und in Bitwarden ablegen.
Der Code wird <strong>nur jetzt</strong> angezeigt. Gib ihn gleich weiter –
persönlich oder über einen Weg, dem du traust.
</p>
<p class="code-kasten__rettung">
Verloren? Kein Problem: In der Liste bei dieser Person auf
<strong>„Neuer Code"</strong> – der alte gilt dann nicht mehr.
(Zum Aufbewahren: Bitwarden.)
</p>
<div class="neu__knoepfe">
<button type="button" class="knopf knopf--klein" id="code-kopieren">Kopieren</button>
<button type="button" class="knopf knopf--klein" id="code-beides">Code und Adresse kopieren</button>
<button type="button" class="knopf knopf--klein" id="code-kopieren">Nur den Code</button>
<button type="button" class="abmelden" id="code-schliessen">Verstanden, schließen</button>
</div>
</div>
@@ -423,10 +449,12 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/personen.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/personen.js?v=202609191506" defer></script>
</body>
</html>
+16 -14
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/profil.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/profil.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -214,11 +214,13 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/steckbrief.js?v=202609191147" defer></script>
<script src="assets/js/profil.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/steckbrief.js?v=202609191506" defer></script>
<script src="assets/js/profil.js?v=202609191506" defer></script>
</body>
</html>
+13 -12
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -134,9 +134,10 @@
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
die seit dem 07.09. jede Seite danach absucht. -->
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/rechte.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/rechte.js?v=202609191506" defer></script>
</body>
</html>
+16 -14
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/report.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/report.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -106,11 +106,13 @@
</main>
<script src="assets/js/ki.js?v=202609191147" defer></script>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/report.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/ki.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/report.js?v=202609191506" defer></script>
</body>
</html>
+15 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/scouting.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/scouting.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -173,10 +173,12 @@
</div>
</div>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/scouting.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/scouting.js?v=202609191506" defer></script>
</body>
</html>
+14 -12
View File
@@ -12,21 +12,21 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
<link rel="stylesheet" href="assets/css/heim.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/heim.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -678,10 +678,12 @@
</p>
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/start.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/start.js?v=202609191506" defer></script>
</body>
</html>
+16 -14
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -85,11 +85,13 @@
</main>
<script src="assets/js/ki.js?v=202609191147" defer></script>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/startcheck.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/ki.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/startcheck.js?v=202609191506" defer></script>
</body>
</html>
+16 -14
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/profil.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/profil.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -150,11 +150,13 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/auskunft.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/steckbrief.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/auskunft.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/steckbrief.js?v=202609191506" defer></script>
</body>
</html>
+15 -13
View File
@@ -7,18 +7,18 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191506" />
<!-- Wegen des Kastens „Anfragen“ ueber der Liste. Eine eigene Kopie
der Regel hier waere die zweite Stelle, an der dieselbe Sache
anders aussieht. -->
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -111,9 +111,11 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/talente.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/talente.js?v=202609191506" defer></script>
</body>
</html>
+15 -13
View File
@@ -7,20 +7,20 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/team.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/team.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -110,10 +110,12 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/team.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/team.js?v=202609191506" defer></script>
</body>
</html>
+14 -13
View File
@@ -7,20 +7,20 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/team.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/team.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<!-- `data-buehne` steht hier fest und nicht im Skript: Die Kachel zu
@@ -67,7 +67,7 @@
<p class="t-kopf__grenze">
Was hier steht, ist eine Merkliste für euch beide – keine Bewertung
von Menschen. „Offen“ heißt: darüber habt ihr noch nicht gesprochen.
Bewertet wird dort, wo die Punkte stehen; jede Zahl führt dorthin.
Eingeschätzt wird dort, wo die Punkte stehen; jede Zahl führt dorthin.
</p>
</div>
</div>
@@ -84,9 +84,10 @@
Kaputt ist dabei nichts -- er funktioniert. Genau deshalb faellt
es niemandem auf, und deshalb prueft pruef-css-klassen die
ABHAENGIGKEIT statt der Optik. Hier hat sie angeschlagen. -->
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/teamlage.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/teamlage.js?v=202609191506" defer></script>
</body>
</html>
+13 -11
View File
@@ -7,13 +7,13 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/bereich.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -112,9 +112,11 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/teilen.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/teilen.js?v=202609191506" defer></script>
</body>
</html>
+14 -12
View File
@@ -7,14 +7,14 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -126,9 +126,11 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/treff-moderation.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/treff-moderation.js?v=202609191506" defer></script>
</body>
</html>
+14 -13
View File
@@ -7,18 +7,18 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/treff.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein. Auf der Adresse des
Treffs biegt der Server genau diesen Dateinamen auf crew-haus.css
um, und die Seite kommt schon beim ERSTEN Abruf in Lila und
Babyblau an -- ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -221,10 +221,11 @@
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
die seit dem 07.09. jede Seite danach absucht. -->
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/auskunft.js?v=202609191147" defer></script>
<script src="assets/js/treff-regeln.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/auskunft.js?v=202609191506" defer></script>
<script src="assets/js/treff-regeln.js?v=202609191506" defer></script>
</body>
</html>
+15 -13
View File
@@ -12,22 +12,22 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
Dialog "Creator anlegen". -->
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -149,11 +149,13 @@
</main>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/uebersicht.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/uebersicht.js?v=202609191506" defer></script>
</body>
</html>
+13 -11
View File
@@ -7,13 +7,13 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/unsere-seiten.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/unsere-seiten.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -217,9 +217,11 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/unsere-seiten.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/unsere-seiten.js?v=202609191506" defer></script>
</body>
</html>
+28 -13
View File
@@ -7,15 +7,15 @@
<meta name="robots" content="noindex, nofollow" />
<link rel="manifest" href="/workspace/app.webmanifest" />
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/werdegang.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/werdegang.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -122,18 +122,33 @@
</section>
<!-- ---- Die eigene Sicht (Modi) ---------------------------------- -->
<!-- ==================================================================
WAS DER MENSCH SELBST SIEHT.
Hier steht kein fremder Name, keine Beobachtung über jemand
anderen und kein Anlasstext. Nur: ob sich etwas bewegt hat.
Die Lesehilfe und die zwei Wege am Ende sind am 19.09.2026
dazugekommen. Vorher standen hier Balken ohne Erklärung und ein
„Frag danach" ohne Adressaten – eine Sackgasse mit freundlichem
Ton.
============================================================== -->
<section id="mich-block" hidden>
<h2 class="e-abschnitt">Deine Entwicklung</h2>
<p class="w-satz" id="mich-satz">…</p>
<p class="w-lesehilfe" id="mich-lesehilfe" hidden></p>
<div id="mich-bewegung"></div>
<p class="w-fuss" id="mich-hinweis"></p>
<div class="w-wege" id="mich-wege" hidden></div>
</section>
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/werdegang.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/werdegang.js?v=202609191506" defer></script>
</body>
</html>
+15 -13
View File
@@ -12,20 +12,20 @@
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
eine Flaeche sind statt zweier. -->
<meta name="theme-color" content="#06090f" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191147" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191147" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/wissen.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191147" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609191506" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609191506" />
<link rel="stylesheet" href="assets/css/gate.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/start.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/wissen.css?v=202609191506" />
<link rel="stylesheet" href="assets/css/module.css?v=202609191506" />
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
genau diesen Dateinamen auf crew-haus.css um, und die Seite
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
ohne Umfaerben im Browser, ohne Flackern. -->
<link rel="stylesheet" href="assets/css/haus.css?v=202609191147" />
<link rel="stylesheet" href="assets/css/haus.css?v=202609191506" />
</head>
<body class="start">
@@ -190,10 +190,12 @@
</main>
<script src="assets/js/wahl.js?v=202609191147" defer></script>
<script src="assets/js/bereiche.js?v=202609191147" defer></script>
<script src="assets/js/kopf.js?v=202609191147" defer></script>
<script src="assets/js/glocke.js?v=202609191147" defer></script>
<script src="assets/js/wissen.js?v=202609191147" defer></script>
<script src="assets/js/meldung.js?v=202609191506" defer></script>
<script src="assets/js/wahl.js?v=202609191506" defer></script>
<script src="assets/js/bereiche.js?v=202609191506" defer></script>
<script src="assets/js/kopf.js?v=202609191506" defer></script>
<script src="assets/js/glocke.js?v=202609191506" defer></script>
<script src="assets/js/wissen.js?v=202609191506" defer></script>
</body>
</html>