Compare commits

...
386 Commits
Author SHA1 Message Date
DogFatherGitandClaude Opus 5 0e522181e2 Pruefungen: der Rueckgabewert 127, der "in Ordnung" meldete
pruef-call-kategorien gab dreimal von dreimal 127 zurueck -- NACH der
Zeile "ALLES IN ORDNUNG". Ursache ist eine libuv-Assertion auf Windows:

  Assertion failed: !(handle->flags & UV_HANDLE_CLOSING),
  file src\win\async.c, line 94

`process.exit()` schlaegt zu, waehrend Playwright seinen Transportkanal
noch abbaut. Das Ergebnis stimmte, der Rueckgabewert log. In einem
Sammellauf zaehlt so ein Lauf als Fehlschlag, obwohl nichts fehlschlug --
und wer sich angewoehnt, den Rueckgabewert dieser einen Datei zu
ignorieren, uebersieht spaeter den echten.

Meine Notiz sagte "sporadisch". Es war drei von drei. Auch eigene
Notizen altern.

MEIN ERSTER FIX WAR DIE ELEGANTERE LOESUNG UND DIE SCHLECHTERE.

Ich wollte keine feste Pause -- 400 ms sind eine Rechnung auf DIESEM
Rechner, und auf einem langsameren waere der Fehler still
zurueckgekommen. Also: auf das Ereignis "disconnected" warten, danach
zwoelf Runden der Ereignisschleife (setImmediate). Sauber begruendet.
Gemessen: ZWEI VON DREI Laeufen weiterhin 127. Die feste Pause, die ich
fuer schlechter hielt, war zweimal gruen.

Die Annahme war falsch: "disconnected" meldet, dass die Verbindung weg
ist, nicht dass der Kanal abgebaut ist -- und setImmediate gibt der
Schleife Durchlaeufe, aber keine ZEIT. Der Kindprozess braucht echte
Millisekunden. Eine stimmige Herleitung ersetzt keine Messung.

Jetzt beides: erst das Ereignis (richtige Ordnung), dann eine zeitliche
Reserve von 600 ms gegen gemessene 400, ueber PRUEF_ABBAU_MS
einstellbar. Fuenf Laeufe hintereinander gruen.

Neu: server/helfer-beenden.mjs (sauberBeenden) und
tools/mess-rueckgabewerte.sh -- letzteres misst alle 42 Browser-
Pruefungen mit demselben Muster daraufhin, ob noch weitere "in Ordnung"
melden und trotzdem einen Fehlercode zurueckgeben. Laeuft nacheinander,
nicht parallel: Zwei gleichzeitige Prueflaeufe sind kein Prueflauf.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-08 01:14:57 +02:00
DogFatherGitandClaude Opus 5 a9f3212da3 Startseite: die tote Mitte der Konsole bekommt drei Ablesungen
Gemessen, nicht geschaetzt: Auf 1440 px lagen zwischen dem Ende des
Textes und der Uhr rund 300 px Leere. Dort stehen jetzt HEUTE (Termine),
ALS NAECHSTES (Uhrzeit) und OFFEN (Punkte, Farbe nach Lage).

Keine zweite Zaehlung: Die Zahlen kommen aus denselben zwei Quellen, die
die Kacheln darunter fuellen. Zwei Rechenwege fuer dieselbe Zahl laufen
auseinander, und dann stehen zwei Wahrheiten auf einem Bildschirm.

EINE Fassung mit Stegen, nicht drei Kaestchen. Der erste Anlauf gab jedem
Wert eine eigene Fraesung -- im Bild sah das aus wie aufgeklebte
Plaettchen. Ein Instrumentenblock ist EIN eingelassenes Feld, in dem
Stege trennen; das Licht laeuft dann einmal ueber eine Kante statt
sechsmal. Auf dem Handy geht der Block auf volle Breite und richtet sich
nach der Uhr, nicht nach dem Rand.

EINE ANNAHME KORRIGIERT: In meiner Merkliste stand "Werktisch-Aufbau
nach VanVans Business Hub, Prozentring links". Im Hub nachgesehen -- es
gibt dort keinen Ring und keinen solchen Aufbau, nur eine schlichte
buehne-hero. Filipes Verweis galt der UHR, und die ist laengst gebaut.
Meine eigenen Notizen altern wie jede andere Bestandsliste.

DREI BEFUNDE AUS EIGENEN PRUEFUNGEN, alle behoben:

1. pruef-css-klassen: Die Zahl der Schriftgroessen unter 11,5 px war um
   genau eine gestiegen -- .stand__schild stand auf 9,3 px. Gesperrte
   Grossbuchstaben in 9 px liest man nicht, man erraet sie. Jetzt 11,5 px
   mit etwas engerer Sperrung, damit drei Schilder bei 320 px weiterhin
   nebeneinander passen (nachgemessen: 287 px, nichts abgeschnitten).

2. pruef-struktur: pruef-arten.mjs bildete das Tagesdatum aus UTC. Nachts
   zwischen 00:00 und 02:00 waere sie rot geworden, ohne dass am Code
   etwas falsch ist. Derselbe Fehler war mir am selben Abend schon im
   Messskript passiert -- dort hatte ich "Heute=0" gemessen und den Code
   verdaechtigt, der richtig lag.

3. Beim Bauen fast eingebaut: margin-left:auto von der Uhrgruppe
   genommen, weil der neue Block sie ja schon nach rechts schiebt. Er tut
   das nur, solange er da ist -- bis zur ersten Antwort steht er auf
   hidden, und die Uhr waere sichtbar weggesprungen.

Neu: server/pruef-ueberlappung.mjs. Misst auf 5 Seiten x 4 Breiten, ob
ein Bedienelement ueber einem anderen liegt (am 06.09. lag der
Sicht-Umschalter bei 412 px auf zwoelf Seiten ueber dem Chat-Knopf).
Ueberlappungen INNERHALB eines Bedienelements zaehlen nicht -- ein
durchsichtiges select ueber seinem eigenen Schild ist die uebliche
Bauart, und eine Warnung, die immer kommt, ist keine Warnung mehr. Mit
Gegenprobe: ein absichtlich verschobener Knopf muss erkannt werden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-08 01:05:54 +02:00
DogFatherGitandClaude Opus 5 de06ce0227 Kalender: sechs neue Terminarten, mit zwei stillen Loechern darin
Wunsch: "kategorien wie bigmatch, turniere, Special-Live ... informier
dich was man da alles noch gebrauchen koennte und auch so dass wenn man
die sachen aussucht die ganze kachel und sachen die man eintippen muss
auch zu der jeweiligen kategorie passen."

Neu: BigMatch, Turnier, Special-Live, Collab, Raid-Train, Charity --
neben den drei internen Arten. Das Formular fragt je Art anderes:
beim BigMatch "Gegen wen?" mit 60 Minuten, beim Turnier "Welches
Turnier?" mit 120, bei Charity "Fuer wen wird gesammelt?" mit 180.

ZWEI FEHLER, DIE BEIDE NICHT ABGESTUERZT WAEREN:

1. termin_serien wurde nicht umgestellt. Die Umbauschleife laeuft ueber
   zwei Tabellen, bildete den Namen der Sicherungsdatei aber ohne die
   Tabelle -- und der Zeitstempel darin wird einmal pro Serverstart
   gebildet. Der zweite Durchlauf wollte also dieselbe Datei anlegen,
   VACUUM INTO weigerte sich, und das (richtige) "ohne Sicherung kein
   Umbau" beendete die ganze Schleife. Ergebnis: termine umgestellt,
   termin_serien nicht. Eine wiederkehrende BigMatch-Reihe waere ohne
   erkennbaren Grund abgelehnt worden.

2. Die neuen Arten waren im Kalender UNSICHTBAR. In kalender.js standen
   zwei weitere Aufzaehlungen derselben Arten: `zeigen = {call, termin,
   review, frist}` und die Schalterleiste. Gefiltert wird mit
   `zeigen[e.art]` -- fuer 'bigmatch' ist das undefined. Anlegen ging,
   der Server meldete 201, die Zeile stand in der Datenbank, und im
   Kalender war sie in keiner Ansicht zu sehen. Ohne Fehler, ohne Hinweis.

   Beide Listen werden jetzt aus ARTNAME abgeleitet. Und `sichtbare()`
   prueft `!== false` statt auf Wahrheit: Der Vorgabewert einer
   Sichtbarkeitsfrage muss "sichtbar" sein -- ein Eintrag zu viel ist
   ein Schoenheitsfehler, ein fehlender ein verpasster Termin.

Gefunden hat Nummer 2 kein Test, sondern ein Bildschirmfoto: In der
Schalterleiste standen vier Arten statt zehn. Meine eigene Pruefung war
zu dem Zeitpunkt gruen -- sie hoerte beim HTTP 201 auf.

Neu: server/pruef-arten.mjs, 28 Pruefungen. Baut eine Datenbank im ALTEN
Stand nach (samt Teilnehmer, Wecker, Serie), laesst die Anwendung
darueberlaufen und zaehlt nach; haelt CHECK und Serverliste gegeneinander;
und oeffnet zuletzt einen echten Browser, um zu sehen, ob die Eintraege
auch ankommen. Gegenprobe gefahren: mit dem alten Stand meldet sie
0 von 6 sichtbar.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-08 00:44:33 +02:00
DogFatherGitandClaude Opus 5 535d86d3f7 Die Termine im Tagesfenster sind Karten statt Werkzeugleisten
Filipe: "die sollen viel besser aussehen und viel geiler."

WAS WIRKLICH SCHIEFLIEF, WAR KEIN GESCHMACK, SONDERN DER AUFBAU

Zeit, Titel, Art und FUENF Knoepfe standen in EINER Zeile. Der Titel
bekam damit den Rest -- "BigMatch vs. Beanii" brach auf DREI Zeilen um,
waehrend rechts daneben Platz war. Die wichtigste Angabe der Zeile war
die gequetschteste, und die Knoepfe waren genauso laut wie der Termin
selbst.

Jetzt drei Ebenen, wie bei einer Karte:

  OBEN    Zeit und Titel, gross, ueber die ganze Breite -- der Titel
          hat keinen Wettbewerb mehr
  MITTE   die Nebendaten (Dauer, Ort, Beschreibung)
  UNTEN   die Knoepfe, rechtsbuendig in einer eigenen Reihe, durch eine
          Haarlinie abgesetzt

Die Zeit steht gross am Anfang und mit gleichen Zifferbreiten: Sie ist
das, wonach man in einem Tagesfenster sucht, und mehrere Zeilen stehen
dadurch in einer Flucht. Die Zeile traegt jetzt dieselbe abgeschnittene
Ecke wie alle Module -- ein Eintrag im Tagesfenster ist ein kleines
Modul, kein Listenpunkt.

ZWEI DINGE, DIE DABEI AN DIE RICHTIGE STELLE GERUECKT SIND

  * DIE ART GEHOERT ZUM TITEL. Sie stand als erstes Element in der
    Knopfreihe und sah damit aus wie ein Knopf, der nicht reagiert. Sie
    ist aber eine ANGABE ueber den Termin, wie Uhrzeit und Titel. Jetzt
    steht sie neben dem Titel, und die Knopfreihe enthaelt nur noch
    Dinge, die etwas tun.
  * DIE NEBENDATEN VOR DIE KNOEPFE. Im Raster bestimmt die Reihenfolge
    im Dokument, welche Zeile ein Feld bekommt -- die Knopfreihe stand
    davor und landete zwischen Titel und "30 Min · TikTok". Im ersten
    Bildschirmfoto stand die Beschreibung UNTER den Knoepfen, als
    gehoerte sie zu ihnen. Geloest ueber die Reihenfolge im Dokument und
    nicht ueber `order` im Stil: Sie gilt auch fuer Vorleseprogramme und
    die Tastatur, `order` verschiebt nur das Bild.

Auf dem Handy stehen Zeit und Titel untereinander -- bei 390 px laesst
eine 1,06-rem-Uhrzeit daneben keine zwei Woerter uebrig.

Zehn Pruefungen gelaufen, alle gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-08 00:18:06 +02:00
DogFatherGitandClaude Opus 5 e744f22fbc Vier Tafeln in einer Reihe -- die offene breiter als die Reiter
Filipe: "die sollen alle in einer reihe sein und nicht 3 und dann eins
drunter. perfektionier das."

DER GRUND WAR EINE RECHNUNG, DIE ICH NICHT KONTROLLIERT HABE

Dort stand `auto-fit` mit 340 px Mindestbreite -- CSS rechnet sich dann
selbst aus, wie viele nebeneinanderpassen. Bei vier Tafeln in einer
1240 px breiten Spalte reichte es fuer drei; die vierte rutschte in
eine zweite Zeile. `auto-fit` ist bequem, solange die Anzahl offen ist.
Sobald sie feststeht, ist es eine Rechnung, die man aus der Hand gibt.

Vier sind es, vier stehen nebeneinander -- und zwar mit `flex` statt
`grid`, weil damit die OFFENE Tafel breiter sein kann als die
geschlossenen. Es ist immer genau eine offen, und die bekommt den
anderthalbfachen Anteil: Dort wird gearbeitet, die anderen sind
Reiter. Das ist der Unterschied zwischen vier gleich grossen Kaesten
und einem Brett.

UND EIN VERSPRECHEN, DAS ERST NACH DEM ERSTEN KLICK GALT

Das Akkordeon griff nur beim Klicken. Beim Laden kamen die gemerkten
Staende aus der Ablage, und die konnten drei offene Tafeln ergeben --
im Bildschirmfoto standen genau so drei offen nebeneinander. Jetzt
bleibt beim Aufbau die erste Tafel offen, die etwas enthaelt; alle
weiteren klappen zu, ohne den gemerkten Stand zu ueberschreiben.

DREIMAL GEMESSEN STATT GESCHAETZT

Nach dem Umbau standen dort "LAEUFT AUTO..." und "FESTGEHALT..." --
252 px je Reiter, gemessen. Ich habe zweimal an den Pixeln gedreht
(Anteil 2,2 -> 1,8 -> 1,5, Sperrung 0,08 -> 0,035 em) und es blieb
abgeschnitten. Die richtige Antwort war nicht die dritte Zahl, sondern
der Name: Ein Reiter braucht ein Wort. Aus "Laeuft automatisch" wurde
"Wiederholungen" -- was es genau heisst, steht im Satz darunter, und
den liest man ohnehin erst, wenn die Tafel offen ist.

Gemessen am Ende: vier Tafeln, EINE Reihe, EINE offen, KEIN
abgeschnittener Titel.

Sechs Pruefungen gelaufen, alle gruen.

OFFEN, damit es nicht untergeht: pruef-call-kategorien meldet auf
Windows sporadisch Rueckgabewert 127 -- NACH "ALLES IN ORDNUNG", also
beim Beenden des Prozesses (libuv-Assertion beim Schliessen des noch
laufenden Servers). Das Ergebnis stimmt, der Rueckgabewert luegt. Wer
nur auf den Code sieht, haelt einen gruenen Lauf fuer rot.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-08 00:05:15 +02:00
DogFatherGitandClaude Opus 5 1c2e196d1d Der Wecker: mehrere Erinnerungen je Termin, jeder fuer sich
Filipe: "wie so ein wecker, den man auch in den eintraegen aktivieren
oder ausschalten kann, den soll man sogar so einstellen koennen, dass
er einen auch mehrmals informiert, einmal eine woche vorher, einmal
drei tage vorher und einmal am tag selber. das soll man auch selbst
jeder fuer sich einstellen koennen. hol die besten skills."

NACHGELESEN, NICHT GERATEN. Google Calendar erlaubt fuenf Erinnerungen
je Termin, Outlook genau eine, Apple zwei. Die verbreitete Empfehlung
fuer Wichtiges lautet "eine Woche, ein Tag, am Tag selbst" -- also
genau die Staffel, die Filipe genannt hat. Uebernommen: sechs Stufen
zur Wahl (Woche, drei Tage, ein Tag, selber Tag, Stunde, zehn Minuten),
hoechstens fuenf gleichzeitig.

EINE ZEILE IST EIN WECKER -- kein Feld am Termin mit einer Liste darin.
Mehrere Vorlaufzeiten UND "jeder fuer sich" sind zusammen eine
n:m-Beziehung; ein Feld mit kommagetrennten Zahlen waere beim ersten
"zeig mir alle faelligen Wecker" nicht mehr abfragbar.

DER ABSTAND STEHT IN DER DATENBANK, NICHT DER ZEITPUNKT. Ein Zeitpunkt
muesste bei jeder Terminverschiebung nachgezogen werden -- und genau
das vergisst man. Ein Abstand rechnet sich beim Wecken aus dem
aktuellen Beginn und ist damit immer richtig.

ZWEI GRENZEN IM WECKLAUF, und beide sind noetig: faellig (Weckzeit
erreicht) UND der Termin liegt noch vor uns. Ohne die zweite wuerde
beim ersten Lauf nach einem Ausfall jeder alte Wecker der letzten
Wochen nachtraeglich klingeln.

DER ABSTAND GEHOERT INS MERKMAL der Doppelsperre. Ohne ihn wuerde der
erste Wecker eines Termins alle weiteren sperren -- und genau das
Mehrfach-Wecken, um das es geht, faende nie statt.

DIE PRUEFUNG HAT SICH ZWEIMAL SELBST KORRIGIERT

  1. Erster Lauf um 23:42: vier Fehler, keiner echt -- der Melder
     schweigt zwischen 22 und 7 Uhr. Sie hat den Kalender gemessen,
     nicht die Software, und waere am Vormittag gruen gewesen. Dass die
     GEGENPROBE mitgefallen ist, war die eigentliche Auskunft: Waeren
     nur die Grenzen falsch, haette sie gehalten. Die Ruhezeit ist
     jetzt ueber die Umgebung einstellbar (Vorgabe unveraendert 22/7),
     damit eine Pruefung ihre Voraussetzung herstellen kann.
  2. Danach immer noch nichts: Ich hatte angenommen, der Melder trage
     den Versand nach dem VERSUCH ein. Er traegt ihn nach der
     erfolgreichen ZUSTELLUNG ein -- und das ist richtig so. Meine
     Annahme war falsch, nicht der Code. Die Pruefung hat jetzt einen
     winzigen echten Empfaenger; damit laeuft der ganze Versandweg mit,
     Verschluesselung und VAPID inbegriffen.

pruef-wecker.mjs: 20 Pruefungen. Sie stellt alle vier Fehler nach, die
bei einem Wecker moeglich sind (klingelt nicht / doppelt / nur einmal
von dreien / nachtraeglich nach einem Ausfall) -- plus die Gegenprobe,
dass ein faelliger Wecker wirklich ankommt.

Zwoelf weitere Pruefungen gelaufen, alle gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 23:51:20 +02:00
DogFatherGitandClaude Opus 5 63036fc33e Wiederholungen bekommen eine eigene Tafel -- und nur den laufenden Monat
Filipe: "ich will da auch noch eine kategorie fuer automatische
wiederholungen. die sollen dann auch nur fuer den monat selbst
angezeigt werden und nicht monate im voraus."

DAS PROBLEM WAR ECHT UND GROSS, UND ES STAND SEIT TAGEN AUF SEINEM
BILDSCHIRM

Der Nachfueller haelt einen Horizont von 180 Tagen gefuellt (siehe
workspace-serien.js). Ein woechentlicher Community-Talk ergibt darin
sechsundzwanzig Zeilen -- und alle standen unter "Steht an". Auf dem
Bild waren es siebenundzwanzig Karten, fast alle derselbe Termin. Die
Liste war damit unbrauchbar fuer genau das, wofuer sie da ist: zu
sehen, was WIRKLICH ansteht.

Jetzt sind es zwei getrennte Fragen:

  STEHT AN            was einmalig bevorsteht
  LAEUFT AUTOMATISCH  was von allein wiederkommt -- und davon nur der
                      LAUFENDE MONAT

Der Monatsschnitt ist die eigentliche Antwort auf "nicht Monate im
Voraus": Eine Wiederholung im November sagt einem heute nichts, was man
nicht schon weiss. Wer weiter schauen will, hat den Kalender -- und
genau das steht als Satz in der Gruppe.

Gerechnet wird auf dem reinen Datumstext (`beginn` beginnt mit
JJJJ-MM), nicht mit `new Date`. Kein Zeitzonenfehler, kein Nachtfehler.
Die Trennung faellt im SERVER, nicht in der Oberflaeche: Eine zweite
Regel im Browser waere die sichere Zusage, dass beide auseinanderlaufen.

DREI AUSSAGEN STATT EINER

pruef-call-kategorien saet jetzt zwei Auspraegungen derselben Serie --
eine in vier, eine in sechzig Tagen -- und misst:

  1. die Wiederholung dieses Monats steht in "Laeuft automatisch"
  2. die des naechsten Monats NICHT
  3. und unter "Steht an" steht keine von beiden

Vorher wird geprueft, dass die beiden ueberhaupt in verschiedenen
Monaten liegen. Ohne diese Zeile waere Nummer 2 an einem 1. des Monats
trivial erfuellt -- gruen, ohne etwas gemessen zu haben.

Zwei Fehler beim Bau der Pruefung, beide von ihr selbst gemeldet:
`page.evaluate` lief in "Target page has been closed" (der Block davor
schliesst seinen Browserkontext -- diese Aussage braucht ohnehin keinen
Browser, sie betrifft die Schnittstelle), und eine Hilfsfunktion stand
nach ihrer ersten Benutzung.

pruef-call-kategorien von 17 auf 22. Neun Pruefungen gelaufen, alle
gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 23:26:38 +02:00
DogFatherGitandClaude Opus 5 fc4616bb05 Immer nur eine Tafel offen -- und eine zugeklappte belegt nichts mehr
Filipe: "wenn ich eine aufklicke soll auch immer nur die aufgehen und
nicht alle 3."

DAS IST MEHR ALS GESCHMACK. Seit die drei Tafeln nebeneinander stehen
und jede ihren eigenen Lauf hat, teilen sie sich die Bildschirmhoehe:
Drei offene Tafeln heissen drei kurze Ausschnitte -- eine offene heisst
eine, in der man wirklich arbeiten kann.

Die anderen werden ZUGEKLAPPT, nicht versteckt: Ihre Koepfe bleiben mit
Namen und Anzahl stehen. Man sieht weiterhin, was es sonst gibt, und
kommt mit einem Klick hin. Der gemerkte Stand wird mitgeschrieben --
sonst waere die Seite beim naechsten Aufruf in einem Zustand, den
niemand hergestellt hat.

UND EIN FEHLER VON MIR, DEN SEIN BILD GEZEIGT HAT

Die zugeklappten Tafeln standen als LEERE KAESTEN ueber die volle Hoehe
da. `align-items: stretch` am Brett gilt eben auch fuer die, die nichts
zeigt. Drei gleich hohe Tafeln sind richtig, solange sie etwas
enthalten -- eine geschlossene enthaelt nichts und soll dann auch
nichts belegen.

Vier Pruefungen gelaufen, alle gruen.

NOCH OFFEN, und bewusst nicht angefangen: Erinnerungswecker,
Terminarten (BigMatch/Turniere/Special-Live) und der Umbau der
Begruessungskachel nach VanVans Werktisch. Jede davon ist ein eigener
Bau -- angefangen und liegengelassen waeren sie schlimmer als gar nicht
begonnen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 18:41:12 +02:00
DogFatherGitandClaude Opus 5 c5cdf686e3 Der Husky jetzt auch auf der Zugangsseite -- und fuenf Zeichen in fuenf Farben
Filipe: "bei dogfather ist immer noch die krone und da soll ja ein husky
sein. und die symbole sollen doch alle viel krasser, geiler und
spezieller sein."

Er hat recht, und der Grund ist eine Haelfte, die ich uebersehen habe:
Die Rollenzeichen gibt es ZWEIMAL im Haus -- als <use>-Bausteine in
personen.html (dort war der Husky schon) und noch einmal ausgeschrieben
in index.html, der Anmeldeseite. Getauscht hatte ich nur die erste.

DER HUSKY, zweite Ausfertigung. Bei 21 Pixeln entscheidet die
Silhouette, nicht das Detail: spitze aufrechte Ohren, breiter Kopf, der
nach unten schmal zulaeuft, Gesichtsmaske. Mehr passt nicht hinein --
und mehr braucht es nicht.

UND ALLE FUENF ZEICHEN TRAGEN JETZT IHRE EIGENE FARBE

Sie waren feine Konturen in einer Farbe, und zwar in DERSELBEN fuer
alle fuenf. Jetzt: eine gefuellte Flaeche in der Farbe ihrer Rolle, die
Zeichnung hell darauf, ein leichter Schatten darunter. Chili rot,
Husky gold, Stern violett, Schild gruen, Person blau -- dieselben
Farben wie auf der Personenseite; wer die eine Seite kennt, erkennt die
andere wieder.

Die Farbe steht am ROLLENKNOPF (`--rf`), nicht im Zeichen. Die Zeichen
wissen damit nichts von Rollen, und eine Farbaenderung passiert an
einer Stelle statt an fuenf. Gewaehlt heisst: mehr Licht auf demselben
Gegenstand -- kein anderer Gegenstand.

Zehn Pruefungen gelaufen, alle gruen, darunter Kontrast und Handy fuer
die Anmeldeseite.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 18:36:04 +02:00
DogFatherGitandClaude Opus 5 af4a00dce2 Die Kopfleiste war nie klebend -- und das Call-Brett hatte eine leere Haelfte
DIE LEISTE BLEIBT JETZT OBEN (screen 3)

Filipe: "diese leiste soll immer da stehen bleiben, egal ob man die
seite runterscrollt oder nicht, auf allen seiten."

In start.css steht seit jeher `.kopfleiste { position: sticky; top: 0 }`.
Zwanzig Zeilen darueber steht aber

    body.start > .kopfleiste { position: relative; z-index: 1; }

und das ist (0,2,1) gegen (0,1,0) -- die staerkere Regel gewinnt,
unabhaengig von der Reihenfolge. Gemessen im Browser: `position:
relative`, und bei 600 px Scrollen wanderte die Leiste 600 px aus dem
Bild. Sie hat also nie geklebt, obwohl es im Code so dasteht.

Das ist heute die VIERTE Spielart derselben Falle: `:where()` zu
schwach, `body.start .willkommen` zu stark, die Kachel-Verschachtelung
zu stark -- und hier eine Regel, die etwas ganz anderes wollte (den
Stapelwert ueber der Buehne) und dabei die Positionierung mitgenommen
hat. Merksatz: Wer `position` setzt, nur um `z-index` zu bekommen,
greift jedes Mal daneben.

Nachgemessen: 700 px gescrollt, Leiste steht bei 0.

DAS CALL-BRETT: DREI TAFELN STATT ZWEIER SPALTEN (screen 2)

Filipe: "das bewegt sich immer noch mit, das ist so scheissen."

DAS PROBLEM WAR DIE AUFTEILUNG, nicht die Gestaltung. Zwei Spalten, und
"Steht an" hatte siebenundzwanzig Karten: Die rechte Spalte lief ueber
mehrere Bildschirmhoehen, die linke war nach zwei Koepfen zu Ende. Wer
scrollt, sieht dann eine leere halbe Seite mit einer Ueberschrift, die
scheinbar mitwandert -- sie steht bloss still, waehrend daneben alles
laeuft.

Jetzt bekommt jede Tafel DIESELBE Hoehe und einen EIGENEN Lauf. Alle
drei Gruppen sind damit immer gleichzeitig zu sehen, egal wie viel in
einer steckt, und die Seite selbst scrollt kaum noch. Das ist die
Bauart jedes Aufgabenbretts, und sie ist es aus genau diesem Grund.

Die Hoehe haengt am Fenster (`min(62vh, 620px)`) statt an einer festen
Zahl. Unter 900 px stehen die Tafeln untereinander und laufen wieder
frei -- auf dem Handy ist ein Kaestchen mit eigenem Balken eine Falle,
keine Hilfe. Der Balken ist selbst gestaltet; der Systembalken reisst
ein weisses Band in eine dunkle Flaeche.

NOCH OFFEN aus derselben Nachricht: der Erinnerungswecker fuer Termine
(ein-/ausschaltbar je Eintrag, mehrere Zeitpunkte, von jedem selbst
einstellbar) -- dazu will Filipe ausdruecklich Recherche, und der baut
sich nicht nebenbei.

Zehn Pruefungen gelaufen, alle gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 18:24:58 +02:00
DogFatherGitandClaude Opus 5 28a260fab2 Ein Husky statt der Krone -- und Spicy sieht DogFather, aber nur ihn
DER HUSKY (screen 1)

Filipe: "die krone bei dogfather durch einen husky ersetzen, wie mein
logo." Bei 24 Pixeln entscheidet die SILHOUETTE, nicht das Detail. Ein
Husky erkennt man an dreierlei, und mehr passt auch nicht hinein: den
spitzen aufrechten Ohren, dem breiten Kopf, der nach unten schmal
zulaeuft, und der Gesichtsmaske. Fell oder Zunge waeren bei dieser
Groesse Matsch -- genau deshalb hat die Krone davor funktioniert.

UND ALLE ROLLENSYMBOLE SIND JETZT KOERPER

Sie waren reine Konturen in einer Farbe -- daneben auf derselben Seite
die Kachelzeichen mit drei Lichtern. Jetzt tragen sie eine gefuellte
Flaeche in ihrer Rollenfarbe, die Zeichnung hell darauf, und Augen und
Nase eigens gesetzt. Beim gewaehlten Knopf leuchtet die Flaeche
staerker -- der einzige Unterschied, den es braucht: mehr Licht auf
demselben Gegenstand.

SPICY SIEHT DOGFATHER, ABER NICHT DEN ZWEITEN ADMIN (screen 2)

Filipe: "die rolle spicy soll auch die rolle dogfather sehen, aber nur
dogfather und nicht vanvan."

NACHGEMESSEN AM ECHTEN SYSTEM, nicht angenommen: In der Datenbank
tragen BEIDE die Rolle `admin` -- id 1 "Dogfather", id 4 "VanVan". Es
gibt kein Feld, das den einen vom anderen unterscheidet.

Der Unterschied, den es wirklich gibt, ist das Alter: DogFather ist der
erste Zugang des Hauses. Deshalb zaehlt die kleinste Nummer unter den
Admins -- eine Eigenschaft, die feststeht und nicht am Namen haengt.
Die Schwachstelle steht im Code, damit sie niemand sucht: Wuerde Zugang
1 je geloescht, rueckte der naechste nach; dann gehoert ein
ausdrueckliches Merkmal in die Tabelle.

Die Entscheidung faellt im SERVER, nicht in der Oberflaeche. Dort stand
vorher ein Filter, der den ganzen Abschnitt wegnahm -- zwei Regeln fuer
dieselbe Frage laufen auseinander, und eine ausgeblendete Zeile hat
noch nie etwas geschuetzt.

MEINE EIGENEN PRUEFUNGEN VON HEUTE MITTAG FIELEN DABEI

Richtig so: Sie pruefen "DogFather steht NICHT darin", und das gilt
nicht mehr. Umgeschrieben -- und dabei kam der eigentliche Prueffall
dazu: ein ZWEITER Admin-Zugang im Testbestand. Ohne ihn waere die Regel
gar nicht pruefbar, bei einem einzigen Admin ist jede Antwort richtig.
pruef-spicy von 57 auf 60.

NOCH OFFEN aus derselben Nachricht: die Terminarten (BigMatch,
Turniere, Special-Live) und der Umbau der Begruessungskachel nach dem
Vorbild von VanVans Werktisch.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 18:14:54 +02:00
DogFatherGitandClaude Opus 5 09a0c17237 Bearbeiten geht jetzt -- und zwei Bedienelemente, die keine Kacheln sind
DER SERVER LIESS DAS AENDERN DIE GANZE ZEIT ZU. IM TAGESFENSTER FEHLTE
DER KNOPF.

Filipe: "ich hab die gemacht und kann sie nicht bearbeiten." Dort
standen nur "erledigt" und "loeschen". Ein Recht ohne Knopf ist kein
Recht. Jetzt oeffnet "bearbeiten" dasselbe Formular, gefuellt -- ein
Formular, zwei Wege (POST oder PATCH), statt eines zweiten, das genauso
aussieht und beim naechsten Feld auseinanderlaeuft.

Die Wiederholung bleibt beim Bearbeiten aussen vor: Sie ist eine REGEL
und wird unter "Laeuft von allein" geaendert, nicht an einer ihrer
Auspraegungen. Wer das zulaesst, bekommt einen Termin, der aus der
Reihe faellt, ohne dass jemand weiss warum.

UND DIE REGEL DAZU IM SERVER

Filipe: "nur diese person selber." Bis hierher durfte JEDER aendern,
der den Termin ueberhaupt sah -- bei einem Termin mit mehreren
Beteiligten also alle. Jetzt: die Leitung und wer ihn eingetragen hat.
Dieselbe Regel wie beim Loeschen, die dort schon richtig stand.

AUSNAHME "erledigt": Ein Haken, dass ein Gespraech stattgefunden hat,
ist keine Aenderung am Termin, sondern eine Rueckmeldung dazu -- sonst
muesste jeder Beteiligte den Anleger bitten, den eigenen Call
abzuhaken.

Gemessen in pruef-teilnehmer, mit allen drei Faellen. Beim Bauen der
Pruefung ist mir ein Aufbaufehler unterlaufen (Bea statt Pat als
zweite Teilnehmerin -- Luna darf Bea gar nicht einladen), und die
Pruefung hat ihn korrekt als 404 statt 403 gemeldet. Der Fehler lag im
Aufbau, nicht im Code.

DER ANSICHTS-UMSCHALTER WAR VIER KACHELN

`.k-ansicht` stand in der Modulliste. Jeder der vier Knoepfe bekam
damit die volle Behandlung einer Kachel: Fase, Kantenlicht, Eckwinkel,
Raster. Auf 90 mal 32 Pixeln ist das kein Modul, sondern Gedraenge --
vier Fasen und sechzehn Eckwinkel nebeneinander.

Die Modulform ist fuer FLAECHEN gedacht, die etwas enthalten. Ein
Umschalter enthaelt nichts, er waehlt aus, und die richtige Form dafuer
ist die SCHIENE: eine vertiefte Bahn, in der ein erhabenes Stueck aus
gebuerstetem Metall sitzt. Man sieht auf einen Blick, dass die vier
zusammengehoeren und genau eines gewaehlt ist.

DIE GRUPPENKOEPFE AUF DER CALLS-SEITE

Vorher eine Textzeile mit Pfeil, und die Karten darunter begannen ohne
Uebergang -- aufgeklappt sah man nicht, wo eine Gruppe aufhoert. Jetzt
ist der Kopf ein Schalter mit Zustand, die Zahl ein gefasstes Schild,
und die Karten stehen aufgeklappt in einer eigenen vertieften Bahn mit
Farbschiene links.

14 Pruefungen gelaufen, alle gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 17:05:28 +02:00
DogFatherGitandClaude Opus 5 dd1f561f3b Vier Meldungen -- und dahinter zweimal derselbe alte Fehler
DIE SYMBOLE IN DER TAGESKACHEL WAREN DIESELBEN -- OHNE IHRE GESTALTUNG

Sie kommen vom selben Bauer wie alle anderen, bekamen aber nie dessen
Aussehen: Alle Lagenregeln waren auf `.kachel__svg` eingegrenzt, und
dieses Zeichen heisst `.dran__svg`. Uebrig blieb eine flache Kontur --
daneben, auf derselben Seite, dieselben Zeichen mit drei Lichtern,
Tiefe und Randlicht. Die Regeln gelten jetzt fuer beide Traeger, und
das Feld darum ist dasselbe gefasste Schild wie auf den Kacheln.

"UNDEFINED" IM CHAT -- DERSELBE FEHLER WIE HEUTE FRUEH, NUR ALS OBJEKT

Ueber Cigdems Namen stand woertlich "UNDEFINED". Die Rollennamen
standen als Objekt in FUENF Skripten (chat.js zweimal, dateien.js,
kalender.js zweimal), in keinem davon 'spicy'.

Heute Frueh war es dieselbe Sache als Menge (`new Set([...])`), und
seitdem sucht `pruef-css-klassen.mjs` danach. Ein Muster, das nur eine
Schreibweise kennt, findet auch nur eine -- die Pruefung sucht jetzt
auch nach Rollen-OBJEKTEN, mit Gegenprobe. Die Namen stehen an EINER
Stelle in bereiche.js.

DOGFATHER UND SPICY MEDIA SIND IM CHAT FUER JEDEN ERREICHBAR

DogFather kam bisher als Nebeneffekt ueber die Betreuungskette mit
hinein, Spicy Media gar nicht -- die Rolle steht in keiner Kette, sie
steht daneben. Beide werden jetzt ausdruecklich hinzugefuegt: Eine
Zustaendigkeit, die nur zufaellig aus einer anderen Regel herausfaellt,
faellt beim naechsten Umbau genauso zufaellig wieder heraus.
Gemessen aus der Sicht eines Creators -- wer bei ihm ankommt, kommt
ueberall an.

DIE PERSONENLISTE: SPICY MEDIA SIEHT ALLES AUSSER DOGFATHER

Der Abschnitt "Spicy Media" fehlte in der Liste komplett -- die Rolle
gibt es seit heute Frueh, die Personenseite kannte sie nicht. Und Spicy
Media selbst sah dort bisher nur das Anlegen-Formular; sie bekommt
jetzt die Liste, ohne DogFathers Zeile, und weiterhin ohne Codes,
Sperren, Loeschen und Protokoll. Ueberblick ist nicht Verwaltung.

ZWEI FOLGEFEHLER, BEIDE VON DEN PRUEFUNGEN GEFUNDEN

  * `next("route")` TUT DAS GEGENTEIL VON DEM, WONACH ES KLINGT. Mein
    erster Versuch war eine Ausnahme-Route VOR der Schranke, die mit
    `next("route")` weiterreicht -- das ueberspringt aber die restlichen
    Handler DIESER Route und geht zur naechsten Schicht, also genau zur
    Schranke. Spicy bekam weiter 404, die Oberflaeche verstand das als
    "nicht erlaubt" und sprang zur Startseite. Gemessen: Auf
    personen.html standen die Kategorien der STARTSEITE.
  * DAS PROTOKOLL WARF SIE VON DER SEITE. Es bleibt bei DogFather und
    antwortet ihr mit 404 -- und `hole()` versteht ein 404 unter
    `/verwaltung/` als "nicht erlaubt". Die Seite baute sich auf und
    sprang im naechsten Atemzug weg. Eine Abfrage, von der man weiss,
    dass sie 404 gibt, stellt man nicht.

UND EINE MEINER EIGENEN NEUEN PRUEFUNGEN WAR WERTLOS

"bei ihr fehlt der Abschnitt DogFather" -- gruen, mit dem Zusatz
"(keine Abschnitte)". Sie war gruen, weil die Liste bei ihr GAR NICHT
DA war. Genau der Haken, der nichts beweist. Er steht jetzt neben einem
Ergebnis, das etwas enthaelt: "Spicy Media | Manager | ...".

18 Pruefungen gelaufen, alle gruen. pruef-spicy von 49 auf 57.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 16:45:50 +02:00
DogFatherGitandClaude Opus 5 0ea8b27aca Aus drei Strichen werden drei Ringe aus Material
Filipe: "ich will dass der rand mit den sekunden minuten und stunden
viel krasser und geiler ist ... ultra modern, ultra speziell, ultra
profissionell, ultra phaenomenal."

EIN STRICH IST EINE LINIE. EIN RING IST EIN KOERPER.

Und ein Koerper hat drei Merkmale, die man zeichnen MUSS, sonst bleibt
es ein Strich. Jeder der drei Ringe besteht deshalb jetzt aus drei
Lagen:

  SCHATTEN   Er liegt ueber dem Zifferblatt, also wirft er einen.
             Dieselbe Bahn, schwarz, ein halbes Rastermass nach UNTEN.
  KOERPER    Der Bogen selbst in seiner Farbe.
  OBERKANTE  Duenner, hell, ein Drittel nach OBEN. Weil er versetzt
             ist, schaut er oben hervor und verschwindet unten -- genau
             das tut eine gewoelbte Kante bei Licht von oben.

Kein Weichzeichner, nirgends: Die Tiefe kommt aus dem Versatz, nicht
aus Unschaerfe.

DIE BAHNEN SIND GEFRAESTE RILLEN

Ein Zeiger laeuft bei einem guten Instrument IN einer Vertiefung. Eine
Rille erkennt man an zweierlei: dunkler als ihre Umgebung, und an ihrer
unteren Wand steht eine helle Kante. Beides steht jetzt da, und die
Breite folgt dem Ring, der darin laeuft -- eine Rille, die schmaler ist
als ihr Zeiger, ist keine.

DREI KOEPFE STATT EINEM

Minute und Stunde bekommen dieselbe polierte Kappe wie die Sekunde, auf
ihren eigenen Bahnen und in ihrer eigenen Farbe. Erst dadurch liest man
die drei Ringe als drei ZEIGER und nicht als drei Fortschrittsbalken.
Sie laufen mit ihrem Ring: die Minute nimmt die Sekunden anteilig mit,
die Stunde die Minuten -- sonst staende der Kopf neben dem Ende seines
Bogens.

ALLE LAGEN WERDEN GEMEINSAM GESETZT. Sie tragen `data-ring`; einzeln
gepflegte Verweise waeren drei Stellen, an denen man eine vergessen
kann, und ein Schatten, der stehen bleibt, sieht sofort kaputt aus.

UND EIN FUND, DEN DIE PRUEFUNG SOFORT GEMELDET HAT

Die neue helle Oberkante des Stundenrings laeuft hinter den Ziffern
durch: schlechtester Kontrast 2,98:1 -- knapp unter der Grenze, und
ausgerechnet bei der Uhrzeit selbst. Die Antwort war nicht "Kante
weg", sondern der fehlende Untergrund: Auf einer echten Uhr steht eine
Anzeige, die ueber Zeigern liegt, auf einer eigenen vertieften INSEL im
Zifferblatt. Jetzt 5,42:1, und dabei 29 statt 14 gemessene Stellen.

Merksatz: Wenn eine neue Schicht einen Text unlesbar macht, ist die
Antwort selten "Schicht weg" -- meistens fehlt dem Text sein Grund.

Zehn Pruefungen gelaufen, alle gruen. Rechner und Handy angesehen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 16:23:27 +02:00
DogFatherGitandClaude Opus 5 9c7952b5a3 Drei Lichter statt einem: die Symbole bekommen Koerper
Filipe: "die symbole und der text dadrin sollen groesser und noch
spezieller sein, und die symbole sollen auch 2-3 farben haben ... sollen
mehr leben haben, auch viel realistischere effekte."

NICHT DREI AUSGEDACHTE FARBEN, SONDERN DIE DREI EINES ECHTEN AUFBAUS

So wird jedes Produktfoto ausgeleuchtet, und aus demselben Grund sieht
es plastisch aus:

  1  FUEHRUNGSLICHT, kalt, von oben links. Ein Spitzlicht ist nie
     reinweiss -- es traegt die Farbe der Lampe, und die ist kuehl.
  2  EIGENFARBE des Gegenstands: der Ton seiner Kachel.
  3  STREULICHT, warm, von unten. Licht, das vom Untergrund
     zurueckkommt, ist waermer als das Hauptlicht. Genau dieser warme
     Saum ist der Grund, warum ein Gegenstand im Bild STEHT statt zu
     schweben.

Dazu die aelteste Regel der Malerei: warmes Licht, KUEHLE Schatten. Die
Seitenwaende der Zeichen kippen jetzt ins Blaue statt nur dunkler zu
werden. Und ein RANDLICHT auf der Lichtseite -- der schmale Streifen,
in dem das Fuehrungslicht die Kante streift. Ein Gegenstand ohne diese
Kante sieht immer ein wenig flach aus, und man kann meist nicht sagen,
warum.

ZWEI FEHLER DABEI, BEIDE ERST BEI FUENFFACHER VERGROESSERUNG SICHTBAR

  * DAS WARME LICHT LAG UNTER DER FORM. Die Verlaeufe spannten ueber das
    ganze 24er-Raster (y 2 bis 22); die Sprechblase des Chats reicht
    aber nur von 5,5 bis 20,5. Der warme Stopp bei y 22 war damit
    ausserhalb -- von den drei Lichtern kam genau eines an.
    `objectBoundingBox` spannt den Verlauf jetzt ueber JEDES Teil
    einzeln: Der Kalenderkorpus bekommt sein volles Licht, seine Fuesse
    ebenfalls. So verhaelt sich ein echter Aufbau -- jedes Teil liegt im
    selben Licht, nicht im selben Ausschnitt.
  * DAS RANDLICHT WAR SCHMALER ALS DIE KONTUR DARUEBER und lag deshalb
    vollstaendig darunter: gebaut, gezeichnet, unsichtbar. Jetzt 2,7
    gegen 1,9 -- so schaut es oben links hervor.

GROESSER, WIE GEWUENSCHT

Plakette 58 -> 64 px (grosse Kachel 62 -> 70), Zeichen 29 -> 35 px
(gross 32 -> 39), Wasserzeichen 132 -> 156 px (gross 168 -> 196),
Name 1,06 -> 1,15 rem (gross 1,24 -> 1,38), Unterzeile 0,78 -> 0,845.
Auf dem Dashboard entsprechend.

UND DAS SCHILD WIRFT LICHT AUF SEINE KACHEL

Ein beleuchteter Gegenstand faerbt seine Umgebung. Ohne diesen Abfall
sieht selbst ein gut gebautes Schild aufgeklebt aus. Weit gestreut und
weit unter der Blendschwelle: Man soll ihn nicht sehen, man soll ihn
vermissen, wenn er fehlt.

Zehn Pruefungen gelaufen, alle gruen -- darunter Handy und Breiten,
weil groesserer Text der schnellste Weg zu einem Ueberlauf ist.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 16:01:27 +02:00
DogFatherGitandClaude Opus 5 5b6f9cef98 Rot, Schwarz, Babyblau -- und ein Glas, das entspiegelt ist
Filipe: "der rand soll auch eine mischung von rot schwarz und babyblau
haben und die kachel selbst soll einen uebertrieben krank geilen
hintergrund haben ... die einzige kachel, die komplett aus dem rudel
faellt. die uhr soll auch VIEL VIEL VIEL KRASSER sein. informier dich,
hol die besten skills von den besten skills."

NACHGELESEN STATT GERATEN -- UND DAS HAT DIE UHR VERAENDERT

Zur Frage, woran man ein hochwertiges Uhrenglas erkennt: Eine
Entspiegelung wird im Vakuum aufgedampft und senkt die Spiegelung auf
unter ein Prozent -- das Zifferblatt wirkt dadurch SCHAERFER, nicht
milchiger. Und ihr Erkennungszeichen ist kein weisser Schleier, sondern
ein TOPASBLAUER SCHIMMER, der je nach Lichteinfall ueber das Glas
laeuft.

Hier lag genau das Gegenteil: ein breiter weisser Verlauf ueber ein
Fuenftel der Scheibe -- also die Spiegelung eines UNBESCHICHTETEN
Glases, das Merkmal des billigeren Materials. Jetzt: ein schmaler,
harter Reflexbogen an der Woelbung, der topasblaue Schimmer diagonal
darueber, und die haarfeine Schnittkante oben.

DAZU ZWEI WEITERE MITTEL AUS DEM UHRENBAU

  * AUFGESETZTE INDIZES bei 3, 6 und 9. Auf einer guten Luenette sind
    die Viertelstunden keine Striche wie die anderen: Sie sind eigene
    Marken, breiter und HELL statt graviert -- weil sie aufgesetzt sind
    und deshalb Licht fangen statt Schatten zu halten. Die 12 bleibt
    die rote.
  * DAS SEKUNDENFELD IST EIN EINGELASSENES FENSTER. Eine Zusatzanzeige
    sitzt in einer Aussparung des Blatts; man erkennt das daran, dass
    der Schatten oben hineinfaellt und unten eine helle Kante steht.
    Genau diese beiden Schatten stehen jetzt darin.

DIE FASSUNG: DREI FARBEN STATT STAHL MIT TUPFERN

Links die rote Haelfte, rechts die babyblaue, dazwischen und an den
Raendern Schwarz -- und ueberall dort, wo Metall das Licht bricht, die
hellen Spitzlichter. Es sind dieselben zwei Farben wie im Motiv und auf
der Anmeldekarte.

DER HINTERGRUND: SECHS SCHICHTEN

Lichtkante, KOHLEFASERGEWEBE (zwei gegenlaeufige Schraegen, die sich
kreuzen -- bei drei Prozent sieht man kein Muster, man sieht ein
MATERIAL), das Messraster, ein HORIZONT im unteren Drittel mit Schein
darueber, die beiden Farbbecken kraeftiger als bisher, und ein fast
schwarzer Grund mit Blauschimmer oben.

Alles weit unter der Blendschwelle -- die Hausregel gilt auch fuer
"krank geil": Es darf beeindrucken, es darf nicht blenden.

Sieben Pruefungen gelaufen, alle gruen. Rechner und Handy angesehen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 15:44:25 +02:00
DogFatherGitandClaude Opus 5 6e09c55002 Die Symbole hatten nie ihre Farbe -- ein Jahr lang, auf jeder Seite
Filipe: "ich will dass die symbole viel krasser, realistischer,
farbiger und spezieller sind ... ich meine wirklich alle alle alle
symbole auf der ganzen website."

DER GRUND WAR KEIN GESCHMACK, SONDERN EIN BAUFEHLER

Die Verlaeufe der Zeichen arbeiten mit `currentColor`, damit jedes
Zeichen die Farbe SEINER Kachel annimmt. Sie lagen aber alle zusammen
in EINEM versteckten SVG am Ende der Seite, und jedes Zeichen verwies
nur darauf. `currentColor` in einem Verlaufsstopp wird an dem Element
aufgeloest, das den STOPP enthaelt -- also dort, im versteckten SVG,
wo die Textfarbe das helle Grau der Seite ist.

Jedes Zeichen im ganzen Haus war deshalb grau. Der Farbton kam sauber
an der Kachel an (gemessen: rgb(62,149,231) auf der Dashboard-Kachel)
und wurde nie benutzt.

GEMESSEN, NICHT VERMUTET: Faerbt man das versteckte SVG rot, werden die
Zeichen rot (hellster Bildpunkt 43/49/61 -> 46/29/40). Faerbt man das
ZEICHEN rot, passiert nichts. Damit war die Frage entschieden.

Das Tueckische daran: Es sah nie kaputt aus. Graue Zeichen auf dunklem
Grund wirken sauber und zurueckhaltend -- man haelt es fuer eine
Entscheidung. Ein Fehler, der wie Gestaltung aussieht, ueberlebt jede
Pruefung, die auf Fehlermeldungen achtet.

DIE REPARATUR

Jedes Zeichen traegt seine Verlaeufe jetzt SELBST, in seinem eigenen
SVG und mit eigener Kennung. Damit steht `currentColor` dort, wo es
hingehoert. Die Verweise setzt das Skript als Inline-Stil, weil eine
Klassenregel die je Zeichen andere Kennung nicht kennen kann -- das
Wasserzeichen bekommt keinen, dort setzt das CSS die Farbe ausdruecklich.

UND DAS LICHT WURDE UMGEDREHT

Weiss stand vorher ueberall: die Deckflaeche begann mit 92 % Weiss, die
Kontur war bis 38 % weiss und bei 100 % wieder. Selbst mit richtiger
Farbe waere davon wenig uebrig geblieben. Jetzt ist Weiss nur noch da,
wo bei einem echten Gegenstand das SPITZLICHT sitzt -- ein schmaler
Streifen ganz oben. Darunter traegt die Eigenfarbe, unten kommt
Streulicht in einer helleren Tonung statt in Weiss: Licht, das vom
Untergrund zurueckkommt, nimmt die Farbe des Gegenstands mit, es
bleicht ihn nicht aus. Das Spitzlicht selbst wurde schmal und hart --
ein Schleier ueber zwei Drittel der Flaeche ist kein Spitzlicht,
sondern der sicherste Weg, jede Farbe blass zu machen.

DAS WASSERZEICHEN

Seine Deckkraft von sieben Prozent war ein Wert aus der Zeit, als das
Zeichen grau war -- mehr ging nicht, ohne dass es schmutzig aussah.
Eine Farbe darf lauter sein als ein Grau, weil sie zur Kachel GEHOERT.
Auf 14 Prozent verdoppelt, kraeftigere Linie, und ein leichter Schein
darunter fuer Tiefe.

15 Pruefungen gelaufen, alle gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 15:22:25 +02:00
DogFatherGitandClaude Opus 5 dbb19f69e9 Uhrmacherei statt Lack: guillochiertes Blatt, laufende Perle, Spiegelung auf Metall
Filipe: "ich will dass diese kachel komplett speziell ist, das
hochwertigste und geilste auf der ganzen website ... auch das gleiche
prinzip fuer die uhr, noch vieeeel spezieller. hol die besten skills
von den besten skills dafuer."

Also keine weitere Schicht Lack, sondern die Mittel, an denen man ein
teures Instrument WIRKLICH erkennt.

DIE UHR -- VIER MITTEL AUS DER UHRMACHEREI

  1. GUILLOCHIERTES ZIFFERBLATT. Guillochieren ist das Verfahren, mit
     dem seit zweihundert Jahren hochwertige Blaetter gemacht werden:
     Eine Maschine schneidet ein feines regelmaessiges Muster ins
     Metall, und weil jede Rille das Licht anders zurueckwirft, LEBT
     die Flaeche. Hier aus Strahlen vom Mittelpunkt und Ringen darum,
     beide bei vier Prozent Deckkraft -- wer das Muster einzeln
     erkennt, hat es zu laut gemacht.
  2. EIN AUFGESETZTER ZWOELF-INDEX. Auf einem echten Blatt ist die
     Zwoelf nie nur ein Strich wie die anderen: Sie ist das, woran das
     Auge sich ausrichtet. Ein Keil in Hausrot mit heller Kante.
  3. DIE PERLE AM KOPF DES SEKUNDENBOGENS. Sie laeuft einmal je Minute
     herum und ist das Einzige an der Uhr, das sich BEWEGT statt zu
     wachsen. Ein Bogen zeigt einen Stand, eine laufende Perle zeigt
     Leben. Sie springt im Sekundentakt statt zu gleiten -- ehrlicher
     (die Anzeige ist digital) und eine Bildberechnung je Sekunde statt
     sechzig.
  4. GRAVIERTE ZIFFERN. Ein dunkler Saum oben, ein heller unten -- das
     Lichtverhalten einer Vertiefung. Die Ziffern stehen damit IM Blatt
     statt darauf.

DIE KONSOLE -- DAS METALL FAENGT DAS LICHT

Auf den Kacheln leuchtet das Licht in der Farbe der Kategorie; dort ist
es ein Hinweis. Auf der Konsole waere das falsch -- ein farbiger
Schleier auf gebuerstetem Metall sieht aus wie eine Folie darauf.
Metall zeigt seine Form ueber die SPIEGELUNG: weiss, schmal, hart an
der Kante, und sie wandert mit dem Zeiger ueber die Fassung wie ein
Fenster, an dem man vorbeigeht. Erst dadurch sieht man, dass die
Fassung gewoelbt ist. Ein Standbild kann das nicht.

Dieselbe Falle wie heute Frueh dabei vermieden, diesmal vorher
bedacht: `.willkommen > *` haette dem Lichtelement wieder sein
`position: absolute` genommen -- jetzt `:not(.licht)`.

UND EIN FEHLER, DEN NUR DAS HANDY GEZEIGT HAT

Die Mulde der Uhr hing am Kasten daneben und war an dessen rechtem Rand
ausgerichtet. Am Rechner passte das; bei 412 px steht die Uhr mittig,
und die Mulde lag als dunkle Scheibe neben ihr. Sie entsteht jetzt aus
zwei Schattenringen der Uhr SELBST und ist damit konzentrisch bei jeder
Groesse -- die bessere Bauart, nicht nur die reparierte: Eine Fassung,
die man ausrichten muss, richtet irgendwann jemand falsch aus.

Zehn Pruefungen gelaufen, alle gruen. Rechner und Handy angesehen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 15:00:59 +02:00
DogFatherGitandClaude Opus 5 532daa3edf Alles fertig: Konsole montiert, Tageskachel als Instrument, zwei Seiten repariert
DIE KONSOLE -- DREI STUFEN DRAUF

  1. VIER NIETEN in den vier Fasen. Das Einzige, was eine Flaeche
     endgueltig zu einem GEGENSTAND macht, ist die Frage, wie sie
     befestigt ist. Ein Gehaeuse haengt nicht in der Luft.
  2. EINE GEFRAESTE NUT trennt Text von Instrumenten -- dunkel auf der
     Lichtseite, hell auf der Schattenseite. Genau umgekehrt zu einer
     aufgemalten Linie, und deshalb sieht sie nach Material aus.
  3. DIE UHR SITZT IN EINER MULDE statt auf der Platte.

Drei Fehler dabei, alle im Bildschirmfoto gesehen: Die Nieten waren
QUADRATE (eine Hintergrundebene laesst sich nicht runden -- jetzt aus
Radialverlaeufen, die selbst rund sind). Die Mulde lag UEBER der Uhr
und hat die polierte Luenette zu mattem Grau gedaempft (`::after` wird
nach allen Kindern gezeichnet). Und das Raster musste von der
Nieten-Ebene herunter: Eine Ebene hat nur EINE Deckkraft.

DIE TAGESKACHEL "WAS IST DRAN"

Die drei Zeilen sind jetzt MODULE -- Fase, Kantenlicht in der Farbe
ihres Bereichs, Eckwinkel. Sie sind damit kleine Ausgaben derselben
Bauteile, zu denen sie fuehren, was sie ja auch sind. Die ZAHL wurde
zum gefassten Schild wie das Zeichen auf den Kacheln, und zwischen den
Haelften laeuft dieselbe gefraeste Nut wie auf der Konsole.

Ein Rueckschritt dabei, von der Pruefung sofort gemeldet: Ich hatte
die Ziffer weiss gemacht, weil das auf Metall gut aussieht -- damit
war ihre Aussage weg. Die Zahl traegt die Farbe ihres Bereichs und bei
etwas Ueberfaelligem die Warnfarbe; das ist die schnellste Auskunft der
ganzen Kachel. Die Farbe gehoert in die Ziffer, nicht ins Schild.

SPICY MEDIA SIEHT DIE ZAHLEN JETZT NIRGENDS

Vorher nur Kachel und Seite -- die Creator-Zahlen standen weiterhin im
Dashboard, weil das sie ueber eine eigene Schnittstelle holt. Die ist
jetzt zu (404 am Server, nicht in der Oberflaeche). Das Dashboard
bleibt fuer sie stehen: Es faengt den Fehlschlag ausdruecklich ab.
Mit Pruefung und Gegenprobe.

UND ZWEI SEITEN, DIE BEIM ANSEHEN AUFFIELEN

Das ist der Ertrag der Durchsicht jener zwoelf Seiten, die bisher nur
GEPRUEFT und nie ANGESEHEN worden waren:

  * chat.html und uebersicht.html luden kopf.js OHNE wahl.js. Der
    Umschalter "Meine Sicht" fiel dort auf das nackte Systemfeld
    zurueck: 92 x 19 px, grauer Kasten, Systemschrift -- auf allen
    anderen Seiten ist es ein selbst gebautes Bedienelement, hinter dem
    dasselbe Feld unsichtbar bei 2 x 2 px liegt. Kaputt war nichts. Es
    sah nur aus wie aus einem anderen Programm, und genau das findet
    keine Pruefung, die auf Fehlermeldungen achtet.
    Neue Pruefung: Wer kopf.js laedt, muss wahl.js laden -- und vorher.
  * Der Chat-Rahmen gehoerte als einzige grosse Flaeche noch nicht zum
    Modulsystem. Jetzt schon.

18 Pruefungen gelaufen, alle gruen. Rechner und Handy angesehen,
dazu Kalender, Chat, Automationen, Uebersicht, Start-Check,
Steckbrief, Bereiche, Profil, Content, Scouting und Report.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 14:31:14 +02:00
DogFatherGitandClaude Opus 5 90d5898640 Drei Stufen auf die Kacheln -- Fase, Rahmung, gefasstes Schild
Filipe: "perfektionier alle kacheln die du vorhin gewechselt hast auf
der ganzen website, mach sie alle noch geiler und geiler und noch
spezieller. sie gehen aber in eine gute richtung schon."

Alle drei Stufen sind FORM, keine neue Farbe -- das war die Lehre der
letzten Runden. Sie gelten fuer alle 35 Bauteile auf allen 18 Seiten,
weil sie in module.css stehen.

STUFE 1 -- DIE SCHRAEGE WIRD EINE ECHTE FASE

Bisher war die abgeschnittene Ecke ein Loch: Material, das fehlt. Eine
gefraeste Fase hat eine FLAECHE, und auf der liegt Schatten, weil sie
schraeg zum Licht steht. Ein Innenschatten aus der Richtung der
Schraege macht daraus ein bearbeitetes Werkstueck.

STUFE 2 -- ECKWINKEL AN DREI ECKEN STATT AN EINER

Eine einzelne Ecke liest sich als Verzierung, drei lesen sich als
RAHMUNG: Das Auge schliesst sie zu einem Ausschnitt. Die vierte bleibt
frei, dort sitzt die Fase -- ein Winkel auf einer abgeschnittenen Ecke
zeigte ins Leere.

STUFE 3 -- DAS ZEICHEN BEKOMMT EINE METALLFASSUNG

Die Plakette war ein abgerundetes Quadrat mit Farbschleier, also
dieselbe Form wie ueberall sonst im Netz. Jetzt ist sie ein gefasstes
Schild: dieselbe abgeschnittene Ecke wie ihre Karte, ein 2 px breiter
Ring aus gebuerstetem Metall, und die Kategoriefarbe INNEN. Damit
spricht die Anwendung EINE Materialsprache -- Konsole, Luenette der
Uhr und Schild sind dasselbe Metall.

ZWEI FEHLER DABEI, BEIDE GEMESSEN STATT VERMUTET

  * DIE RUNDUNG BLIEB. `.kachel[data-gross="ja"] .kachel__zeichen`
    setzt in start.css zweimal einen Radius (21 px, 18 px) und ist
    staerker als eine einzelne Klasse. Gemessen: 18 px, obwohl
    module.css 0 setzt und zuletzt geladen wird. Heraus kam ein Schild
    mit abgeschnittener Ecke UND runden Ecken. Das ist heute die
    dritte Spielart derselben Falle -- `:where()` war zu schwach,
    `body.start .willkommen` zu stark, hier ist es die
    Verschachtelung.
  * DIE FASSUNG WAR ZU GRELL. Fast weiss auf 2 px Breite las sich als
    Rahmen, der lauter ist als das Zeichen darin. Eine Fassung soll
    das Schild halten, nicht mit ihm konkurrieren -- dieselben Stopps,
    eine Blende dunkler.

18 Pruefungen gelaufen, alle gruen, keine mit gesunkener Anzahl.
Rechner und Handy (412 px) angesehen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 13:37:29 +02:00
DogFatherGitandClaude Opus 5 2acb6bc020 Das Maus-Licht ist zurueck, und die Begruessung ist keine Kachel mehr
DAS LICHT, DAS DEM ZEIGER FOLGT -- MEIN EIGENER FEHLER VON HEUTE FRUEH

Es war nicht geloescht. In module.css stand seit heute

    .kachel > * { position: relative; z-index: 1; }

damit der Inhalt ueber Raster und Kantenlicht liegt. Das Lichtelement
ist aber ein direktes KIND jeder Karte -- ihm wurde damit sein
`position: absolute` genommen. Aus einer Flaeche ueber der ganzen Karte
wurde ein leerer Inline-Span ohne Ausdehnung. Im Browser gemessen:
`display: inline`, obwohl in start.css `absolute` steht.

Merksatz dazu im Code: Eine Regel auf `> *` trifft auch das, was gar
kein Inhalt ist.

Zweiter, aelterer Fehler beim selben Thema, den erst die Pruefung
gefunden hat: Beim Wechsel von einer Kachel direkt auf die naechste
ging das Licht GANZ aus. `pointermove` der neuen Karte meldet einen
Bildaufbau an, `pointerout` der alten kommt danach und hat ihn
geloescht -- obwohl er gar nicht ihr gehoerte. Jetzt wird nur noch der
EIGENE Bildaufbau entwertet.

DIE BEGRUESSUNG FLIEGT AUS DER REIHE

Filipe: "diese hauptkachel muss komplett aus der rolle fliegen im
gegenzug zu den anderen ... AUCH MIT DER UHR RECHTS; WIE IN DER
BUISNESS HUB SEITE VON VANVAN."

Nachgesehen statt geraten: Auf VanVans Business-Hub gibt es keine Uhr.
Gemeint ist das `gate-medaillon` der Anmeldeseite -- ein runder
Kegelverlauf, der wie gebuerstetes Metall aussieht, gefasst in zwei
eingelassenen Ringen. Diese Bauart steht jetzt hier, weitergetrieben.

Die Begruessung ist keine Kachel mehr, sondern eine KONSOLE, und sie
unterscheidet sich in der FORM, nicht im Lack:

  * Sie ist BREITER ALS DIE SEITE -- sie tritt links und rechts ueber
    die Spalte hinaus, in der alle Kacheln stehen.
  * Sie ist ein ACHTECK. Die Module haben EINE abgeschnittene Ecke,
    sie hat VIER.
  * Sie hat eine METALLFASSUNG, laengs gebuerstet, mit je einer warmen
    und einer kuehlen Spiegelung.

Die Uhr ist von 124 auf 164 px gewachsen und hat eine echte Luenette:
10 px deckendes Metall, zwoelf eingravierte Stundenmarken, sechzig
feine Minutenstriche, Glaskuppe.

VIER FEHLER AUF DEM WEG DAHIN, ALLE IM BILDSCHIRMFOTO GESEHEN

  1. HALBDURCHSICHTIGES METALL ist kein Metall, sondern graues Glas.
     Stand gleichzeitig an Konsole und Uhr.
  2. KEGELVERLAUF AUF EINEM BREITEN BALKEN bewirkt nichts: Die ganze
     Oberkante liegt in wenigen Grad. Rund -> conic, lang -> linear.
     Die Verlaufsart muss zur FORM passen, nicht zum Material.
  3. DIE SKALA DER UHR WAR NIE SICHTBAR, seit es sie gibt. Ihre Maske
     rechnete Prozente auf die weiteste ECKE (116 px) statt auf den
     Radius (82 px) -- der Ring lag komplett ausserhalb der Uhr.
     `closest-side` behebt es. Eine unsichtbare Verzierung sieht aus
     wie gar keine, nicht wie ein Fehler.
  4. `body.start .willkommen` in start.css hat die neue Konsole
     ueberschrieben -- nicht ueber die Ladereihenfolge, sondern ueber
     die SPEZIFITAET (0,2,1 gegen 0,1,0). Derselbe Fehler wie mit
     `:where()` heute Frueh, nur andersherum: damals zu schwach
     geschrieben, hier zu stark stehen gelassen.

Der Ueberstand haengt an der Polsterung der Inhaltsspalte
(`min(34px, 3.6vw)`) statt an einer festen Zahl -- eine feste haette
auf dem Handy 17 px aus dem Bildschirm geragt.

UND EINE PRUEFUNG, DIE UNTER DEN BILDRAND GEZIELT HAT

pruef-start-ansicht meldete zwei Fehler am Licht. Das Licht war in
Ordnung: Die hoehere Konsole hatte Kachel 3 auf y = 1134 geschoben,
bei einem 1200 px hohen Fenster lag ihre Mitte unter dem Rand. Sie
rollt jetzt hin, misst danach neu -- und die Zahl der wirklich
gemessenen Kacheln steht in der Bedingung. 140 statt 137 Pruefungen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 13:03:16 +02:00
DogFatherGitandClaude Opus 5 ac432d85e1 Spicy Media: sieben stille Luecken -- und der Kalender zeigt wirklich nur noch Eigenes
Filipe hat drei Sachen gemeldet. Zwei davon waren nicht das, wonach sie
aussahen.

1. "BEI DER SPICY ROLLE IST DA EIN PROBLEM" (Creator-Profil laedt nicht)

Nicht diese Seite war kaputt. In NEUN Skripten stand dieselbe Zeile

    const LEITUNG = new Set(['admin', 'manager']);

und in keinem davon 'spicy'. profil.js nahm deshalb den Creator-Zweig
und lud das EIGENE Profil -- ein Spicy-Zugang ist kein Creator, also
404. Acht der neun sind nicht kaputtgegangen; sie haben sich still
falsch verhalten.

Die Liste steht jetzt EINMAL in bereiche.js. Beim Aufraeumen kamen mit
derselben Suche sechs weitere Luecken heraus, alle vom selben Typ:

  * Aufgaben abbrechen -- Server UND Knopf ohne 'spicy'
  * Teilen-Knopf in der Kopfleiste -- ohne 'spicy'
  * Chat: die Gespraechsliste laeuft ueber eine feste Rollenfolge.
    Wer nicht darin steht, taucht gar nicht auf -- eine Person, die es
    fuer die anderen nicht gibt.
  * Kalender: dieselbe Rollenfolge, dort landete Spicy Media durch
    indexOf() === -1 ganz oben statt an ihrem Platz.
  * Dashboard: "Creator anlegen" nur fuer admin/manager -- der Server
    haette sie gelassen, den Knopf hat sie nie gesehen.
  * Personenliste: ROLLENNAME ohne 'spicy'. Der Auffangwert
    `|| p.rolle` schrieb "spicy" statt "Spicy Media" -- plausibel
    genug, um jahrelang zu bleiben.

`pruef-css-klassen.mjs` verlangt jetzt, dass kein Workspace-Skript sich
wieder eine eigene Rollenliste baut. Mit Gegenprobe, dass der Sucher so
eine Liste auch wirklich findet.

2. "DIE ZAHLEN DIESE KATEGORIE NICHT SEHEN"

Kachel und Seite waren beim Anlegen der Rolle auf ["spicy","admin"]
gesetzt worden -- "dieselben Rechte ausser Automationen". Beides jetzt
auf DogFather allein.

3. "IN CIGDEMS KALENDER STEHT IMMER NOCH MEIN MANAGER MEETING"

Das war MEIN Denkfehler von heute Frueh, nicht ein vergessener Fall.
Ich hatte "geht alle an" als "kein Teilnehmer eingetragen" definiert --
also entschied der Server, was alle angeht, und nicht der, der den
Termin anlegt. Wer niemanden eintraegt, meint aber meistens nicht
"alle", sondern "mich".

Der Fall ist entfallen. Sichtbar ist ein Termin jetzt nur noch fuer
den, der ihn angelegt hat, dem er zugeordnet ist, der das Gegenueber
ist -- oder der in der Teilnehmerliste steht. Damit ist das Markieren
im Termin die einzige Antwort auf "wen geht das an": eine sichtbare
Entscheidung im Formular statt einer unsichtbaren Regel im Server.

Calls & Protokolle rufen dieselbe Funktion auf und koennen deshalb gar
nicht auseinanderlaufen -- das war Filipes vierter Wunsch, und er ist
mit derselben Zeile erledigt.

Am laufenden System als Cigdem nachgemessen: Profil laedt ("Profil:
Luna"), Zahlen-Kachel weg und Seite gesperrt, im Kalender NUR der
Termin, bei dem sie markiert ist, Calls zeigt genau denselben.

Fuenf Pruefungen auf die endgueltige Regel umgeschrieben statt
geloescht (sicht, spicy, teilnehmer, scout-zuteilung, css-klassen).
Die dritte Drehung bei scout-zuteilung steht mit allen drei Staenden
in der Akte -- eine geloeschte Pruefung hinterlaesst keine Spur davon,
dass hier einmal etwas anderes galt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 07:36:59 +02:00
DogFatherGitandClaude Opus 5 4eab64bd20 Eine neue Form fuer jedes Modul -- und der Kalender zeigt nur noch, was einen angeht
Filipe: "DU HAST WIEDER EINE KLEINE AENDERUNG UEBERALL GEMACHT ANSTATT
EINE RIESEN AENDERUNG." Er hatte recht, und der Grund war jedes Mal
derselbe: Ich habe das MATERIAL getauscht (Mattglas, Leuchtschiene,
Verlauf) und die FORM gelassen. Ein abgerundetes Rechteck bleibt ein
abgerundetes Rechteck -- und die Silhouette ist das Einzige, was man aus
fuenf Metern erkennt.

Jetzt ist die Form eine andere: abgeschnittene Ecke oben links
(clip-path, echte Silhouette), ein Kantenlicht in der Kategoriefarbe
darauf, Eckwinkel unten rechts, ein feines Raster statt Koernung.
35 Bauteile auf allen 18 Seiten, in einer eigenen Datei (module.css).

DREI DINGE, DIE DABEI SCHIEFGINGEN UND JETZT ABGESICHERT SIND

1. Die Regeln standen in :where() -- Spezifitaet null, also gewann jede
   aeltere .kachel::before-Regel. Jetzt :is().
2. module.css stand an DRITTER Stelle im Ladeweg. Auf Calls,
   Automationen, Bereich und Dateien hat sie damit gar nichts bewirkt:
   Die Seitendateien setzen dort selbst border-radius und box-shadow und
   kommen spaeter. Genau das ergab wieder "ueberall ein bisschen". Sie
   wird jetzt als LETZTE geladen, geprueft auf allen 18 Seiten.
3. Die Klassenliste steht siebenmal in der Datei (CSS kennt keine
   Variable fuer Selektoren). Eine vergessene Kopie faellt niemandem
   auf -- pruef-css-klassen vergleicht sie deshalb alle, mit Gegenprobe.

DER KALENDER: NUR NOCH, WAS EINEN ANGEHT

Filipe: "calls oder termine soll jeder nur sehen die er selber macht
oder jeden betrifft." Das gilt auch fuer DogFather, und das ist die
eigentliche Aenderung -- er bekam bisher 1=1. "Jeden betrifft" heisst:
ohne Gegenueber und ohne Teilnehmerliste. Wer niemanden eintraegt, meint
alle. Die Rollenfrage entfaellt im Kalender damit vollstaendig.

SECHS PRUEFUNGEN, DIE EINE WELT GEMESSEN HABEN, DIE ES NICHT MEHR GIBT

Alle sechs wurden auf die neue Regel umgeschrieben, keine geloescht --
loeschen haette die Zahl gesenkt und den neuen Weg ungeprueft gelassen:
ics, serien, sicht, spicy, teilnehmer, tagesblick, team.

UND VIER ECHTE FUNDE, DIE DABEI HERAUSFIELEN

* pruef-workspace-seiten war seit dem Bildumbau von heute Frueh auf
  ALLEN 32 Seiten rot: Sie verlangte noch das eine Motiv. Die neue
  Bedingung ist schaerfer als beide alten -- das geladene Bild muss zu
  dem Merkmal passen, das die Seite selbst traegt. Und die Meldung sagt
  jetzt, WELCHE Bedingung gefallen ist.
* pruef-leistung-optik hat sich selbst ausgesperrt (meldete sich als
  Manager an, der die Zahlen seit Filipes Anweisung nicht mehr sehen
  darf) und stuerzte danach ab: 2 Pruefungen statt 56. Der Absturz hat
  verdeckt, dass sie sieben Fingerziele als "zu klein" meldete -- es
  waren die unsichtbaren Knoepfe der zugeklappten Woche, 0x0.
* personen.html beim Manager: Der Erklaersatz ("Codes, Sperren und das
  Protokoll bleiben bei DogFather") kam nie an -- das Element gab es
  nicht, und das && davor hat den Fehlgriff verschluckt. Die Liste stand
  ausserdem dauerhaft auf aria-busy="true": fuer einen Screenreader lud
  die Seite fuer immer.
* pruef-schranke hielt personen.html noch fuer DogFather-only. Die Seite
  wechselt jetzt die Erwartung, statt aus der Pruefung zu verschwinden:
  Seite auf fuer die Leitung, Verwaltungsdaten dahinter weiterhin zu.

Die Anmeldeseite wurde nicht angefasst (nur ihr Versionsstempel).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 07:12:48 +02:00
DogFatherGitandClaude Opus 5 e8478c9cae Spicy sieht DogFathers Sachen nicht mehr -- und sechs Fehler dazu
VIER DINGE AUS DEN BILDSCHIRMFOTOS, jedes ein echter Fehler:

1. SPICY MEDIA SAH DOGFATHERS DATEN. Beim ersten Anlauf bekam die Rolle
   dieselbe Regel wie DogFather (`1=1`) -- damit stimmte der Ueberblick
   ueber Manager, Scouts und Creator, und nebenbei standen seine eigenen
   Termine, Aufgaben, Dateien und Eintraege mit drin. Jetzt gibt es
   ohneDogFather() als EINE Stelle dafuer: Zwei Spalten werden geprueft,
   `creator_id` (um wen geht es) und `erstellt_von` (wer hat es
   geschrieben) -- ein Termin, den er sich selbst anlegt, haengt nur an
   der zweiten. `IS NULL OR NOT IN` und nicht bloss `NOT IN`: In SQL ist
   `NULL NOT IN (...)` weder wahr noch falsch, die Zeile fiele
   stillschweigend heraus.

2. "PERSOENLICHER ZUGANGSCODE · undefined" auf der Anmeldeseite. Eine
   zweite Namensliste im Browser, die beim Hinzufuegen der Rolle
   niemand gepflegt hat. Ein unbekannter Schluessel faellt jetzt auf
   sich selbst zurueck statt auf `undefined` -- haesslich, aber
   sichtbar.

3. SPICY KAM NICHT IN DIE PERSONENVERWALTUNG. Der Server liess sie
   herein, das Skript warf sie wieder hinaus: zwei Listen fuer dieselbe
   Frage, gepflegt wurde nur die erste.

4. DIE ANMELDEKARTE WAR ZU KLEIN FUER FUENF ROLLEN. Der Kommentar an
   genau dieser Stelle warnt woertlich davor -- und ich habe getan,
   wovor er warnt: eine Rolle eingefuegt und die Zahl daneben nicht
   angefasst. Gemessen: Inhalt 573 px in einer 526 px hohen Tafel, die
   Fusszeile stand unter dem Rahmen. Schrift kleiner half nicht (sie lag
   schon auf dem Anschlag), also sind die Abstaende an neun Stellen
   enger. Nachgemessen auf fuenf Groessen: passt ueberall.

DIE ZEIT WAR WIRKLICH FALSCH. Nicht nur in meinem Satz: `datum()` in
personen.js schnitt die ISO-Zeichenkette ab -- und die ist UTC. Im
Protokoll stand 03:00, wo 05:00 war. Das Tueckische daran ist, dass es
nie kaputt aussieht: Eine Uhrzeit ist immer plausibel.

PROFILBILDER WERDEN JETZT GEZEICHNET. Der Server lieferte sie seit
gestern mit, gezeichnet wurden sie nirgends -- deshalb aenderte sich
nichts. Jetzt in der Personenauswahl (Aufgaben, Termine, Dateien,
Bereiche), in der Gespraechsliste und an jeder Nachricht. Der
Anfangsbuchstabe bleibt als Unterlage LIEGEN: Faellt das Bild aus, steht
dort weiter etwas Sinnvolles.

DER CHAT: Gesichter mit Rollenfarbe, Blasen mit Richtung (die erste
einer Folge eckig, die naechsten rund -- so sieht man, wo ein Gedanke
anfaengt), das offene Gespraech mit Schiene, Ungelesenes hervorgehoben,
und das Eingabefeld in einer eigenen Leiste, die sich beim Schreiben
hebt.

ZWEI EIGENE PATZER, beide von Pruefungen gefunden:
  - `o is not defined`: Mein Suchmuster hat die zwei Zeilen fuer das
    Bild ans DATEIENDE gesetzt statt in die Schleife -- und in
    kalender.js an einen <span> statt ans <option>. Gemeldet von
    pruef-sicht, das die Browserkonsole mitliest.
  - Ein Kommentar mit `bild` in schraegen Anfuehrungszeichen stand INNEN
    in einer Vorlagenzeichenkette und hat sie geschlossen. Die halbe SQL
    wurde zu Programmtext.
Dazu: `.chat-neu` gibt es nicht (heisst `.chat__eingabe`), und der
Rollenknopf hatte fuer den Manager keinen sichtbaren Fokus -- ein
box-shadow wird von `overflow: hidden` abgeschnitten, ein outline nicht.

Gruen: spicy (43), creator-anlegen (29), personen-liste (33), sicht (48),
chat (48), chat-optik (24), buehne (38), struktur (32), css-klassen (15),
handy (59), breiten (23), formulare (19), start-ansicht (136),
lesbarkeit (14), barrierefrei (18), tempo (8), kopf-messen (3),
glocke (26), haerte (20), manager-sicht (43), alle-wege (19),
aufgabenbrett (44), kalender (84).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 05:46:19 +02:00
DogFatherGitandClaude Opus 5 3b356d6eef Ein Material fuer die ganze Website -- und die Uhr wird zum Chronographen
DIE UHR: Sekunde nach AUSSEN, Minute in die Mitte, Stunde nach innen
(Wunsch Filipe). Das ist die Anordnung eines Chronographen und nicht die
eines Fortschrittsbalkens: Der schnellste Zeiger laeuft auf der
laengsten Bahn, weil man Bewegung dort am besten sieht -- der langsamste
innen, wo eine kleine Drehung viel bedeutet. Die drei Umfaenge sind
mitgewandert; ein vertauschter Ring ohne vertauschte Zahlen endet nie
dort, wo er soll.

EIN MATERIAL FUER ALLE SEITEN. Die Startseite hatte seit heute
beleuchtete Platten, die anderen sechzehn Seiten flache Rechtecke -- man
wechselte die Seite und fiel aus einer Oberflaeche in eine andere. Ich
habe das bisher Seite fuer Seite nachgezogen, und genau deshalb war es
nie fertig: Es sind FUENFUNDVIERZIG Stellen.

Jetzt EINE Regel. Die Klassenliste ist nicht erfunden, sondern gemessen
-- es sind genau die, die `var(--flaeche)` als Kartenflaeche benutzen.
`:where()` ist der Kniff dabei: Spezifitaet null, also ueberschreibt die
Regel nichts, was eine Seite selbst festlegt. Eine Warnkarte bleibt rot,
eine Spalte behaelt ihre Statusfarbe. Ohne das haette ich an dreissig
Stellen `!important` gebraucht.

DREI FUNDE DER PRUEFUNGEN, alle berechtigt:

1. `.profil-gruppe` gibt es nicht -- ich hatte den Namen aus dem Kopf
   geschrieben statt aus der Datei. pruef-struktur meldete totes CSS.
   Die Karten auf profil.html heissen `.gruppe`, und genau der Name darf
   NICHT in die Liste: Auf der Startseite heissen die Kachelgruppen
   ebenso und haetten ploetzlich eine Kartenflaeche.

2. Der erste Verlauf war HELLER als das, was er ersetzt. Ein Material,
   das die ganze Website aufhellt, hellt auch jeden Text darauf auf --
   und das faellt an der leisesten Schrift zuerst auf.

3. `.gruppe__unter` stand auf 4,21:1. Und das ist der interessante Fall:
   Die Farbe war nicht schuld, eine VERSCHIEBUNG war es. Mit der fuenften
   Rolle rutschte auf personen.html alles eine Zeile nach unten, und die
   Zeile landete auf einer helleren Stelle des Buehnenbilds. Sie ist die
   einzige Beschriftung ohne Karte -- ein Text, dessen Untergrund ein
   FOTO ist, bekommt nicht den leisesten Ton. Jetzt 5,51:1.

Der zweite Fund fiel nur auf, weil die Zahl sich nach meiner ersten
Korrektur KEIN Stueck bewegte (zweimal exakt 4,21) -- dasselbe Zeichen
wie schon zweimal heute: falsche Stelle, nicht zu wenig.

Gruen: buehne (38), css-klassen (15), struktur (32), handy (59),
breiten (23), start-ansicht (136), lesbarkeit (14), tempo (8),
personen-liste (33).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 05:14:01 +02:00
DogFatherGitandClaude Opus 5 68bc116c36 Die Rolle "Spicy Media" -- und drei Fehler, die nur ihre Pruefung fand
DIE PERSONENTABELLE WURDE NEU GEBAUT. Eine Rolle ist ein erlaubter Wert
in einer Spalte, und der steckt in einem CHECK -- den kann SQLite nicht
aendern. Auf `personen` zeigen ZWEIUNDFUENFZIG Fremdschluessel.

Deshalb wird der Bauplan NICHT abgeschrieben, sondern gelesen: Der
CREATE-Text kommt aus sqlite_master, darin wird ausschliesslich die
Rollenliste ersetzt, und die Kopierliste kommt aus PRAGMA table_info.
Die beiden aelteren Umstellungen schreiben ihre Spalten von Hand ab --
`personen` hat seit damals SIEBEN dazubekommen (bild, ueber_mich,
tiktok ...). Wer hier abschreibt, verliert alle Profilbilder.

ZWEI FRAGEN, DIE MAN AUSEINANDERHALTEN MUSS:
  siehtAlles()   = DogFather ODER Spicy Media -> Listen, Uebersichten
  istDogFather() = nur DogFather              -> loeschen, Rollen, Codes
Es waere weniger Arbeit gewesen, istDogFather() um "spicy" zu erweitern
-- und genau das waere der Fehler: Spicy Media koennte dann DogFather
loeschen.

DER CHAT BRAUCHTE NICHTS. Er haengt allein an der Teilnehmerliste und
kennt kein "das Management sieht alles". Spicy Media sieht fremde
Gespraeche nicht, weil es dafuer keinen Weg gibt -- nicht, weil eine
Abfrage es verbietet.

DREI ECHTE FEHLER, alle von pruef-spicy gefunden, keiner vorher sichtbar:

1. MEIN EIGENER KOMMENTAR STAND IM BAUPLAN. SQLite hebt den CREATE-Text
   woertlich auf, samt Kommentaren. Ich hatte "'spicy' steht HIER mit
   drin" hineingeschrieben -- und die Erkennung suchte genau dieses Wort
   im ganzen Text. Ergebnis: Die Umstellung hielt die Tabelle fuer
   erledigt, obwohl die CHECK-Regel noch die alte war. Jetzt wird die
   Regel herausgeschnitten und NUR darin gesucht; der Kommentar steht
   ausserhalb des SQL.

2. DER SICHERUNGSNAME HATTE NUR MINUTEN. `VACUUM INTO` weigert sich, eine
   vorhandene Datei zu ueberschreiben -- zu Recht. Zwei Umstellungen in
   derselben Minute wollten in dieselbe Datei, die zweite scheiterte, und
   weil ohne Sicherung nicht umgestellt wird, blieb sie aus. Es sah nach
   "lief" aus (die Datei lag ja da) und war keine. Jetzt mit Sekunden.

3. EINE FRISCHE DATENBANK LEGTE DIE ALTE ROLLENLISTE AN und stellte beim
   allerersten Start sofort um -- Tabelle neu bauen, Sicherung schreiben,
   fuer nichts. Ein Bauplan, der sofort umgebaut werden muss, ist der
   falsche.

Und ein vierter in der Pruefung selbst: Der Chat-Aufbau benutzte einen
falschen Weg, das Gespraech entstand gar nicht -- "Spicy Media sieht 0
Gespraeche" war trotzdem gruen, weil es keine gab. Jetzt ist das Anlegen
selbst eine Pruefung, und eine Gegenprobe zeigt, dass Max es sehr wohl
sieht.

DAZU: Manager sehen die Kachel "Personen & Zugaenge" -- die SEITE geht
auf, die Schnittstellen nicht. Alles unter /verwaltung haengt weiter an
`nurAdmin` und antwortet 404; sie sehen genau den Teil, fuer den es
einen Weg gibt. Spicy Media legt Creator UND Manager an (zweite enge
Tuer, Rolle steht auch dort nicht im Aufruf; ein Manager kommt durch
sie nicht). Der Rollentext des Managers stimmte nicht mehr -- er legt
jetzt Creator an.

pruef-spicy: 36 Pruefungen, alle gruen. Ausserdem gruen: personen-liste
(33), creator-anlegen (29), sicht (48), css-klassen (15), alle-wege (19),
haerte (20), manager-sicht (43).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 04:56:07 +02:00
DogFatherGitandClaude Opus 5 4bb5afc942 Drei Ringe, klappbare Gruppen -- und die Calls-Seite endlich wirklich umgebaut
DU HATTEST RECHT (Bildschirmfoto 3). Beim letzten Mal habe ich an der
Calls-Seite nur das MATERIAL der Karten getauscht und die Aufstellung
gelassen. Drei Gruppen untereinander, und weil "Steht an" 27 Eintraege
hat, lagen die beiden anderen ausserhalb des Bildes -- man SAH die
Aenderung gar nicht, weil man nie so weit kam. Das war Lackieren, kein
Umbauen.

JETZT EIN BRETT AUS ZWEI SPALTEN. Die Gruppen sind eigenstaendige Tafeln
in einem Raster; "Protokoll fehlt" (kurz, dringend) und "Festgehalten"
(Archiv) liegen NEBEN der langen Liste statt darunter. `align-items:
start` ist dabei der Punkt: Ohne ihn waeren alle Tafeln so hoch wie die
hoechste, und neben der langen Liste stuenden zwei fast leere Kaesten.
Welche Tafel wo landet, entscheidet die Breite und nicht das Skript --
eine festgeschriebene Spalte waere eine Zahl, die beim naechsten Fenster
falsch ist.

Jede Tafel klappt zu, und der Zustand wird gemerkt. Vorgabe: "Festgehalten"
ist zu -- es ist das Archiv; wer die Seite oeffnet, will wissen, was
ansteht.

DIE GRUPPEN AUF DER STARTSEITE genauso. Der Kopf IST der Schalter, nicht
ein Dreieck daneben: Die ganze Zeile ist ein Ziel von 40 Pixeln statt
eines von vierzehn. <button> statt <div>, damit Tastaturbedienung und
Ansage nicht mit tabindex und role nachgebaut werden muessen. Gemerkt
wird je Gruppe UND je angesehener Rolle -- DogFather, der sich einen
Creator ansieht, hat dort eine andere Aufteilung im Kopf.

DIE UHR: drei Ringe statt zwei. Aussen die Stunde in Schwarzsilber, in
der Mitte die Minute in Blau, innen die Sekunde in Rot -- von aussen nach
innen immer schneller, so liest man eine Uhr ohne nachzudenken.

UND SIE IST SCHARF. Kein blur(), kein drop-shadow mehr auf den Boegen --
an der alten lag beides drauf, und der Vorwurf stimmte. Der Glanz kommt
jetzt aus dem VERLAUF: Echtes Metall glaenzt nicht, weil es leuchtet,
sondern weil es das Licht abwechselnd hell und dunkel zurueckwirft. Fuenf
Stopps statt zwei -- ein zweifarbiger Verlauf waere ein Farbverlauf,
erst der Wechsel ist Metall. Dazu `shape-rendering="geometricPrecision"`
(sonst rastert der Browser duenne Boegen grober) und Kappen auf `butt`
statt `round`: Eine runde Kappe steht ueber das Ende hinaus, bei null
Sekunden saehe man trotzdem einen Punkt.

Minute und Stunde laufen WEICH mit: die Minute bekommt die Sekunden
anteilig, die Stunde die Minuten. Ein Minutenring, der einmal pro Minute
springt, sieht aus wie eine haengende Anzeige.

Gruen: css-klassen (15), call-kategorien (17), start-ansicht (136),
handy (59), breiten (23), buehne (38), struktur (32).

NOCH OFFEN: der neue Kachelstil fuer die ganze Website und die Rolle
"Spicy Media".

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 04:39:32 +02:00
DogFatherGitandClaude Opus 5 49bd4a7cec Manager legen Creator an -- eine neue Tuer statt eines Schluessels fuer die alte
Filipe: "wieso greift das in die rechte rein? ist doch ok, die sollen
einfach paar leute selber hinzufuegen koennen." Er hat recht, und ich
hatte zwei Dinge in einen Topf geworfen: Das hier braucht KEINE
Datenbankaenderung -- nur die Rolle "Spicy Media" braucht eine.

WARUM EIN EIGENER WEG UND NICHT DIE ALTE TUER

Alles unter /workspace/api/verwaltung haengt an EINER Schranke
(nurAdmin). Dahinter liegen acht Wege: Rollen aendern, Codes neu setzen,
sperren, loeschen, Protokoll lesen. Einen Manager dort hineinzulassen und
danach in jedem der acht einzeln zu pruefen, was er darf, ist genau die
Bauweise, durch die am 31.08.2026 ein Loch entstanden ist -- zwei von
drei Stellen abgesichert, die dritte vergessen.

Deshalb bleibt die Tuer zu, und daneben steht eine neue mit genau einem
Zweck: POST /workspace/api/creator-anlegen. Sie kann nichts anderes, als
einen Creator anzulegen -- nicht weil eine Abfrage es verbietet, sondern
weil es hier keinen anderen Weg gibt. Unterschied zwischen "darf nicht"
und "kann nicht".

DIE ROLLE STEHT NICHT IM AUFRUF, sie wird im Server gesetzt. Ein Feld
`rolle` im Koerper waere die naheliegende Loesung und die falsche: Dann
muesste eine Abfrage sie pruefen, und eine vergessene Abfrage ist ein
zweiter Zugang mit vollen Rechten. Geprueft wird deshalb nicht, dass ein
mitgeschicktes `rolle: "admin"` abgelehnt wird, sondern dass es
WIRKUNGSLOS ist.

DIE ZUTEILUNG PASSIERT SOFORT. Ein Creator ohne Betreuung ist fuer alle
ausser DogFather unsichtbar -- der Manager haette ihn angelegt und
danach nicht mehr gesehen. Wer anlegt, betreut; ein eigener Scout kann
mitgegeben werden. Eine FREMDE Scout-Nummer wird abgelehnt (403) und
nicht stillschweigend auf den Anleger zurueckgesetzt: Sonst glaubte der
Manager, er haette zugeteilt.

Der Knopf sitzt auf dem Dashboard, nicht in der Personenverwaltung --
dort kommt ein Manager gar nicht hinein, und hier sieht er seine Creator
ohnehin. Der Zugangscode steht einmal im Dialog und wird nie nachgeladen;
deshalb schliesst sich das Fenster NICHT von selbst.

pruef-creator-anlegen.mjs, 29 Pruefungen, alle gruen -- und die Haelfte
davon prueft, was NICHT geht: Personenverwaltung 404 fuer Manager, Scout
und Creator kommen gar nicht erst durch, fremder Scout 403 (und der
Creator wird dabei gar nicht erst angelegt), erfundene Nummer 403. Zwei
Manager mit je einem eigenen Scout, weil sich "nur die eigenen" mit nur
einem Manager gar nicht pruefen laesst.

Nebenbei: .feld-hinweis ist von bereich.css nach aufgaben.css gewandert
(zu den uebrigen Formularstilen) -- ein Formularbaustein in der
Bereichsdatei ist nur so lange richtig, wie ihn keine zweite Seite
braucht.

Gruen: creator-anlegen (29), personen-liste (33), css-klassen (15),
struktur (32), formulare (19).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 04:25:49 +02:00
DogFatherGitandClaude Opus 5 4447a653e7 Die Uhr wird ein Gegenstand -- Rot x Babyblau, und start.css wird geteilt
DIE UHR. Filipe: "viel viel viel viel viel viel viel spezieller ... eine
mischung von rot und babyblau". Was eine Uhr teuer aussehen laesst, ist
nicht die Farbe, sondern das GEHAEUSE -- bei einer echten sieht man drei
Schichten uebereinander: einen Metallrand, der das Licht von oben faengt,
eine VERTIEFTE Scheibe darin, und ein Glas darueber, das spiegelt. Genau
die drei sind jetzt gebaut.

Die beiden Farben sind nicht aus dem Farbkasten: Rot ist Spicy Media,
Babyblau ist DogFather -- dieselben zwei, die im Kopf der Anmeldekarte
nebeneinanderstehen und im Motiv den Neonrahmen bilden. Zwei Verlaeufe,
absichtlich GEGENLAEUFIG (Stundenring rot->blau, Sekundenring blau->rot):
Wo der eine warm ist, ist der andere kuehl, so bleiben sie
auseinanderzuhalten, wo sie sich ueberlagern.

Dazu: eine Skala aus sechzig Strichen, jeder fuenfte kraeftiger (aus
einem Kegelverlauf, nicht aus sechzig Elementen); ein leuchtender Kopf,
der am Ende des Sekundenbogens mitlaeuft -- die einzige Stelle, an der
sich sichtbar etwas bewegt; und die Mischung IM Text als zwei Schatten
statt als Verlaufsschrift (background-clip: text waere im Windows-
Kontrastmodus unsichtbar). Die Glocke bekommt dasselbe Gehaeuse, nur
kleiner -- zwei runde Dinge aus verschiedenem Material sehen
zusammengesucht aus.

PROFILBILDER: /api/personen lieferte nur id, name, rolle. Deshalb konnte
KEINE Oberflaeche ein Gesicht zeigen, auch wenn eines hochgeladen war --
der Fehler lag nicht in der Anzeige, sondern in der Schnittstelle.
Jetzt kommt die fertige Bildadresse mit (nicht der Dateiname: sonst
setzt jede Stelle im Browser denselben Pfad zusammen). Und DogFather ist
fuer alle sichtbar -- Name, Rolle, Bild, sonst nichts; seine Eintraege,
Chats und Zahlen haengen weiter an den eigenen Regeln der Module.

start.css GETEILT statt gekuerzt. Sie lief mit 202 KB wieder in die
200-KB-Grenze, und diesmal war Kuerzen die falsche Antwort: Dreizehn
Seiten laden diese Datei, Uhr und Begruessungskachel braucht genau EINE.
Also heim.css -- 184 KB statt 202, und zwoelf Seiten laden 14 KB
weniger. Vorher mit grep nachgesehen, dass `uhr`, `willkommen` und
`glocke-platz` in keiner anderen HTML-Datei vorkommen.

ZWEI FUNDE DER PRUEFUNGEN, beide berechtigt:
  - `.glocke--kachel` musste zurueck nach start.css. glocke.js laeuft auf
    ALLEN siebzehn Seiten und kann die Klasse ueberall setzen; die Regel
    gehoert dorthin, wo das Skript sie brauchen KANN, nicht dorthin, wo
    es sie heute zufaellig braucht.
  - Die Sekundenanzeige stand auf 10,88 px (Grenze 11,5). Gesperrte
    Ziffern in Versalhoehe wirken kleiner als die Zahl sagt -- auf
    11,84 px.

Gruen: css-klassen (15), struktur (32), sicht (48), buehne (38),
start-ansicht (136), handy (59), aufgabenbrett (44).

NOCH OFFEN aus derselben Nachricht: die Calls-Seite komplett umbauen mit
Auf-/Zuklappen ueberall, die Profilbilder auch WIRKLICH ueberall
zeichnen (die Daten sind jetzt da), die Rolle "Spicy Media" und
Manager-legt-Creator-an.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 04:18:27 +02:00
DogFatherGitandClaude Opus 5 8f8c283a1a Kacheln werden ein Raster -- dritter Anlauf, diesmal die FORM
Filipe zweimal davor: "das ist genau das gleiche" und "sorry aber ich
glaub du verstehst nicht was ich meine". Er hatte beide Male recht, und
ich weiss jetzt warum: Ich habe zweimal die OBERFLAECHE geaendert
(Mattglas, dann Leuchtschiene) und beide Male die FORM gelassen -- eine
breite Zeile, Zeichen links, Text daneben. Wer eine Zeile umlackiert,
bekommt eine lackierte Zeile.

AUS DREI BREITEN ZEILEN WIRD EIN RASTER AUS SECHS KARTEN.
Hochformat, Zeichen oben, Name unten, Schiene von links nach OBEN
gewandert (an einer hochkanten Karte wuerde ein Lichtbalken links sie
optisch halbieren). Die Zahl steht als Marke oben rechts statt in der
Namenszeile, der Pfeil unten rechts. `data-gross="ja"` behaelt das
Querformat -- so hebt sich die Kachel WIRKLICH ab, statt nur breiter zu
sein. Bei 1380 px passen jetzt sechs Karten nebeneinander statt drei.

BILDER: Kontrast 1,08 -> 1,14, Saettigung 1,10 -> 1,26, Glanz 0,34 ->
0,46, dazu ein neuer Durchgang "Tiefe" -- eine S-Kurve auf der
Helligkeit. Das ist NICHT mehr Kontrast: Kontrast dehnt alles gleich und
frisst Zeichnung in den Lichtern; die S-Kurve laesst die Mitte in Ruhe
(dort sitzt das Motiv) und arbeitet nur an den Enden. Gerechnet auf dem
Maximum der drei Kanaele, nicht je Kanal -- sonst wandert der Farbton
(ein dunkles Rot wuerde braun).

ANMELDESEITE: "Dogfather Universe" -> "SpicyMedia x DogFather" (das
Kreuz kleiner und leiser, sonst liest man drei Namen statt zwei), und
der Satz darunter nennt jetzt Manager, Scouts und Creator von Spicy
Media -- ohne DogFather.

ZAHLEN nur noch fuer DogFather. Beides zusammen, nicht nur die Kachel:
Eine Kachel ist ein Weg, keine Schranke -- wer die Adresse kennt, waere
weiterhin hineingekommen. Also auch in der Rechteliste auf ["admin"].

AUFGERAEUMT, weil pruef-struktur zu Recht rot wurde: start.css lief mit
202 KB in die 200-KB-Grenze. Der Grund war echter Ballast -- die Datei
trug DREI Generationen Kacheldesign uebereinander. Die ueberholten
Regeln sind weg (nur was der neue Entwurf nicht selbst setzt, bleibt),
der Entwurfstext dazu auf seine Lehre gekuerzt. 198,7 KB, und wichtiger:
nur noch EINE Stelle, an der eine Kachel beschrieben wird.

Gruen: buehne (38), css-klassen (15), leistung (50), start-ansicht (136),
handy (59), breiten (23), struktur (32).

NOCH OFFEN aus derselben Nachricht: die neue Rolle "Spicy Media" und die
Aenderung, dass Manager Creator anlegen duerfen. Beides greift in die
Rechte und in die CHECK-Regel der Personentabelle ein -- das kommt als
eigener Schritt mit Sicherung und eigener Pruefung, nicht nebenbei.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 03:59:16 +02:00
DogFatherGitandClaude Opus 5 1b2874299e Uhr und Glocke in die Begruessung, Teilen in die Leiste, ein echter Fehler weg
Neun Punkte aus Filipes Bildschirmfotos. Der wichtigste war kein
Aussehen, sondern ein Fehler:

UEBEREINANDERLIEGENDE TEXTE IN DER SICHERUNGSLISTE. Das Datum entstand
aus `name.split('-').slice(1).reverse().join('.')`. Bei
"woechentlich-2026-09-06.db" ging das gut; die Sicherungen vor einem
Umbau heissen aber "vorher-2026-09-02-160012438.db" -- mit Zeitstempel.
Heraus kam "160012438.02.09.2026", dreimal so lang wie die 96-px-Spalte,
und es lief ueber den Nachbartext. Ein Muster, das Bestandteile ZAEHLT
statt sie zu SUCHEN, bricht beim ersten Namen mit einem Teil mehr. Jetzt
ein Suchmuster nach vier-zwei-zwei Ziffern -- und in der CSS eine
Kuerzung, damit der NAECHSTE zu lange Text nur abgeschnitten wird. Eine
Spalte mit fester Breite ohne Kuerzung ist immer eine Zeitbombe.

DIE BEGRUESSUNGSKACHEL. Runde Digitaluhr rechts: zwei Ringe um dieselbe
Mitte -- innen die Sekunde, aussen der Stand der Stunde. Kein
setInterval(1000): Ein fester Takt laeuft mit der Zeit aus dem Tritt und
ueberspringt Sekunden; gewartet wird bis zur naechsten VOLLEN Sekunde.
Die Glocke ist aus der Kopfleiste hierhergezogen -- mit Rueckfall, denn
zwoelf andere Seiten haben diesen Platz nicht. Nebenbei ist die Leiste
damit um ein Element leichter; sie ist am 06.09. schon einmal an einem
sechsten zerbrochen.

TEILEN-KNOPF in der Leiste, nur fuer Scout, Manager und DogFather.
Geteilt wird der EINGANG, nicht die aktuelle Seite: Ein Link auf
bereich.html?b=schutz schickt jemanden auf eine Seite, die er nicht
sehen darf. Wo es navigator.share gibt, wird es benutzt; sonst
Zwischenablage; wo beides fehlt, erscheint der Knopf gar nicht -- ein
dritter Ausgang statt einer Schaltflaeche, die nichts tut.

WEITER: Kachelreihenfolge Steckbrief -> Profile -> Zahlen. Die
Tagesliste laesst sich zuklappen und zeigt dann SIEBEN Tage (die Woche,
nicht die vier von ueberall sonst). Dialoge, Automationen-Karten,
Sicherungsblock, KI-Kasten und Call-Karten bekommen dasselbe Material
wie die Kacheln -- Leuchtschiene, Materialstaerke, Glanz.

Profilbilder brauchten nichts: Der Weg gibt es fuer jede Rolle bereits
(steckbrief.html fuer die Betreuung, derselbe Block auf profil.html fuer
Creator, Hochladen schreibt immer auf req.person.id).

ZWEI EIGENE FEHLER, beide gemessen statt vermutet:
  - Die Koernung lag in vier neuen Bloecken auf DERSELBEN Ebene wie die
    Spiegelung und erbte deren 50 % Deckkraft. Gemessen rgb(56,44,58)
    statt rgb(20,26,38) -- die Karten sahen durchsichtig aus, obwohl sie
    zu 95 % decken. Derselbe Fehler wie heute Nachmittag an der
    Anmeldekarte. Koernung gehoert auf eine eigene Ebene mit overlay.
  - `.glocke-platz:empty { display: none }` liess die Kachel wachsen,
    sobald die Glocke geladen war. Layout-Sprung von start.html: 0,708.
    Platz wird jetzt reserviert -> 0,473.

pruef-struktur: `-breit` gehoert in die Stufen-Ausnahme. Schaerfe kostet
Bytes (hohe Frequenzen lassen sich nicht wegrechnen), die mittlere Stufe
wuchs auf 230-300 KB. Die Regel bleibt inhaltlich: gross nur, WENN eine
kleinere Stufe daneben steht. Die Verkettung der beiden replace() waere
ein stiller Fehler gewesen -- fuer uhd haette sie nach `-schmal` gesucht.

Gruen: kopf-messen (3), breiten (23), glocke (26), css-klassen (15),
struktur (32), buehne (38), start-ansicht (136), handy (59),
formulare (19), barrierefrei (18), lesbarkeit (14), tempo (8).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 03:40:49 +02:00
DogFatherGitandClaude Opus 5 8efdb39ce9 Kein Weichzeichner mehr -- die Bilder werden scharf, die Kacheln Lampen
Filipe: "gerade sehen die sogar leicht verschwommen aus ... die sollen
nicht durch die kacheln gehen ... ich wollte eine krasse aenderung."

Drei Ursachen, alle drei von mir eingebaut, alle drei gemessen:

1. ICH HABE 1647-PIXEL-BILDER ALS "uhd" MIT 2400 PIXELN AUSGELIEFERT.
   Am Morgen stand in derselben Datei, sein Schirm sei 2550 px breit und
   das groesste Bild 1600 -- meine "Loesung" war, die Ausgabe
   hochzurechnen. Es gab nie mehr Bildpunkte. Dazu fehlte der Schritt,
   den der Kommentar daneben selbst verlangt ("Verkleinern MITTELT
   Bildpunkte ... deshalb schaerft man danach nach"): Es wurde nie
   nachgeschaerft. Jetzt Unscharfmaske auf der ENDgroesse
   (Ausgabeschaerfung) und ein Glanz-Durchgang: die hellsten Stellen
   weich gezeichnet und additiv dazu -- Licht, das ueber seine Kante
   strahlt. Guete 0,80 -> 0,88, am Gate 0,93.

2. backdrop-filter: blur() UNTER JEDER FLAECHE. 16 px unter siebzehn
   Kacheln, 13 px unter sieben weiteren Flaechen, 26 px unter der
   Anmeldekarte. Ein Weichzeichner mittelt nicht nur, was hinter dem
   Element liegt -- er zieht seinen Radius weit darueber hinaus. Hinter
   der Anmeldetafel ist Schwarz, direkt daneben aber die hellste Stelle
   des Motivs: Die Karte wurde deshalb GRAU, gemessen rgb(46,57,64)
   statt rgb(15,22,34). Je schoener der Rahmen, desto grauer die Karte.
   Alle Weichzeichner raus, --flaeche 0,78 -> 0,89: Durchsicht bleibt,
   aber scharf.

3. DIE TAFELMESSUNG WAR FALSCH, UND ICH HABE SIE VON HAND "KORRIGIERT".
   Sie lief vom Inneren nach aussen und hielt beim ersten farbigen Punkt
   an -- links glueht der Rahmen breiter als rechts, also hielt sie dort
   frueher an (58,59 statt 55,37). Statt den Messfehler zu beheben, habe
   ich in der CSS "um 1,1 % nach links" geschaetzt. Ergebnis: 32 px
   schwarze Tafel blieben links offen. Jetzt wird die einzige
   Eigenschaft gesucht, die nur der Rahmen hat -- kraeftig UND farbig --,
   mit Gegenprobe auf der linken Bildhaelfte (dort muessen es 0 sein).

KACHELN: keine Politur mehr, ein anderes Ding. Jede steckt in einer
LEUCHTSCHIENE ihrer Kategoriefarbe, die Licht in die Platte wirft --
links scharfe Kante, rechts rund. Steiler Abfall (52 % statt 68 %):
beleuchtet, nicht eingefaerbt. Der Glanz wandert beim Ueberfahren
einmal durch. Dieselbe Sprache auf der Anmeldeseite: die vier Rollen
sind Tasten in Schienen (Gold/Bernstein/Gruen/Blau).

Zwei eigene Fehler nebenbei, beide durch das Unveraenderlichkeits-
Zeichen gefunden -- eine Zahl, die sich nach einer Aenderung KEIN Stueck
bewegt, sagt "falsche Stelle", nicht "zu wenig":
  - Dreimal exakt 4,16:1 an "Ueberfaellig". Der Pruefpunkt lag nicht auf
    dem Knopf, sondern in der Luecke daneben auf dem Bild. Ursache war
    das hellere Hochkant-Bild, nicht das Bedienelement -> mehr Schleier
    und weniger Glanz NUR fuer die Handy-Fassung. 4,16 -> 5,10.
  - Die Koernung der Anmeldekarte lag mit 90 % Deckkraft ohne Mischmodus
    ueber allem und hob jeden Bildpunkt um 30 Stufen. Jetzt 4 % mit
    overlay, auf eigener Ebene.
  - --flaeche anzuheben machte die WICHTIGE Anleitung duenner als eine
    gewoehnliche (88 gegen 89) -- feste Zahl neben beweglicher Groesse.
    Gefunden von pruef-lesbarkeit, jetzt an --flaeche gebunden.

Gruen: buehne (38), start-ansicht (136), handy (59), breiten (23),
lesbarkeit (14), css-klassen (15), barrierefrei (18), tempo (8).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 03:10:13 +02:00
DogFatherGitandClaude Opus 5 947c9d8a26 Agentur: Eintraege gehoeren allen -- und Events bekommen ein eigenes Formular
Gemessen am 07.09.2026, bevor irgendetwas geaendert wurde: Von drei
Agentur-Eintraegen sah eine Creatorin genau EINEN -- den, der ihr
zugeordnet war. Ein Event fuer alle traf weder `creator_id = ich` noch
`erstellt_von = ich`; die Agentur-Seite war fuer jeden Creator leer.
Nicht kaputt, nicht fehlerhaft: leer, so wie eine Seite aussieht, auf
der noch nichts steht.

Die Pruefung dazu war gruen. Sie legte ihre Testeintraege mit
`creator_id: idLuna` an und pruefte damit einen Fall, den es im Alltag
nicht gibt.

- Bereichseinstellung `fuerAlle` + `ohneCreatorBezug`, daraus abgeleitet
  sichtbarEintrag(). BEWUSST neben sichtbar() statt darin: an derselben
  Funktion haengen Aufgaben, Dateien, Termine und Calls -- wer dort
  "1=1" einschleust, gibt nebenbei fremde Akten frei. Eine Gegenprobe
  mit einer zweiten Creatorin haelt das fest.
- Die Zuordnung bietet nur noch "Agentur" an. Der Server verwirft eine
  Zuordnung ausserdem selbst -- inklusive des frei getippten Namens, und
  zwar NACH externPruefen: davor haette der Name den Riegel wieder
  aufgemacht.
- "Event & Kampagne" heisst jetzt "Agentur-Events" und hat ein eigenes
  Formular: Von/Bis, Titel, Beschreibung, Aufgaben (Punkte und Preise),
  Regeln. Die Karte zeigt den Zustand als WORT (laeuft bis / startet /
  vorbei seit), nicht nur als Farbe.
- Drei neue Spalten -- und sie stehen auch im Tabellenneubau vom 06.09.
  Der laeuft NACH dem Spaltennachtrag und haette sie samt Inhalt
  weggeworfen, ohne Fehler und mit stimmender Zeilenzahl.
- Creator sehen weiterhin alles und tragen weiterhin nichts ein (403).
  Die Unterzeile sagt jetzt "alles, was hier steht" statt "alles, was zu
  dir gehoert" -- eine vollstaendige Liste soll sich nicht wie ein
  Ausschnitt lesen.

pruef-agentur: 61 Pruefungen (vorher 40), alle gruen. Zwei eigene
Fehler nebenbei gefunden und behoben: eine Beschriftung mit 11,2 px
(Grenze 11,5) und ein Testdatum aus UTC statt Ortszeit.
Zusaetzlich gruen: css-klassen, struktur, formulare,
barrierefrei-workspace, handy.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 02:31:47 +02:00
DogFatherGitandClaude Opus 5 3914adf46c Material statt Farbe -- Kacheln werden Gegenstaende, die Karte ein Bildschirm
Filipe: "ich will dass alle kacheln viel geiler und spezieller aussehen,
viel realistischer" und "so dass der komplette von der kachel vom
hintergrund bild komplett bedeckt ist. ueberrasch mich."

DIE BILDER: BEARBEITET STATT ABGEDUNKELT.
Bis hierher tat der Bildbauer zwei Dinge -- kleiner rechnen und einen
schwarzen Schleier darueberlegen. Abdunkeln macht ein Bild aber nicht
ruhiger, sondern TOT: Es zieht jede Farbe zur Mitte, und uebrig bleibt
ein grauer Schleier mit einer Ahnung von Motiv.

Drei Dinge, die ein Fotograf zuerst anfasst, und keines davon ist
"dunkler":
  * KONTRAST -- Verkleinern MITTELT Bildpunkte; deshalb wirkt jedes
    verkleinerte Bild flauer als das Original, und deshalb schaerft man
    danach nach.
  * SAETTIGUNG -- holt das Rot der Chili und das Gruen der Blaetter
    zurueck, die der Schleier herausgewaschen hatte.
  * VIGNETTE -- der eigentliche Unterschied zwischen "Screenshot" und
    "Aufnahme". Ein Objektiv verliert zu den Ecken hin Licht; das Auge
    liest das als Tiefe. Sie ersetzt ausserdem einen Teil des flachen
    Schleiers: Dunkel wird, wo ohnehin nichts steht.
Der flache Schleier sinkt dadurch von 0,42 auf 0,26 -- heller UND
lesbar, weil die Vignette genau dort arbeitet, wo die Kacheln liegen.

DIE KACHELN: VIER DINGE, DIE EIN DING VON EINEM RECHTECK TRENNEN.
Sie hatten Farbverlauf, Lichtkante und ein Licht, das dem Zeiger folgt --
und blieben Rechtecke mit Farbe darin. Es fehlten:
  1. GEWICHT. Ein Ding, das auf etwas liegt, wirft einen Schatten, auch
     wenn es niemand anfasst. Den gab es nur beim Ueberfahren.
  2. MATERIALSTAERKE. Ein Blech hat oben eine Licht- UND unten eine
     Schattenkante. Nur die obere ergibt einen aufgeklebten Strich.
  3. KOERNUNG. Perfekt glatte Verlaeufe kommen in der Natur nicht vor,
     und das Auge merkt das, ohne es benennen zu koennen. Drei Prozent
     Rauschen genuegen -- aus einem SVG-Filter als Adresse, also ohne
     Datei und ohne zusaetzliche Anfrage.
  4. DURCHSICHT. Hinter den Kacheln liegt jetzt ein aufwendiges Bild.
     Eine deckende Flaeche verdeckt es, eine mattierte nimmt seine Farbe
     auf -- erst dadurch gehoeren beide zusammen.
Beim Ueberfahren wird die Kachel nicht heller, sondern kommt NAEHER:
Sie steigt, ihr Schatten wird groesser und weicher (der Abstand zum
Untergrund waechst). So verhaelt sich ein angehobener Gegenstand.

DIE ANMELDEKARTE: EIN GERAET STATT EINES BILDES AN DER WAND.
Sie sass mit Abstand in der gemalten Tafel -- zwei Rahmen ineinander,
dazwischen schwarze Leere. Jetzt geht sie bis unter das Gluehen des
Neonrahmens: Der Rahmen ist das Gehaeuse, die Karte der eingeschaltete
Bildschirm. Sie leuchtet von den Kanten herein in den Farben des Rahmens
(rot oben links, blau unten rechts), traegt eine Glasscheibe als sehr
schwache Spiegelung und dieselbe Koernung. Die Rollen sind Tasten
geworden: Materialstaerke oben hell, unten dunkel, beim Druecken sinken
sie ein, und die gewaehlte ist beleuchtet statt eingefaerbt.
Alle Angaben bleiben -- vier Rollen mit Beschreibung, Codefeld, Auge,
Rollenhinweis, Fusszeile. "Hochwertiger" heisst nicht "weniger drin".

EIN ECHTER FUND DABEI: --text-still lag ploetzlich bei genau 4,50:1
statt der noetigen 4,5. Der Ton war am 01.09. gegen die DAMALIGE,
dunklere Buehne gemessen. Wer den Hintergrund heller macht, muss die
leiseste Schrift nachziehen -- sonst haette man den Aufwand auch lassen
koennen. Erkannt daran, dass die Zahl sich bei zwei Schleier-Aenderungen
NICHT bewegte: Die Pruefung rechnet mit der CSS-Farbe.

Gemessen statt angesehen: Kontrast an echten Bildpunkten (pruef-buehne),
neun Bildschirmbreiten, drei Handygroessen, Ladezeit und Datenvolumen,
Klassen, Struktur, Startseite -- alles gruen. Die Seiten ueber dem
CLS-Zielwert sind nebenbei von sieben auf vier gesunken.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 02:06:08 +02:00
DogFatherGitandClaude Opus 5 8107b97379 Die Kopfleiste wuchs beim Laden um vier Pixel -- und schob die Seite
Beim Messen des Datenvolumens nach dem Bildumbau fiel etwas anderes
auf: Der Layout-Sprung auf der Startseite lag bei 0,99. Die Bilder waren
es nicht -- ein Hintergrundbild liegt `position: fixed` und bewegt gar
nichts.

Die Meldung sagte es selbst, man musste nur hinsehen: ALLES sprang um
denselben Betrag (295->301, 144->150, 685->692, 605->611). Wenn die
ganze Seite gleichmaessig nach unten rutscht, waechst etwas ueber ihr.

Gemessen: Leiste vor dem Skript 67 px, danach 71. Der Sicht-Umschalter
kommt erst, wenn die Personenliste geladen ist, und ist mit 42 px das
hoechste Teil in der Reihe. Vier Pixel -- man sieht sie kaum und merkt
sie doch: Wer beim Laden schon zielt, klickt daneben.

Die Loesung ist keine reservierte Hoehe auf Verdacht, sondern die Hoehe,
die die Zeile ohnehin haben muss: `min-height: 44px`. Das ist das
Mindestmass fuer ein Fingerziel (WCAG 2.5.8) und gilt hier sowieso fuer
jedes Teil. Damit ist die Zeile immer so hoch wie ihr groesstes
zulaessiges Element -- unabhaengig davon, ob dieses gerade schon da ist
oder erst kommt.

Ergebnis: 0,99 auf 0,60, und die Zahl der Seiten ueber dem Zielwert von
zwoelf auf sechs. Datenvolumen unveraendert in Ordnung trotz der
groesseren Bilder -- der Browser holt je Seite nur eine Stufe.

Und der offene Punkt von vorhin ist geklaert: pruef-browser laeuft
gruen durch, alle vier Maschinen (Chromium, Firefox, WebKit, WebKit auf
dem iPhone), 64 Seitenaufrufe, 15898 Elemente. Der eine rote Punkt im
Gesamtlauf war eine Zeitueberschreitung unter Dauerlast -- erkennbar
daran, dass ZWEI Pruefungen gar nicht gelaufen waren und die Datei die
doppelte Zeit brauchte. Eine gesunkene Anzahl ist ein eigener Befund.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 01:46:29 +02:00
DogFatherGitandClaude Opus 5 accf01b5d6 Sieben Motive statt einem -- und Bilder, die nicht mehr hochgerechnet werden
Filipe: "die grafik soll viieeeel besser aussehen bitte. die hintergrund
bilder sollen viel hochwertiger und spezieller aussehen."

DIE URSACHE WAR NICHT DIE BILDGUETE, SONDERN DIE GROESSE.
Sein Bildschirm ist 2550 px breit, das groesste ausgelieferte Bild war
1600 -- der Browser rechnet es also um das 1,6-fache hoch. Jede Kante
wird dabei weich, gleichmaessig ueber das ganze Bild. Genau das sieht
man als "billig", ohne benennen zu koennen, warum. Eine hoehere
WebP-Guete haette daran nichts geaendert: Man kann keine Bildpunkte
zurueckholen, die nie ausgeliefert wurden.

  Anmeldeseite: jetzt 960 / 1280 / 1600 / 1920 / 2560, Guete 0,88.
  Buehnen:      jetzt 2400 (uhd) / 1600 (breit) / 900 (schmal).
Ueber srcset bzw. eine Fenstergroesse laedt trotzdem jeder nur die
Stufe, die er braucht -- ein Handy weiterhin 32 KB.

UND NEUN BUEHNEN ZEIGTEN DASSELBE BILD.
In start.css standen neun Regeln, eine je Szene -- alle zeigten auf
dieselbe Datei. Die Zuordnung war seit dem 01.09. richtig gedacht und
seit dem 03.09. wirkungslos. Aufgefallen ist es niemandem, weil jede
Seite fuer sich stimmig aussah; man merkt es erst, wenn man zwei
nebeneinander haelt. Jetzt hat jede Gruppe ihr eigenes Motiv, und die
Zuordnung folgt dem, was auf der Seite passiert:

  studio      Startseite            Chili-Wasserfall, "More Than Media"
  showbuehne  Dashboard, Reports    Spiegelkabinett -- viele auf einmal
  portal      LIVE, Content         Splash mit IDEAS / BRAND / CONTENT
  garage      Aufgaben, Technik     Kohle und Glut, Werkstatt
  arena       Dateien, Wissen       Medaillon-Sammlung, ein Archiv
  halle       Profile, Schutz       dieselbe Sammlung -- Personen, nicht
                                    Betrieb
  wald        Start-Check, Scout    roter Ahorn, etwas das waechst
  skyline     Kalender              Podest unter dem Mond
  lounge      Calls, Chat           dieselbe Nachtbuehne, ruhig

Sieben Motive auf neun Plaetze; die zwei Paare sind inhaltlich
benachbart und liegen nie nebeneinander auf einer Seite.

ABDUNKLUNG 0,42 -- gemessen, nicht uebernommen. Beim Tresorbild hatte
ich 0,62 aus den alten Szenen uebernommen, und uebrig blieb ein Schemen.
pruef-buehne rechnet den Kontrast an echten Bildpunkten nach: 4,69 bis
7,14 gegen die noetigen 4,5, an bis zu 28 Stellen je Seite.

Die Groessenpruefung in pruef-struktur bekommt eine begruendete Ausnahme
fuer GESTUFTE Bilder: Bei einer Datei, von der der Browser immer nur
eine von drei Stufen holt, misst eine feste 200-KB-Grenze das Falsche.
Sie gilt unveraendert fuer alles andere -- und die Ausnahme prueft
zusaetzlich, dass die kleineren Stufen wirklich existieren, damit sie
niemand als Schlupfloch benutzt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 01:38:02 +02:00
DogFatherGitandClaude Opus 5 44b3290bc5 Die Kopfleiste: nicht die dritte Zahl, sondern gar keine
BEFUND. Der Gesamtlauf war nach dem Bildumbau bei SIEBZEHN Dateien rot,
pruef-handy allein mit 37 Fehlern. Alle mit derselben Meldung:
"ueber=126px". Eine Ursache, siebzehnfach gezaehlt.

Sie war meine. Um den Layout-Sprung bei 1280 px zu beheben, hatte ich
den Umbruch der aeusseren Leiste wieder auf "nur unter 380 px" gestellt
-- und damit bei 390 und 412 px genau das Loch aufgerissen, das ich
Stunden vorher geschlossen hatte. Dieselbe feste Schwelle von 380, ueber
die zwei Commits vorher eine Regel in CLAUDE.md gewandert ist.

DREI ANLAEUFE, und die ersten beiden waren Symptomkur:

  1. `flex: 0 0 auto` fuer die Bedienelemente -> 126 px Ueberstand bei
     412 px (sie koennen weder schrumpfen noch umbrechen).
  2. Hoher Schrumpf-Faktor am Schriftzug (220 gegen 1) -> besser, aber
     von 19 fehlenden Pixeln nahmen die Knoepfe 0,19, und die runden auf
     1 auf. Ein Pixel, und die Gruppe brach um. Ich habe daraufhin die
     Abstaende verkleinert; danach war es wieder genau ein Pixel. Wer an
     einem Rundungsfehler schraubt, hat die falsche Stellschraube.
  3. RICHTIG: dem Schriftzug gar keine Wunschbreite geben.
     `flex: 1 1 0` heisst "ich beanspruche nichts und nehme, was uebrig
     bleibt". Damit entsteht ueberhaupt kein Fehlbetrag, der verteilt
     werden muesste. Kein Schrumpf-Faktor, keine Schwelle, nichts zu
     runden. Dazu `max-width: max-content`, sonst zieht `flex-grow` die
     Marken-Pille ueber die ganze Leiste (990 statt 375 px) -- wovor der
     Kommentar zwei Zeilen darueber ausdruecklich warnt und was ich beim
     Umbau uebergangen habe.

Gemessen ueber neun Breiten: 1920 bis 768 einzeilig (71 px), 412 und 390
zweizeilig (127), 320 dreizeilig (167). Ueberstand ueberall null. Und der
Layout-Sprung von 0,94 ist damit auch weg -- er kam aus derselben Ecke.

Dabei drei weitere echte Fehler gefunden, alle in Neuem von heute:
  * Der Namenslink in der Fruehwarnung war 22x25 px -- unter dem
    Mindestmass von 24x24 (WCAG 2.5.8). Bei kurzen Namen trifft man
    daneben. Jetzt ein echtes Fingerziel mit negativem Aussenabstand, der
    die Polsterung optisch wieder aufhebt.
  * Die Warnkarten standen bei 320 px 9 px aus dem Bild:
    `minmax(320px, 1fr)` begrenzt die Spalte, nicht ihren Inhalt. Erst
    `min(320px, 100%)` PLUS `min-width: 0` PLUS umbrechender Kopf loesen
    es -- einzeln keines davon. Die Schreibweise stand zwei Abschnitte
    weiter oben laengst richtig da.
  * Der Zurueck-Link im Schriftzug ragte aus seinem Kasten:
    `overflow: hidden` schneidet nur die ANZEIGE ab, der Link behaelt
    seine Layoutbreite. Bei 768 px lag sein Mittelpunkt unter den
    Bedienelementen -- ein Klick dorthin traf das Falsche, auf 17 von 18
    Seiten, und zu sehen war davon nichts.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 00:56:18 +02:00
DogFatherGitandClaude Opus 5 aa1d05a5c0 Neues Anmeldebild -- die Karte sitzt IN der gemalten Tafel
Filipe: "integriere die zugangskachel links nach rechts und passe sie
perfekt an damit sie in dem neuen bild rechts perfekt und die
vorgemachte kachel passt."

Das neue Motiv (Spicy Media x DogFather) hat rechts eine leere Tafel mit
Neonrahmen. Die Anmeldekarte sitzt jetzt DARIN -- und zwar wirklich
darin, nicht ungefaehr in der Gegend.

DAS PROBLEM, DAS MAN LEICHT UEBERSIEHT: Das Bild liegt mit
`object-fit: cover` unter der Seite; je nach Fensterform schneidet der
Browser oben/unten oder links/rechts etwas ab. Ein `left: 58%` bezieht
sich aber auf das FENSTER. Auf genau einer Bildschirmgroesse saehe es
richtig aus und ueberall sonst falsch. Deshalb rechnet `.tafel-anker`
dieselbe Cover-Formel noch einmal nach und ist damit deckungsgleich mit
dem Bild -- Prozentwerte darin sind Prozent DES BILDES.

Die vier Zahlen sind GEMESSEN (tools/gate-bauen.mjs), nicht geschaetzt.
Zwei Anlaeufe standen daneben und haben sich selbst verraten:
  1. "Suche den leuchtenden Rahmen" fand den Mond, die roten Blueten und
     jede Spiegelung -- Ergebnis: die ganze rechte Bildhaelfte. Eine
     Messung, die das Offensichtliche zurueckgibt, hat nichts gemessen.
  2. "Suche die groesste dunkle Flaeche" fand die Breite richtig, aber
     94 % Hoehe: Ueber und unter der Tafel ist die Szene genauso
     schwarz.
Richtig ist der dritte Weg: vom Mittelpunkt der Tafel nach aussen
laufen, bis es hell ODER farbig wird -- auf diesem Weg liegt nichts
anderes, denn die Tafel ist leer. Danach eine Plausibilitaetspruefung,
die abbricht statt vier geratene Zahlen auszugeben.

DREI FEHLER IM EIGENEN ENTWURF, alle gemessen statt angesehen:
  * Die Karte hing 300 px unter dem Bildschirmrand. Auf `.tafel` liegt
    die Einblend-Animation, deren Endbild `transform: none` ist -- mein
    `translateY(-50%)` war damit wirkungslos. Zwei Wege, dasselbe
    Element zu verschieben, vertragen sich nicht.
  * Der Anker lag 3 % neben dem Bild: Das Bild traegt `scale(1.03)` als
    Reserve fuer die Parallaxe. Jetzt tragen beide dieselbe Verwandlung,
    und die Parallaxe laeuft ueber zwei CSS-Groessen am <body> -- so
    wandert die Karte mit, statt dass der Rahmen unter ihr wegrutscht.
  * Der Hochkant-Ausschnitt zeigte die LEERE Tafel: ein schwarzes
    Rechteck mit ein paar Saeulen. Auf dem Handy liegt die Karte ohnehin
    davor; dort gehoert das Logo hin. Jetzt faellt der Schriftzug
    SPICY MEDIA in den sichtbaren Streifen -- nachgerechnet, nicht
    probiert.

STARTSEITE: das Tresorbild als neuer Hintergrund. Die uebernommene
Abdunklung von 0,62 war zu viel (der Tresor ist von Haus aus dunkel) --
uebrig blieb ein Schemen. Und der "Leseweg" verdunkelte ausgerechnet die
MITTE: Bei den alten Szenen stand dort nichts, beim Tresor steht dort
die Tuer. Beides korrigiert und mit pruef-buehne an echten Bildpunkten
nachgemessen.

DABEI GEFUNDEN, ohne Zusammenhang mit dem Bild: Der abgeschaltete
"Ueberfaellig"-Filter kam auf 4,05:1 statt 4,5:1. Die Deckkraft zu
erhoehen half nichts -- die Pruefung rechnet mit der CSS-FARBE und dem
gemessenen Bildpunkt dahinter, und ein `opacity` am Elternknopf aendert
die Farbe nicht. Die Zahl blieb dreimal exakt gleich; genau das war der
Hinweis. Jetzt ein eigener, hellerer Ton. Der Kommentar daneben sagte
ohnehin, was gewollt ist: Man SOLL sehen, dass es null sind.

Und die vier Regeln von heute stehen in CLAUDE.md.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 23:44:10 +02:00
DogFatherGitandClaude Opus 5 70039714c5 Die Sicherung war zwei Loecher gross -- gefunden, weil sie zum ersten Mal wirklich zurueckgespielt wurde
"Wir haben eine Sicherung" war bis heute ein Satz, kein Nachweis. Die
taegliche Kopie ausserhalb des Servers prueft `PRAGMA integrity_check`
und meldet seit dem 31.08. jeden Tag "in Ordnung". Das heisst: die Datei
ist nicht zerschossen. Es heisst NICHT, dass man damit weiterarbeiten
kann.

tools/wiederherstellung-proben.mjs geht den Weg jetzt wirklich: juengste
Sicherung in ein Wegwerf-Verzeichnis kopieren, die echte Anwendung
dagegen starten (damit laufen alle Schemawanderungen durch), vorher und
nachher zaehlen, anmelden, jede Seite aufrufen. Drei Ausgaenge, nicht
zwei -- "konnte nicht nachsehen" ist weder Erfolg noch Fehler.

BEIM ERSTEN LAUF BLIEB GENAU EINE VON ACHTZEHN SEITEN ROT: der
Steckbrief, mit einem 404 fuer ein Bild. Daran hingen zwei echte Luecken:

  1. PROFILBILDER WAREN NIE GESICHERT. sicherung-holen.sh holte
     dateien/ und wissen/ -- die Bilder liegen aber in einem dritten
     Ordner (profilbilder/, siehe workspace-steckbrief.js). Nach einem
     Plattenausfall waeren alle Profilbilder weg gewesen, waehrend die
     taegliche Meldung weiter "in Ordnung" sagte. Sie hat ja nie
     behauptet, vollstaendig zu sein.

  2. DIE RUECKMELDUNG AN DEN SERVER LIEF INS LEERE. Das Skript meldete
     sich bei dogfather-universe.com/workspace/... -- seit dem Umzug am
     06.09. antwortet diese Adresse mit 410 "Gone". Der Server erfuhr
     also seit dem Umzug nicht mehr, dass die Kopie laeuft; auf der
     Automationen-Seite waere sie mit jedem Tag aelter erschienen,
     obwohl sie taeglich lief. Ein 410 wird jetzt eigens gemeldet -- es
     heisst etwas anderes als "Netz kaputt", naemlich "die Adresse
     stimmt nicht mehr".

Beides behoben. Und damit es kein drittes Mal passiert, vergleicht die
Probe die Ordnerliste des Sicherungsskripts gegen die Ordner, die der
Server im Quelltext wirklich benutzt (`join(DATEN_ORDNER, "...")`). Wer
morgen einen vierten Datenordner anlegt und ihn nicht sichert, bekommt
hier einen Fehler statt eines stillen Lochs -- mit Gegenprobe, dass der
Vergleich einen fehlenden Ordner auch wirklich erkennt.

Ergebnis nach den Reparaturen: 18 Pruefungen, alle gruen,
"DIE SICHERUNG LAESST SICH ZURUECKSPIELEN." Der Lauf traegt sich mit
Datum in die Sicherungsablage ein -- sonst weiss beim naechsten Mal
niemand, wann zuletzt wirklich zurueckgespielt wurde.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 23:20:10 +02:00
DogFatherGitandClaude Opus 5 233cd76d78 Der Handy-Durchgang: drei echte Bedienfehler, alle vom selben Ursprung
Filipe: "ich will dass du die komplette seite auf dem handy abcheckst.
ich will dass alles perfekt aussieht und bedienbar ist."

DER SCHWERSTE FUND: Auf ZWOELF Seiten war der "Meine Sicht"-Umschalter
nicht bedienbar -- wer darauf tippte, landete im Chat.

Bei 412 px (der haeufigsten Android-Breite ueberhaupt) brach die
Kopfleiste nicht um, der Umschalter wurde auf 50 px zusammengedrueckt,
sein Knopf behielt aber seine 104 px Mindestbreite und lag damit quer
ueber dem Chat-Knopf. Sichtbar war davon nichts.

Und die Ursache steht seit dem 05.09. woertlich im Kommentar daneben:
"Die Rechnung ging genau auf, solange rechts VIER Dinge standen. Mit der
Glocke sind es FUENF, und bei 320 px passte es nicht mehr." Am 06.09.
habe ich den Chat-Knopf dazugesetzt -- SECHS -- und die Schwelle bei 380
gelassen. Derselbe Fehler, eine Position weiter.

Die Lehre ist nicht "380 auf 430 erhoehen"; das waere er ein drittes
Mal, nur mit einer anderen Zahl. Eine feste Schwelle ist eine Rechnung,
die jemand einmal aufgestellt hat und die beim naechsten Knopf still
falsch wird. `flex-wrap: wrap` OHNE Schwelle rechnet nicht, sondern
misst -- es bricht genau dann um, wenn der Platz nicht reicht.

DASSELBE NOCH EINMAL, am anderen Ende: Bei 1280 px brauchte die Leiste
1235 px (Marke 375 + Bedienelemente 844 + Abstand 16) und hatte 1200.
Auch hier war der Chat-Knopf der Tropfen. Sie brach um, sobald das
Skript die Bedienelemente eingehaengt hatte, und schob die ganze Seite
52 px nach unten -- CLS 0,94. Jetzt gibt die MARKE nach (sie darf
gekuerzt werden, ein Knopf nicht), und umgebrochen wird nur noch auf
sehr schmalen Geraeten.

WEITERE ECHTE FUNDE:
  * /workspace/api/chat/ungelesen wurde auf JEDER Seite ZWEIMAL geholt:
    einmal beim Laden, einmal Millisekunden spaeter beim Aufgehen des
    Ereignisstroms. Der 'open'-Zuhoerer war fuer Wiederverbindungen
    gedacht und feuerte auch beim ersten Mal.
  * Drei Kacheln teilten sich einen Farbton mit einer anderen (18 Farben
    auf 21 Kacheln). Filipe wollte ausdruecklich, dass jede ihre eigene
    hat. Nicht eine Farbe dazuerfunden -- der ganze Farbkreis ist mit
    tools/kachel-farben.mjs neu in 21 geteilt; kleinster Abstand zweier
    Nachbarn jetzt 120 Grad (vorher 106). Dabei fiel eine feste 16 in
    der Mischschleife auf: Bei 18 Kacheln wurden die letzten beiden nie
    mitgemischt.
  * Der Agentur-Untertitel wurde auf dem Handy abgeschnitten.
  * Ein zugeklappter <details>-Kasten (Kalender-Abo) verdeckte einen
    Filter-Chip: Chromium versteckt dessen Inhalt ueber
    `content-visibility`, nicht ueber `display` -- die Kaesten behalten
    eine Groesse. Dieselbe Falle wie bei den Zeitbloecken, dieselbe
    Loesung.

UND DREI PRUEFUNGEN, DIE SELBST FALSCH LAGEN:
  * "achtzehn Kacheln" stand als feste Zahl im Test. Eine Pruefung, die
    bei jeder neuen Kachel rot wird, erzieht dazu, ihr Rotwerden zu
    ignorieren. Sie zaehlt jetzt aus bereiche.js.
  * "das Licht der Kalender-Kachel (tuerkis) muss mehr Blau als Rot
    haben" -- Wissen von aussen, und nach dem Neurechnen war der
    Kalender rosa. Gemessen wird jetzt gegen den Ton der Kachel selbst.
  * pruef-workspace-umzug meldete sein Ergebnis in eigenen Worten. Der
    Gesamtlauf las daraus NULL Pruefungen und schrieb bei JEDEM Lauf ein
    FEHL, obwohl alle 31 Punkte bestanden. Ein Fehlalarm, der immer
    kommt, macht den einen echten unsichtbar.

Und weil "BUTTON 'Meine Sicht' verdeckt" mich eine Stunde gekostet hat,
sagen pruef-handy und pruef-tempo jetzt DAZU, was verdeckt und was
springt -- mit Elternkette und Koordinaten. Ein Befund, den man nicht
verorten kann, ist ein halber.

NEU: der Kalender zum Abonnieren (Stufe 3.2). Persoenlicher, jederzeit
widerrufbarer Link fuer Google, Apple und Outlook. 36 Pruefungen, die
das ICS ZURUECKLESEN statt es anzusehen -- entfaltet, entschluesselt,
verglichen. Wichtigster Punkt: Der Weg ohne Anmeldung darf nichts
zeigen, was der Weg mit Anmeldung nicht zeigt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 23:15:37 +02:00
DogFatherGitandClaude Opus 5 bede91921d Der sechste Bereich, und zwei Pruefungen, die den Lauf lahmgelegt haben
DER AGENTUR-BEREICH (Stufe 4 des Plans)
Kernprinzip 04 des Konzepts lautet woertlich "Die Agentur bleibt
angebunden" -- und dafuer gab es bis heute nichts. Fuenf Bereiche
standen, der sechste fehlte vollstaendig. Damit stand nirgends, welche
Kampagne laeuft, welche Schulung ansteht und wie ein Anliegen an die
Agentur ausgegangen ist. Das lief ueber private Nachrichten: nicht
auffindbar, nicht nachvollziehbar, beim naechsten Mal von vorn.

Vier Arten, und die vierte ist der Punkt: kampagne, schulung, anliegen
und ZUSTAENDIGKEIT. Die letzte ist keine Verlegenheit, sondern das, was
das Konzept ausdruecklich fordert -- Betreuung und Umsetzung sind unsere
Seite, offizielle Wege sind Agenturseite. Solange das nur im Kopf steht,
wird es bei jedem Streitfall neu verhandelt.

Der Umbau war das eigentliche Risiko, nicht der Bereich: Ein CHECK
laesst sich in SQLite nicht aendern, die Tabelle muss neu gebaut werden
-- und dabei werden ALLE vorhandenen Eintraege umgeschrieben. Deshalb
geht pruef-agentur.mjs (31 Pruefungen) den Weg wirklich: Sie baut eine
Datenbank im ALTEN Stand nach, fuellt sie mit allen fuenf Bereichen,
laesst die echte Anwendung darueberlaufen und zaehlt danach jede Zeile
UND jedes Feld nach -- auch die seltenen Spalten (hook, creator_extern),
die man beim Abschreiben des Bauplans vergisst. Wichtigste Gegenprobe:
Der CHECK muss danach noch BEISSEN. Eine Umstellung, die nebenbei die
Schranke entfernt, sieht aus wie ein Erfolg und ist der schlimmere
Ausgang.

Nebenbei drei abgeschriebene Bereichslisten beseitigt (Wochenbericht,
Suche, Report-Ziele). Im Wochenbericht waere der neue Bereich sonst
schlicht nicht vorgekommen -- ohne Fehler, ohne Luecke, einfach nicht da.

ZWEI PRUEFUNGEN, DIE DEN GESAMTLAUF ZUM STILLSTAND BRACHTEN

pruef-call-kategorien.mjs hing DREI STUNDEN. Ursache war ein Zeitzuender
in ihr selbst: Sie legte Calls auf "heute 18:00" und erwartete zwei
davon unter "Heute". Ab 18 Uhr sind die vorbei, der Server schiebt sie
nach "Protokoll fehlt", und die Oberflaeche unterteilt erst ab FUENF
anstehenden. Uebrig blieben vier, die Bloecke verschwanden, und die
Pruefung wartete auf einen Klick auf einen Block, den es nicht gab.
Genau die Sorte Fehler, vor der die Projektnotiz vom 06.09. warnt: Sie
war um 17:59 gruen und um 18:01 rot, ohne dass sich am Code etwas
geaendert hatte. Jetzt liegen alle Zeiten RELATIV zu jetzt, und die
Erwartung wird mit derselben Vorschrift gerechnet, die calls.js benutzt
-- statt Zahlen, die nur zu einer bestimmten Tageszeit stimmen.

Und der Grund, warum daraus ein Stillstand statt eines Fehlers wurde:
index.js setzt bewusst zwei Auffangnetze (uncaughtException,
unhandledRejection). Fuer den Betrieb richtig -- ein Fehler darf die
Website nicht offline nehmen. Fuer eine Pruefung fatal: Sie importiert
index.js in denselben Prozess und erbt die Netze. Ihr eigener Absturz
wird dann nur protokolliert, und der Express-Server haelt den Prozess
danach ewig am Leben. Kein Fehler, kein Ergebnis, kein Ende.

Deshalb zwei Abhilfen, die zusammengehoeren:
  * server/helfer-notbremse.mjs -- haengt eigene Zuhoerer DANEBEN
    (process.on ergaenzt, es ersetzt nicht) und beendet den Lauf mit
    Fehler; dazu ein Wecker. In alle 42 Browserpruefungen eingebaut.
    Der Betrieb bleibt unveraendert.
  * tools/alles-pruefen.mjs gibt jeder Datei eine Frist von 600 s. Wer
    sie reisst, wird abgebrochen und ZAEHLT ALS FEHLGESCHLAGEN, mit der
    letzten Ausgabe davor als Beleg. Einzelne Dateien nachzuruesten hilft
    nur bis zur naechsten ohne Notbremse -- deshalb sitzt sie dort, wo
    sie fuer alle gilt, auch fuer die, die es noch nicht gibt.

AUSSERDEM: pruef-breiten.mjs pruefte 16 Seiten aus einer Liste von Hand,
chat.html und leistung.html fehlten darin. Der Chat war bei keiner
einzigen der neun Breiten je gemessen worden. Wie bei pruef-handy kommt
die Liste jetzt aus dem Verzeichnis.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 22:06:49 +02:00
DogFatherGitandClaude Opus 5 80a8aac774 Der ganze Prueflauf blieb am Chat-Strom stehen -- behoben
BEFUND. Seit der Chat am 06.09. dazukam, LIEF DER GESAMTE
REGRESSIONSLAUF NICHT MEHR DURCH. Nicht "er wurde rot" -- er blieb
einfach stehen, bei Datei 3 von 62, ohne Fehlermeldung, ohne FEHL, ohne
Absturz. Nach zwoelf Minuten stand er immer noch dort. Von aussen sieht
"noch nicht fertig" genauso aus wie "haengt fuer immer"; deshalb ist
mir das gestern nicht aufgefallen, sondern erst, als ich den Lauf
gezielt beobachtet habe.

DIE URSACHE. pruef-alle-wege.mjs geht stumpf ueber ALLE 134
Schnittstellen und liest jede Antwort mit `await a.text()` aus. Der
Chat haelt seine Verbindung aber absichtlich offen und schickt neue
Nachrichten hinein, solange jemand zusieht (SSE). `text()` wartet, bis
der Server fertig ist -- und der wird nie fertig. Angemeldet als
DogFather trat der Lauf dort ein und kam nicht wieder heraus.

DIE ABHILFE, zwei Teile, die zusammengehoeren:
  * Jeder Ruf hat jetzt eine Frist von 8 s. Laeuft sie ab, ist das ein
    ERGEBNIS ("hing") und kein Absturz. Ein neuer Abschnitt meldet am
    Ende, WELCHER Weg nicht geantwortet hat -- statt dass der Lauf
    wortlos stehenbleibt.
  * Bekannte Stroeme stehen in einer Liste MIT BEGRUENDUNG und werden
    nicht uebersprungen, sondern anders geprueft: verbinden, Status
    ablesen, abbrechen. Die Schranke wird damit genauso gemessen wie
    ueberall. Zusaetzlich wird nachgemessen, dass ein eingetragener
    Strom auch wirklich offen bleibt -- sonst verdeckte die Ausnahme
    nur seine Inhaltspruefung.
Der "hing"-Zustand musste eigens gesammelt werden: In Abschnitt 1 haette
er ausgesehen wie "ohne Anmeldung erreichbar", in Abschnitt 2 waere er
ganz durchgefallen (`"hing" >= 500` ist false, Zeichenkette gegen Zahl).
Genau so verschwinden Befunde.

Ergebnis: 134 Schnittstellen, 532 Aufrufe, alles gruen, kein Haenger.

AUSSERDEM, gefunden beim Nachsehen:

  * pruef-handy.mjs pruefte 15 Seiten -- aus einer Liste von Hand, die
    veraltet war. chat.html, leistung.html und steckbrief.html standen
    nicht darin: DREI von neunzehn Seiten waren nie auf einem Handy
    gemessen worden, ausgerechnet der Chat. Die Liste kommt jetzt aus
    dem Verzeichnis, die naechste neue Seite ist von selbst dabei.
  * chat.html und leistung.html fehlte <link rel="manifest">. Auf dem
    Handy heisst das: Wer die App installiert hat und ueber eine
    Benachrichtigung dort landet, verlaesst den App-Rahmen -- die Seite
    oeffnet im Browser, mit falscher Leistenfarbe. Ihre theme-color war
    ausserdem eine andere als auf allen uebrigen Seiten. Beides behoben
    UND als Pruefung in pruef-struktur nachgetragen, damit es beim
    naechsten Mal nicht am Gedaechtnis haengt.

NEU: tools/wiederherstellung-proben.mjs — die Probe aufs Exempel.
Die Sicherung ausserhalb des Servers laeuft taeglich und prueft
`integrity_check`. Das sagt: die Datei ist nicht zerschossen. Es sagt
NICHT, ob die Anwendung damit startet, ob man sich anmelden kann und ob
die Daten vollstaendig sind. Eine Sicherung, die man nie zurueckgespielt
hat, ist eine Hoffnung. Das Werkzeug kopiert die juengste Sicherung in
ein Wegwerf-Verzeichnis, startet die echte Anwendung dagegen (damit
laufen alle Schemawanderungen wirklich durch), zaehlt vorher und
nachher, meldet sich an und ruft jede Seite auf. Drei Ausgaenge, nicht
zwei -- "konnte nicht nachsehen" ist weder Erfolg noch Fehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 18:34:57 +02:00
DogFatherGitandClaude Opus 5 953c3f5721 Zahlen bekommen eine Oberflaeche, das Dashboard bekommt Zahlen
Der Server konnte seit gestern rechnen -- und niemand konnte etwas
eintragen. Ein Rechenwerk ohne Oberflaeche ist kein Modul, sondern ein
Versprechen. Das ist jetzt eingeloest:

  * leistung.html: Wochenkarten (Wert, Vergleich zur Vorwoche,
    Verlaufslinie), Ziele als Fortschrittsbalken, die letzten 14 Tage
    zum Eintragen -- auch die LEEREN, denn eine Luecke ist selbst die
    Auskunft. Import aus dem TikTok-Export mit Vorschau VOR dem
    Schreiben.
  * Kachel "Zahlen" als erste der Gruppe "Rund um den Creator", eigenes
    Zeichen (steigende Linie, kein zweites Balkendiagramm).
  * Dashboard: jede Creator-Karte traegt jetzt Diamanten, LIVE-Tage und
    Verweildauer. Bis heute zeigte sie ausschliesslich ARBEIT -- ein
    Creator konnte null ueberfaellige Aufgaben haben und gleichzeitig
    seit drei Wochen einbrechen, und die Karte sah tadellos aus.
  * Die Fruehwarnung steht oben im Dashboard. Sechs benannte Signale im
    Klartext mit Beleg, bewusst OHNE Punktzahl -- ein Wert wie
    "Abwanderungsrisiko 73" klingt nach Wissenschaft und ist eine
    Behauptung, der man nicht widersprechen kann.
  * chat.html?mit=<person>: der Weg von der Warnung ins Gespraech. Gab
    es vorher nicht; ein Hinweis ohne Weg dahin ist nur ein schlechtes
    Gewissen.

GEFUNDEN VON DEN PRUEFUNGEN, nicht von der Theorie:

  1. Die Verlaufslinie zeichnete fuer vier der sieben Groessen NICHTS.
     Der Server liefert im Verlauf nur drei Reihen; fuer den Rest kam
     undefined an. `=== null` faengt das nicht, Number(undefined) ist
     NaN, und ein <polyline points="NaN,NaN"> ist im DOM vorhanden und
     auf dem Bildschirm unsichtbar -- ohne Fehlermeldung. Die Pruefung
     misst deshalb die PUNKTE, nicht die Anwesenheit des Elements.
  2. Fuenf Schriftgroessen im CHAT lagen unter 11,5 px (.68/.7 rem) und
     waren seit vorgestern drin. Die Grundlinie der Groessenpruefung
     stand auf 43, gemessen wurden 48. Nicht die Grundlinie angehoben,
     sondern die fuenf Stellen behoben: Eine Grundlinie, die mitwaechst,
     ist keine mehr.

pruef-leistung-optik.mjs: 57 Pruefungen, zwei echte Browser, Gegenprobe
zu jeder Aussage -- auch dazu, dass die Vorschau vor dem Bestaetigen
wirklich noch nichts geschrieben hat und dass ein Creator seine Zahlen
zwar SIEHT, sie aber weder eintragen noch die Fruehwarnung ueber sich
lesen kann (auch nicht ueber eine von Hand gebaute Anfrage).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 18:27:16 +02:00
DogFatherGitandClaude Opus 5 c8a4e7628c Stufe 1+2: Zahlen ueber das Geschaeft, und eine Fruehwarnung
Der grosse Befund aus dem Plan vom 31.08.2026: Der Workspace
organisiert ARBEIT hervorragend, wusste ueber das GESCHAEFT aber
nichts. Keine Diamanten, keine LIVE-Tage, keine Verweildauer. Damit
hing die ganze Betreuung in der Luft -- der Start-Check bewertete ohne
zu messen, der Report fragte "was hat funktioniert" ohne Beleg, das
90-Tage-Ziel war ein Satz statt eines Fortschritts.

STUFE 1 -- LEISTUNG

Eine Zeile je Creator und TAG (nicht Woche: Feineres laesst sich immer
zusammenfassen, Groeberes nie aufteilen). Diamanten, LIVE-Dauer,
gueltiger Tag, Zuschauer im Schnitt und in der Spitze, Verweildauer,
Schenker, neue Follower -- und eine NOTIZ. Ohne sie sieht man in drei
Monaten einen Einbruch und weiss nicht mehr, dass die Person Grippe
hatte.

DIE VERWEILDAUER IST DIE LEITZAHL (Recherche 06.09.2026): 2026 haengt
der Algorithmus alles an der Completion Rate, und sie gehoert als
BETRIEBSkennzahl behandelt -- sie soll die naechste Runde steuern,
nicht die letzte erklaeren.

Jede Zahl kommt mit Vergleich, Verlauf und Einordnung. Eine Zahl ohne
diese drei ist eine Eitelkeitszahl: Sie sieht nach Auskunft aus und ist
keine, weil man nichts entscheiden kann.

Zwei Wege hinein: Schnelleingabe und CSV-Import aus dem offiziellen
Export, mit Vorschau vor dem Uebernehmen.

STUFE 2 -- FRUEHWARNUNG

Sechs benannte Signale (Zahlen fallen, LIVE-Tage brechen weg, lange
nicht angemeldet, kein Gespraech, Aufgabenstau, Start-Check haengt) und
daraus EIN ruhiger Satz: "Bei Nora wuerde ich diese Woche nachfassen."

BEWUSST KEIN SCORE. Eine Note von 1 bis 100 wirkt objektiv und ist es
nicht; sie verfuehrt dazu, Menschen nach einer Zahl zu behandeln. Am
Score kann man nichts tun, am Signal schon. Deshalb Saetze statt
Punkte, und jedes Signal einzeln nachvollziehbar.

Ein CREATOR sieht die Fruehwarnung nicht: Das ist eine
Arbeitsgrundlage fuer die Betreuung, keine Mitteilung an die
betroffene Person.

ZWEI ECHTE FEHLER, VON DER PRUEFUNG GEFUNDEN

1. ZAHLENFORMAT, Faktor tausend daneben. "1.250" wurde als 1,25
   gelesen und "1,234" als 1,234. Die Regel "das letzte Trennzeichen
   ist das Dezimalzeichen" liefert bei nur EINEM Trennzeichen fuer
   beide Faelle das Falsche -- und die Zahl sieht danach trotzdem
   plausibel aus. Jetzt entscheidet, wie viele Ziffern dahinter stehen:
   genau drei heisst Tausendertrenner.

2. DIE ZIELE-ROUTE WURDE VERSCHLUCKT. Express nimmt die erste passende
   Route, und `:tag` passt auch auf "ziele" -- ein PUT auf .../ziele
   landete in der Tages-Route und scheiterte am Datumsmuster. Die
   Meldung sagte "ungueltig", was auf die Zahlen deutet, nicht auf den
   Weg. Reihenfolge getauscht.

Beides waere still gewesen: falsche Zahlen sehen richtig aus, und ein
Ziel, das sich nicht setzen laesst, probiert man zweimal und laesst es
dann.

pruef-leistung.mjs: 47 Punkte, darunter jede Rechnung einzeln
nachgerechnet (Durchschnitte ohne Tage ohne Wert, Prozent von null,
Wochengrenzen), Import zweimal eingelesen, deutsche und englische
Zahlenformate, Rechte, und die Gegenprobe, dass die Fruehwarnung bei
Unauffaelligkeit SCHWEIGT.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 18:12:16 +02:00
DogFatherGitandClaude Opus 5 17157b4f18 Sicht-Umschalter: ein Auge statt des Wortes "SICHT"
Filipe zu dem Feld in der Kopfleiste: "das soll viel besser aussehen
viel geiler aber nicht so eine scheisse."

Er hatte recht. Da stand ein Etikett mit dem Wort SICHT und daneben ein
Auswahlfeld -- ein Formular, das in die Kopfleiste gerutscht war. Drei
Aenderungen:

  * DAS AUGE ersetzt das Wort. Es sagt dasselbe in einem Zeichen und
    laesst dem NAMEN den Platz, um den es eigentlich geht. Fuer
    Vorleseprogramme bleibt die Beschriftung erhalten, nur unsichtbar --
    sie ersatzlos zu loeschen haette das Feld unbeschriftet gelassen.

  * "Meine Sicht" statt "Alles (meine Sicht)". Der Zusatz erklaerte
    nichts und machte den Knopf so breit, dass am Handy nichts anderes
    mehr danebenpasste.

  * EIN KREUZ statt "zurueck zu mir". Der Satz brauchte mehr Platz als
    der Name daneben, und was ein Kreuz an einer aktiven Auswahl tut,
    weiss jeder. Die Worte bleiben im aria-label und im Titel.

DIE EIGENTLICHE VERBESSERUNG ist aber die Hierarchie: Im Normalfall
ist der Umschalter still (graues Auge, ruhiger Rand). Sobald eine
FREMDE Sicht laeuft, traegt er ganz Farbe -- nicht nur ein
Zusatzknopf am Rand.

Das ist der gefaehrlichste Zustand dieser Funktion: Wer sie vergisst,
sieht am naechsten Tag drei Aufgaben statt dreissig und haelt das fuer
den Bestand. Ein Zustand, der Schaden anrichtet, muss lauter sein als
einer, der nichts tut.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 17:48:03 +02:00
DogFatherGitandClaude Opus 5 9dc35f5da6 Chat wieder einhaengen -- die Datei ist jetzt da
Nachtrag zu 09a4cfd ("AUSFALL BEHOBEN: versehentlich mitcommitteten
Import zurueckgenommen"). Das war richtig: Import und Einhaengung des
Chats waren aus einem unfertigen Stand mitgekommen, waehrend
workspace-chat.js noch gar nicht im Repo lag -- der Server fand das
Modul nicht und stuerzte in einer Schleife ab.

Seit 4637aa2 liegt die Datei im Repo. Ohne diese Zeile blieb der Chat
aber tot: Der Server startete tadellos, und JEDER Chat-Weg antwortete
still mit 404. Gemessen ueber die Browserkonsole -- die Ampelpruefung
meldete "Failed to load resource: 404" und nannte drei Adressen:
/api/chat/raeume, /api/chat/strom, /api/chat/ungelesen.

DER UNANGENEHMERE VON ZWEI FEHLERN: Fehlt die Datei, faellt der ganze
Dienst aus -- laut und sofort. Fehlt nur die Einhaengung, laedt die
Seite, der Chat bleibt leer, und niemand sieht warum. Deshalb steht
jetzt ein Kommentar an der Zeile, der beides zusammen nennt.

Nachgemessen nach der Reparatur: 0 fehlende Ressourcen (vorher 3).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 17:43:09 +02:00
DogFatherGitandClaude Opus 5 686249e36c Und die restlichen 40 Pruefbilder
Der erste Durchgang hat nur 62 von 102 erwischt. Grund: Die Liste kam
aus `path: "..."` -- Bilder, deren Name aus einem Template-Literal oder
einer Variablen entsteht, standen nicht darin:

    path: breite === 1440 ? "pruef-kalender-computer.png" : "…-handy.png"
    path: `pruef-rollen-${r.rolle}.png`

Danach war die Kontrolle entscheidend, nicht die Erfolgsmeldung: "0
Pruefbilder versioniert" -- es waren noch 40. Wer nach einem
Aufraeumen nicht nachzaehlt, glaubt der Absicht statt dem Ergebnis.

Vor dem Entfernen noch einmal im GANZEN Repo geprueft (nicht nur in
server/ und tools/, wie beim ersten Mal): Kein readFileSync, kein
existsSync, kein readFile auf eine .png-Datei. Sie werden ueberall nur
geschrieben.

Kontrolliert danach: 0 Pruefbilder versioniert, QR-Codes weiterhin 3.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 17:06:48 +02:00
DogFatherGitandClaude Opus 5 1f4717d683 Bilder der Pruefungen aus der Versionierung nehmen
62 Bildschirmfotos, 54 MB, die bei JEDEM Prueflauf neu geschrieben
werden. Sie standen im Repo und tauchten dadurch bei jedem Commit als
Aenderung auf -- man haette sie mitcommittet oder jedes Mal von Hand
aussortiert. Beides ist Ballast, und beides passiert irgendwann falsch.

Sie bleiben auf der Platte und werden weiter erzeugt. Nur versioniert
sind sie nicht mehr.

GEPRUEFT VOR DEM ENTFERNEN: Keine einzige Pruefung LIEST je ein Bild --
kein readFileSync, kein existsSync auf .png; sie werden ausschliesslich
geschrieben. Es sind also Ansichtsbilder, keine Vergleichsvorlagen,
deren Verlust eine Pruefung blind machen wuerde. Waeren es welche,
duerften sie nicht weg.

UND EIN BEINAHE-FEHLER, der hier festgehalten gehoert: Der erste Anlauf
nahm "alle .png ausser workspace/assets und assets/img". In dieser
Liste standen assets/qr/qr-instagram.png und zwei weitere QR-Codes der
Website -- echte Dateien, die niemand neu erzeugt. Ein zu grobes Muster
haette sie mitgeloescht.

Die Liste kommt deshalb jetzt aus der Quelle statt aus einem Muster:
Was in einem `screenshot({ path: ... })` einer Pruefdatei steht, kann
weg. Alles andere nicht. Kontrolliert danach: QR-Codes (3) und
Website-Bilder (105) sind unveraendert versioniert.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 17:05:54 +02:00
DogFatherGitandClaude Opus 5 3bab6d9fbf Der Knopf auf der Umzugsseite ist jetzt ein echter Verweis
Hinweis von Filipe: Wenn etwas wie ein Knopf aussieht, muss man auch
draufdruecken koennen. Er hatte recht.

Meine urspruengliche Begruendung - die Adresse solle nur zum Lesen
dastehen, damit niemand versehentlich ein Lesezeichen auf den alten Weg
setzt - traegt nicht: Wer klickt, landet auf der NEUEN Adresse und setzt
sein Lesezeichen genau dort. Ein Element, das wie ein Knopf aussieht und
sich nicht wie einer verhaelt, ist eine Falle.

Der Knopf hat jetzt einen Hover- und einen Tastatur-Fokuszustand, und
beide Bewegungen entfallen bei prefers-reduced-motion.

Die Pruefung geht dabei einen Schritt weiter als vorher: Sie prueft nicht
mehr nur, WELCHE Anfragen zugeordnet werden, sondern ruft die Middleware
wirklich auf und sieht sich die ANTWORT an. Denn die Zuordnung kann
stimmen und die Seite trotzdem kaputt sein:

  - ein Platzhalter, der woertlich in der Seite steht statt eingesetzt zu
    werden
  - ein Knopf ohne Verweis (genau der Fall, den Filipe gemeldet hat)
  - HTML statt JSON fuer /workspace/api/ - ein noch offener Tab wuerde
    sonst die Hinweisseite als Daten zu lesen versuchen
  - und die wichtigste: dass die NEUE Adresse durchgelassen wird

31 Pruefungen, alle gruen. Gegenprobe gemacht: Dreht man den Knopf zurueck
zu einem div ohne Verweis, meldet die Pruefung 2 Fehler - sie haette den
gemeldeten Mangel also selbst gefunden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 16:50:30 +02:00
DogFatherGitandClaude Opus 5 8e3eccc7a8 Alte Workspace-Adresse abgeschaltet statt weitergeleitet
Auf Filipes Wunsch: dogfather-universe.com/workspace/ soll nicht mehr
existieren, er gibt den neuen Link selbst an Creator und Scouts weiter.
Vorher stand dort eine Weiterleitung - die haette die alte Adresse auf
unbestimmte Zeit am Leben gehalten.

Der Server antwortet dort jetzt mit 410 Gone. Beides - 404 und 410 -
heisst "gibt es hier nicht"; 410 sagt zusaetzlich "gab es, ist
absichtlich weg, kommt nicht wieder". Genau der Sachverhalt.
Suchmaschinen nehmen die Adresse dadurch schneller heraus, und in einem
Protokoll ist ein 410 sofort als gewollt erkennbar, waehrend ein 404
immer nach Versehen aussieht.

Statt einer nackten Fehlermeldung kommt eine kurze Hinweisseite mit der
neuen Adresse. Wer hier landet, hat einen alten Link oder eine alte
Verknuepfung und soll erfahren, wohin der Workspace gezogen ist, statt vor
einem leeren Fenster zu sitzen. Bewusst OHNE Verweis zum Anklicken, damit
niemand aus Versehen ein neues Lesezeichen auf den alten Weg setzt.

Schnittstellen-Aufrufe unter /workspace/api/ bekommen JSON statt HTML -
ein alter, noch offener Browser-Tab wuerde sonst die Hinweisseite als
Daten zu lesen versuchen und einen unverstaendlichen Fehler zeigen.

Die Pruefung deckt weiterhin 21 Faelle ab und ist mitgezogen: Der
gefaehrlichste Fall heisst jetzt nicht mehr "Endlosschleife", sondern
"die neue Adresse mit abschalten" - wer nicht auf den Hostnamen prueft,
sperrt den Workspace fuer alle aus. Alle gruen, Gegenprobe schlaegt an.

Diesmal vor dem Commit den Diff Datei fuer Datei kontrolliert - beim
letzten Mal hatte ich hier eine fremde uncommittete Zeile mitgenommen und
damit die Website fuer zwei Minuten abgeschossen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 16:34:27 +02:00
DogFatherGitandClaude Opus 5 09a4cfdeab AUSFALL BEHOBEN: versehentlich mitcommitteten Import zurueckgenommen
Die Website war ab 16:27 unerreichbar (HTTP 502, 29 Neustartversuche).
Ursache war mein Commit 58b8ef2: Beim "git add server/index.js" habe ich
eine uncommittete Zeile einer parallel laufenden zweiten Sitzung
mitgenommen -

    import { chatRouter } from "./workspace-chat.js";
    app.use(chatRouter);

- deren Datei workspace-chat.js aber noch nicht committet war. Auf dem
Server fehlte sie deshalb, und Node bricht beim Start mit
ERR_MODULE_NOT_FOUND ab.

Warum es niemandem vorher auffiel: Der Dienst lief seit Stunden mit dem
alten Code im Speicher. Ein fehlender Import faellt erst beim NEUSTART auf
- und der erste Neustart seit dem Push war meiner fuer den Workspace-Umzug.
Der Umzug selbst war nicht die Ursache, er hat den Fehler nur ausgeloest.

Behoben durch Entfernen der beiden Zeilen - nicht durch Nachcommitten von
workspace-chat.js: Die Chat-Funktion ist unfertige fremde Arbeit
(workspace.js und workspace-push.js sind dort ebenfalls geaendert und
uncommittet). Sie gehoert von der Sitzung committet, die sie gebaut hat.
Der Server laeuft damit wieder auf dem Stand, der vorher lief - plus dem
Workspace-Umzug.

Eigene Lehre, doppelt: Bei den 17 workspace-Seiten hatte ich die fremden
Aenderungen sorgfaeltig aus dem Index herausgehalten - bei server/index.js
habe ich genau das vergessen. Und: node --check haette es nie gefunden,
ein Import auf eine nicht existierende Datei ist syntaktisch tadellos.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 16:29:20 +02:00
DogFatherGitandClaude Opus 5 4637aa24e7 Chat zwischen Creator, Scouts und Managern
Wunsch Filipe, 06.09.2026: *"eine kategorie chat, wo die creator mit
ihren manager nachrichten austauschen koennen, wie erinnerungen, fragen
und noch vieles mehr."* Auf Nachfrage entschieden: Zweier-Gespraeche UND
Gruppen; Manager und Scouts duerfen sich auch ohne gemeinsamen Creator
schreiben.

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

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

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

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

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

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

DAZU: CALL-LISTE NACH ZEIT GEGLIEDERT

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

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

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

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

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 16:28:58 +02:00
DogFatherGitandClaude Opus 5 58b8ef233a Creator Workspace zieht auf eine eigene Adresse um
Der Workspace laeuft ab sofort auf workspace.dogfather-universe.com. Die
alte Adresse dogfather-universe.com/workspace/ leitet dorthin weiter.

Grund: Chrome laesst neben der Hauptseite, deren Bereich "/" die ganze
Domain umfasst, keine zweite App auf derselben Adresse zu. Unter dem alten
Pfad liess sich der Workspace nur als Verknuepfung ablegen, nie als
richtige App installieren - im Menue stand "Oeffnen in DOGFATHER
UNIVERSE" statt "Seite als App installieren". Das ist kein Fehler,
sondern Absicht (w3c/manifest Nr. 1180: verschachtelte Bereiche auf einem
Ursprung sind "strongly not recommended"). Auf der neuen Adresse hat
Filipe die App erfolgreich installiert.

Der PFAD /workspace/ bleibt erhalten. An ihm haengen 150 Server-Routen,
107 API-Aufrufe und 26 Server-Dateien - ihn wegzuschneiden waere ein
grosser Umbau ohne Gewinn, denn der Konflikt entsteht durch die
gemeinsame ADRESSE, nicht durch den Pfad. Dadurch musste am Code nichts
weiter geaendert werden.

Die Entscheidung steht in einer eigenen Datei (workspace-umzug.js), weil
sie zwei Stellen hat, an denen ein Denkfehler teuer waere und die man
einer Bedingung nicht ansieht:

  1. ENDLOSSCHLEIFE - derselbe Dienst bedient beide Adressen. Ohne
     Hostpruefung leitet die neue Adresse auf sich selbst, und der
     Workspace waere sofort nach dem Neustart fuer alle unerreichbar.
  2. PRAEFIX-IRRTUM - "faengt an mit /workspace" trifft auch
     /workspaceXYZ und /workspace-alt.

Als eigene Funktion ist beides pruefbar, ohne den Server zu starten:
pruef-workspace-umzug.mjs deckt 21 Faelle ab (alte/neue Adresse, mit und
ohne www, Gross-/Kleinschreibung, Portangabe, lokale Testadressen, beide
Praefix-Fallen). Alle gruen. Gegenprobe gemacht: Baut man die
Endlosschleife absichtlich ein, meldet die Pruefung 3 Fehler; baut man den
Praefix-Irrtum ein, meldet sie 2. Sie kann also auch "nein" sagen.

Bewusst 302 und nicht 301: Ein 301 wird vom Browser dauerhaft gemerkt und
laesst sich praktisch nicht zurueckholen - waere an der Umleitung etwas
falsch, waere die alte Adresse fuer jeden, der sie einmal aufgerufen hat,
dauerhaft unbrauchbar.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 16:23:32 +02:00
DogFatherGitandClaude Opus 5 e0d8e6d55d Tagesblick: Termine als Karten, und vier Fehler, die dabei auffielen
Filipe zu der Liste mit einem einzigen Termin darin: "das soll viel
geiler und krasser aussehen bitte."

Er hatte recht, und der Grund war der Einzelfall. Ein Zeitstrahl lebt
davon, dass er etwas VERBINDET -- bei einem Eintrag verbindet er nichts.
Uebrig blieben eine magere Zeile, ein Strich ins Leere und Weissraum bis
zur Plakette am rechten Rand.

Jetzt traegt jede Karte fuer sich: eigene Flaeche mit farbiger Kante,
die Uhrzeit gross und in der Farbe der Terminart (vorher war sie
kleiner als der Titel -- dabei ist sie das, was man sucht), ein Balken
fuer die Dauer, und "JETZT" am naechsten Termin statt eines etwas
helleren Hintergrunds. Erledigtes bekommt einen Haken und verliert die
Farbe, bleibt aber voll lesbar; vorher wurde alles blasser, was auch
den Text traf.

Kein Neon dazu. Die Wirkung kommt aus Kontrast und Hierarchie.

VIER FEHLER, DIE DIE PRUEFUNGEN GEFUNDEN HABEN

1. ZEITZONE, in NEUN Pruefungen. Um 00:10 meldete pruef-teilnehmer
   ploetzlich 13 Fehlschlaege an einer Datei, die seit Stunden niemand
   angefasst hatte:

       Ortszeit:  06.09.2026, 00:10
       UTC:       05.09.2026, 22:10

   Sie bildeten ihr Tagesdatum mit toISOString() -- also UTC -- legten
   ihre Termine auf gestern und suchten heute. ZWEI STUNDEN AM TAG waren
   sie damit rot, im Winter eine. Wer nur tagsueber laeuft, sieht das
   nie. Jetzt gibt es helfer-zeit.mjs mit derselben Rechenweise wie die
   Anwendung, und pruef-struktur sucht das Muster kuenftig automatisch.

2. MEIN UMSTELL-SKRIPT VERSAGTE STILL. Es pruefte
   `if "helfer-zeit.mjs" not in s` -- und mein eigener Kommentar
   enthielt den Dateinamen. Ergebnis: keine einzige der neun Dateien
   bekam den Import, alle waeren zur Laufzeit abgestuerzt. `node --check`
   findet das nicht. Aufgefallen, weil danach nachgezaehlt wurde statt
   der Erfolgsmeldung zu glauben.

3. DIE KACHEL LIESS DIE SEITE SPRINGEN. Der Layout-Sprung auf
   start.html stieg von 0 auf 0,96 -- zweimal bestaetigt. Sie erschien
   erst nach dem Laden und schob alles darunter weg. Das ist kein
   Schoenheitsfehler, sondern der Grund, warum man auf den falschen
   Knopf drueckt.

   Gemessen wurden die echten Hoehen (1 Termin 169 px, 3 → 327, 5 →
   486). Daraus zwei Konsequenzen: Platz vorher reservieren, und
   hoechstens DREI Termine zeigen -- das halbiert die Spanne und ist
   die klarere Aussage. Der Rest steht als "1 weiterer Termin heute"
   darunter, nicht stillschweigend abgeschnitten. Von 0,96 auf 0,163.

4. SCHRIFTGROESSE, zweimal am selben Tag: erst die Art-Plaketten mit
   10,56 px, dann -- nach der Korrektur -- die neue Jetzt-Marke mit
   10,88. Beide Male gemeldet von pruef-handy und pruef-grosscheck,
   beide Male erst nach einem mehrminuetigen Browserlauf.

ZWEI PRUEFUNGEN, DIE SICH SELBST IM WEG STANDEN

pruef-tempo-workspace meldete "NEUE Doppelabfrage: start.html 2x
/workspace/api/termine". Nachgemessen an einem einzelnen Seitenaufruf:
genau eine Anfrage. Die Pruefung startete ihren Zaehler, bevor die
Anmeldung zur Ruhe gekommen war, und schrieb der Seite an, was die
vorherige noch offen hatte.

pruef-tagesblick fiel zum zweiten Mal auf dieselbe Falle herein: Sie
mass die Artfarbe an einem vorbeigezogenen Termin, der absichtlich grau
ist. Diesmal an der Wurzel geloest -- die Daempfung wird fuer die
Messung kurz abgeschaltet und sofort zurueckgesetzt. Damit ist die
Zuordnung fuer JEDEN Termin geprueft, unabhaengig von der Uhrzeit des
Laufs, und zusaetzlich beweist die Pruefung, dass die Daempfung greift.

NEU: Schriftgroessen werden jetzt AN DER QUELLE geprueft, in Sekunden
statt Minuten. Im Bestand stehen 43 solche Stellen; sie alle rot zu
melden haette die Pruefung ab Tag eins wertlos gemacht. Deshalb eine
Grundlinie wie beim Layout-Sprung: Sie haelt den Stand fest und
schlaegt an, sobald es MEHR werden. Heute haette das zweimal gegriffen.
Die Browserpruefung bleibt daneben -- sie sieht, was am Ende auf dem
Schirm steht, die CSS-Pruefung nur, was gemeint war.

Gesamtlauf: 56 von 56 Dateien, 2002 von 2002 Punkten.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 01:32:33 +02:00
DogFatherGitandClaude Opus 5 6541ecbfa7 Alte Symboldateien tragen jetzt auch das neue Bild
Filipe hat die Verknuepfungen dreimal neu angelegt und sah trotzdem das
alte Symbol. Die Ursache lag nicht an den neuen Dateien - die werden
nachweislich korrekt ausgeliefert (36 von 36 live byteweise geprueft),
sondern an vier Altlasten:

  /assets/img/favicon.png          256x256
  /assets/img/apple-touch-icon.png 180x180
  /assets/img/icon-192.png         192x192
  /assets/img/icon-512.png         512x512

Die stammen aus der Zeit vor dem Umbau auf app-symbole/ und werden von
KEINER Seite mehr verlinkt - geprueft mit einer Suche ueber alle html,
js, json und webmanifest: nur noch sw.js und main.js nennen sie.

Aber Chrome hat sich das Favicon dieser Domain gemerkt, als favicon.png
noch verlinkt war, und gibt es nicht mehr her: Beim Anlegen einer
Verknuepfung nimmt Windows dieses alte Bild, egal was im HTML steht. Auf
Filipes Bildschirm trugen deshalb ZWEI verschiedene Verknuepfungen
dasselbe Symbol - das war der entscheidende Hinweis, denn zwei Apps mit
verschiedenen Manifesten koennen unmoeglich dasselbe Symbol haben.

Statt zu hoffen, dass ein Zwischenspeicher irgendwann aufgibt, tragen die
alten Adressen jetzt einfach dasselbe Bild wie die neuen. Damit ist es
egal, welche ein Programm nimmt.

Dazu im Service Worker: CACHE_NAME auf v2 hochgezaehlt - activate()
loescht dadurch den alten Zwischenspeicher samt altem Symbol. Und
SHELL_ASSETS zeigt nicht mehr auf die verwaisten icon-192/-512, sondern
auf die Adressen aus dem Manifest.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 00:32:59 +02:00
DogFatherGitandClaude Opus 5 ed12d75404 App-Symbole: Kennzeichen-Plakette, damit man sie klein unterscheiden kann
Die Symbole waren farblich schon verschieden, zeigten aber alle dasselbe
Motiv. Gemessen in echter Groesse: Bei 24px (Startmenue) und 32px
(Taskleiste) ist der Husky nur noch ein Fleck - es unterscheidet sie
einzig die Farbe, und Webdesign, Kundenportal und WD-Verwaltung liegen
im Lila-Rosa-Bereich dicht beieinander.

Jedes Symbol traegt jetzt unten rechts eine Plakette mit einem Kennzeichen
in der Farbe der App:

  Hauptseite  *      DogiCrew-Verwaltung  C     Webdesign         W
  Kundenportal K     WD-Verwaltung        V     Creator Workspace A

Der Husky bleibt gross und dominant - die Marke wird nicht angetastet,
die Plakette traegt nur die Unterscheidung.

Die maskable-Fassungen haben eine EIGENE Plakettenlage, und die ist
gerechnet, nicht geschaetzt: Android behaelt nur den Kreis mit 80%
Durchmesser (Radius 0.40 ab Mitte). Der aeusserste Punkt der Plakette
liegt bei sqrt(2)*(0.5-rand-groesse/2)+groesse/2. Der erste Entwurf kam
damit auf 0.417 - die Plakette waere auf dem Handy angeschnitten worden.
Mit rand 0.190 und groesse 0.280 sind es 0.380, also mit Reserve drin.

Erzeugt aus den vorhandenen Symbolen (nicht neu gezeichnet), 36 Dateien:
je App 32, 180, 192, 512 und die beiden maskable. Jede Datei danach
einzeln aus dem PNG-Kopf vermessen - 36 von 36 haben die richtige
Groesse und Inhalt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-06 00:20:05 +02:00
DogFatherGitandClaude Opus 5 de60df5637 Creator Workspace ist jetzt wirklich installierbar
Der Workspace liess sich nicht als App installieren: Windows und Android
legten nur eine Browser-Verknuepfung an, mit dem globalen Favicon der
Domain statt dem eigenen Symbol. Grund: workspace/app.webmanifest lag
fertig da und wurde sogar ausgeliefert (HTTP 200), war aber in KEINER der
17 Seiten verlinkt. Ohne rel=manifest gibt es fuer den Browser nichts zu
installieren.

Jede der 17 Seiten bekommt deshalb den Manifest-Verweis und die eigene
Fensterfarbe #0674b9 (Blau, passend zum nachtblauen Symbol).

Zur Entstehung: Diese 17 Dateien tragen auch Versionsnummern einer
parallel laufenden zweiten Sitzung (?v=202609052252), deren zugehoerige
start.css und start.js noch nicht committet sind. Die gehoeren nicht in
diesen Commit. Sie wurden fuer das Bereitstellen kurz auf den committeten
Stand zurueckgesetzt und in der Arbeitskopie sofort wiederhergestellt -
im Index liegt dadurch nur die eigene Aenderung, ihre Arbeit bleibt
unangetastet uncommittet. Vorher gesichert, hinterher Datei fuer Datei
verglichen: kein Unterschied.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 23:44:54 +02:00
DogFatherGitandClaude Opus 5 5ef3a13615 Jede App der Domain bekommt eine eigene Fensterfarbe
Beim Installieren als App sahen alle Dienste gleich aus. Nicht wegen der
Symbole - die sind seit heute Mittag gut unterscheidbar - sondern wegen
der Farbe: zwoelf von vierzehn trugen dasselbe Fast-Schwarz (#05070b,
#05070d, #0a0910, #0b0d10, #0d0817, #0e0a16). Das ist die theme_color,
also am PC die Titelleiste des App-Fensters und am Handy die Statusleiste.

Neu, je App eine Farbe, abgeleitet vom eigenen Symbol:

  Hauptseite            #065f76  Petrol      (Symbol stahlblau)
  DogiCrew-Verwaltung   #8d4125  Kupfer      (Symbol orange)
  Webdesign             #564e95  Violett     (Symbol lila)
  Kundenportal          #924985  Magenta     (Symbol pink)
  WD-Verwaltung         #b44f5e  Rose        (Symbol rot)
  Creator Workspace     #0674b9  Blau        (Symbol nachtblau)

Nextcloud (#17a5a6) bleibt unveraendert - als einzige hob sie sich schon ab.

WICHTIG war, beide Stellen zu aendern: Die meta-Angabe im HTML
ueberschreibt die theme_color aus dem Manifest. Nur das Manifest zu
aendern haette gar nichts bewirkt.

Der background_color (Startbildschirm beim Oeffnen) bleibt bewusst sehr
dunkel, nur leicht in Richtung der App-Farbe getoent - kraeftig ist nur
die schmale Leiste, damit nichts grossflaechig aufblitzt (Vorgabe
augenschonend).

Wie die Farben entstanden sind: Zwei Entwuerfe fielen bei der eigenen
Pruefung durch. In HSL gerechnet lagen Workspace und Webdesign bei einem
Farbabstand von 12.6 statt der noetigen 25 - auf dem Papier 30 Grad
auseinander, fuers Auge dasselbe Blauviolett. Auch der zweite Versuch
scheiterte (Hauptseite zu nah an Nextclouds Tuerkis, 19.1). Neun Farben
bei gleicher Helligkeit passen schlicht nicht mit genug Abstand auf den
Farbkreis. Erst mit der Helligkeit als dritter Dimension und einem
Optimierer, der den KLEINSTEN Abstand im Satz maximiert, kam ein Satz
heraus, der haelt: kleinster Abstand 25.5.

Geprueft: 68 Farbpruefungen (weisse Schrift ueberall lesbar 5.0-7.2:1,
keine blendet, jede hebt sich vom bisherigen Schwarz ab, alle Paare
>= 25 Delta E) und 101 Browserpruefungen ueber 16 Seiten (Manifest und
meta stimmen ueberein, genau eine theme-color je Seite, Symbole
vorhanden) - alle gruen, Gegenprobe schlaegt an.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 23:35:25 +02:00
DogFatherGitandClaude Opus 5 c3b156dbad Tagesblick: eine Kachel fuer offene Punkte und die Termine des Tages
Wunsch Filipe, 05.09.2026, mit Bildschirmfoto der Hinweisliste: "eine
ganze kachel wo links die sachen sind die du da schon siehst und rechts
in der kachel die termine vom tag. in der mitte gesplittet. mach das
richtig geil und hochwertig."

WARUM DIE BEIDEN ZUSAMMENGEHOEREN

Links steht, was zu TUN ist, rechts, was schon FESTSTEHT. Zusammen
ergeben sie die einzige Frage, die man morgens hat -- wie sieht mein Tag
aus. Untereinander musste man scrollen, um sie zu beantworten.

Eine Kachel und nicht zwei nebeneinander: Zwei Rahmen lesen sich als
zwei Themen. Der Trenner laeuft oben und unten aus, statt von Kante zu
Kante durchzuschneiden -- eine harte Linie macht aus einer Kachel wieder
zwei.

Die rechte Haelfte ist ein Zeitstrahl, kein Kalenderauszug:
  * Was als NAECHSTES dran ist, wird hervorgehoben. Beim Ueberfliegen
    ist das die Auskunft, die man sucht -- nicht "der erste des Tages".
  * Vorbei heisst nicht weg. Erledigtes tritt zurueck, bleibt aber
    sichtbar; man will sehen, was man schon hinter sich hat.
  * Dieselben vier Farben wie im Kalender. Eine Farbe, die hier etwas
    anderes bedeutete, muesste man zweimal lernen.

Die Daten kommen aus der Kalender-Schnittstelle mit tage=1 -- keine
zweite Abfrage, keine zweite Sichtbarkeitsregel. Was jemand im Kalender
nicht sehen darf, kommt hier gar nicht erst an.

DREI FEHLER, DIE DABEI AUFFIELEN

1. Die rechte Haelfte war 38 statt 579 Pixel breit. Die versteckte
   Ueberschrift fuer Vorleseprogramme zaehlt als Kind im Raster und hat
   alles um eine Spalte verschoben, sodass "Heute" in der Ein-Pixel-
   Spalte des Trenners landete. Die Spalten sind jetzt ausdruecklich
   zugewiesen -- damit verschiebt auch ein spaeteres viertes Element
   nichts mehr.

2. Die Plaketten (CALL, TERMIN, REVIEW) hatten 10,56 px. Die Hausgrenze
   liegt bei 11,5 -- darunter liest auf einem Handy niemand mehr.
   Gemeldet von pruef-handy auf allen drei Geraeteklassen UND von
   pruef-grosscheck bei allen vier Rollen. Ein Fix, zwei Pruefungen.

3. Am Handy stand die Uhrzeit mittig zur Zeile, waehrend der Titel oben
   begann -- sie fluchteten nicht, sobald die Plakette unter den Text
   rutschte.

Die Kachel bleibt ganz weg, wenn BEIDE Haelften leer sind, und zeigt
sonst auf der leeren Seite einen Satz. Vorher haette jemand ohne offene
Punkte, aber mit drei Calls seinen Tagesplan nicht gesehen: Das
Verstecken hing an der linken Haelfte allein.

ZWEI PRUEFUNGEN, DIE SICH SELBST IM WEG STANDEN

pruef-tempo-workspace meldete "Layout springt: bereich.html 0,703".
Nachgemessen: derselbe Wert schwankt zwischen den Laeufen um den Faktor
zwei (aufgaben.html 0,478 / 0,262 / 0,262; bereich.html 0,703 dann
0,347). Er haengt davon ab, ob die Daten ankommen, waehrend das Geruest
noch aufgebaut wird. Eine Pruefung, die zufaellig rot wird, ist so
wertlos wie eine, die nie anschlaegt -- man klickt sie weg, und mit ihr
die echte Warnung. Statt die Grenze anzuheben (das haette sie stumpf
gemacht) wird ein Ausschlag jetzt durch WIEDERHOLUNG bestaetigt, und
gemeldet wird der zweite Wert, nicht der kleinere. Ein echter Sprung
kommt bei jedem Lauf und uebersteht das muehelos. Die neue Kachel selbst
springt uebrigens gar nicht: start.html steht bei 0.

pruef-push-weg meldete "ALLES IN ORDNUNG" und stuerzte danach ab
(libuv: UV_HANDLE_CLOSING, Rueckgabewert 3221226505). Ursache war
process.exit() mitten im Schliessen der fetch-Verbindungen. Jetzt
process.exitCode -- Node raeumt zu Ende. Fuenf Laeufe hintereinander
sauber. Eine Pruefung, die inhaltlich besteht und trotzdem rot ist, ist
das Schlimmste von beidem.

Gesamtlauf: 1994 von 1994 Punkten (1973 vorher + 21 der neuen Pruefung).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 22:43:59 +02:00
DogFatherGitandClaude Opus 5 5119e0139d vanvan.html: Coming-soon-Knopf wird Link zu VAN'S DIY
Der silberne Knopf trug 'Coming soon...' und war gesperrt (aria-disabled,
kein href). Er heisst jetzt 'VAN`S DIY' und fuehrt zu
https://vans-diy-bastelbedarf.com/ - in neuem Tab, mit rel=noopener, wie
der TikTok-Knopf darueber und die uebrigen Shop-Verweise im Projekt.

aria-disabled musste weg: sonst meldet ein Bildschirmleser 'nicht
bedienbar', obwohl der Knopf jetzt bedienbar ist.

Dabei aufgefallen und mitbehoben: Die Beschriftung brach auf schmalen
Geraeten mitten im Wort um ('VAN / `S / DIY' bei 390px, Knopf 117px statt
89px hoch), weil die Sektion auf vanvan.html auch auf dem Handy
zweispaltig bleibt - ein Inline-Style ueberschreibt dort die Regel aus
@media(max-width:620px). .btn-silver bekommt deshalb white-space:nowrap.
Das behebt zugleich den gleichen Umbruch von 'Coming soon...' auf
abonnieren.html bei 320px.

Geprueft im echten Browser: 52 Pruefungen (5 Sprachen x Text, href,
kein aria-disabled, target, rel, sichtbar, bedienbar) plus echter Klick,
der auf vans-diy-bastelbedarf.com landet - alle gruen, Gegenprobe
schlaegt an. Zusaetzlich 180 Messungen ueber 4 Seiten x 9 Breiten x 5
Sprachen (270 Knoepfe angesehen): kein Umbruch, kein Ueberlauf. Ohne den
nowrap-Fix meldet dieselbe Messung 30 Befunde.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 22:35:23 +02:00
DogFatherGitandClaude Opus 5 627f710d71 Aufgaben abbrechen, Teilnehmerwahl fuer alle, Benachrichtigungen
Zwei Wuensche vom 05.09.2026, dazu drei Fehler, die dabei ans Licht kamen.

ABBRECHEN (Wunsch: "in jedem status die aufgaben auch abbrechen koennen,
nur ich die manager und scouts")

Neuer Status mit Pflicht-Grund, aus jedem der vier Status heraus.
Festgehalten wird auch, WO die Aufgabe stand -- "im Review abgebrochen"
ist eine andere Aussage als "nie angefangen", und das Wiederaufnehmen
geht dorthin zurueck statt nach "offen".

Eigener Weg statt "abgebrochen" in der Statusliste: Dort entscheidet
darfAendern(), und das laesst auch den zustaendigen Creator aendern.
Der gewoehnliche PATCH kann diesen Zustand deshalb gar nicht erreichen
-- auch nicht fuer DogFather, sonst waere die Grund-Pflicht umgehbar.

Abgebrochenes steht in einem zugeklappten Bereich unter dem Brett, nicht
als fuenfte Spalte: Am Handy waeren dann alle fuenf unlesbar schmal.
Verschwinden darf es nicht, sonst waere der Abbruch ein Loeschen mit
Zwischenschritt.

Die Tabelle musste dafuer getauscht werden (SQLite kann CHECK nicht
aendern). Vorher auf einer Kopie durchgespielt: 40 von 40 Aufgaben,
Inhalte, Verweise und Indizes geprueft, Gegenprobe zeigt, dass der CHECK
noch lebt.

TEILNEHMERWAHL (Wunsch: "das soll viel besser aussehen und fuer jeden
verfuegbar sein")

Auf dem Bildschirm klebten die Namen aneinander: "DogfatherDogFather".
Ursache war, dass kalender.html das Stylesheet mit diesen Klassen nie
eingebunden hat -- sie standen in dateien.css. Vierzig gruene Pruefungen
zur Teilnehmerwahl hatten das nicht gemerkt, weil keine je gefragt hat,
ob es AUSSIEHT wie gedacht.

Jetzt eigene Klassen im eigenen Stylesheet, nach Rollen gruppiert: Die
Rolle steht einmal als Ueberschrift statt neunmal am Namen. Damit ist
das Kleben an der Wurzel weg, nicht zugepflastert.

"Fuer jeden" war mehr als ein hidden zu entfernen: darfEintragen() haette
einem Creator nur sich selbst erlaubt. Er haette seinen Scout gesehen,
angeklickt, und der Server haette ihn still weggelassen -- ein Knopf, der
nichts tut. einladbareIds() schaut jetzt in beide Richtungen, bewusst
getrennt von /api/personen: Wer die erweitert, gibt einem Creator
nebenbei die Moeglichkeit, seinem Scout Aufgaben zuzuweisen.

BENACHRICHTIGUNGEN (Wunsch: "sowas, und dass es perfekt funktioniert
fuer jeden")

Web Push nach RFC 8291/8292, ohne fremde Abhaengigkeit. Der Knopf sagt
in jeder Lage die Wahrheit, auch die unbequemen: abgelehnt (mit dem
Hinweis, wo man es zuruecknimmt), iPhone im Reiter (mit Anleitung),
Browser ohne Push. Ein Knopf, der bei abgelehnter Berechtigung nur
nichts tut, ist der sichere Weg zu "das funktioniert nicht".

DREI FEHLER, DIE DABEI AUFFIELEN

1. Ein defekter Zugangsdatensatz sperrte ALLE einer Rolle aus. Wirft
   hashe() bei einer Person, flog die ganze Anmeldung in den catch: 503
   "nicht verfuegbar" fuer jeden mit dieser Rolle. Aufgefallen durch
   einen eigenen Testfehler. Jetzt wird die defekte Person uebersprungen
   und laut protokolliert; die Gegenprobe zeigt, dass ein falscher Code
   weiterhin abgelehnt wird.

2. Die Glocke sprengte die Kopfleiste -- zweimal. Bei 320 px lag die
   Lupe des Suchknopfes auf dem Sicht-Umschalter (ein Knopf, der auf 12
   Seiten ins Leere tippt), bei 768 px wurde der Abmelden-Knopf bis zu
   15 px aus dem Bild geschoben, weil die Textgrenze auf 760 stand und
   ein Tablet 768 hat. Nachgewiesen durch Messen mit und ohne Glocke,
   nicht durch Vermuten.

3. .block__frage war viermal gestaltet und stand auf einer Seite, die
   keine dieser Dateien laedt -- derselbe Fehler wie bei der
   Teilnehmerwahl. Gefunden von der neuen Klassenpruefung beim ersten
   Lauf.

NEUE PRUEFUNGEN

pruef-css-klassen   jede gestaltete Klasse muss auf ihrer Seite ankommen
                    (unterscheidet Struktur-Anker von echtem Verlust)
pruef-dabei-optik   die Wahl im Browser, an den echten Pixeln
pruef-abbrechen     Umstellung auf einer Kopie, Rechte, Rueckweg
pruef-abbrechen-optik   Knopf, Dialog, Bereich, Handy
pruef-glocke        Zustaende, An/Abmelden, jede Rolle
pruef-push(-weg)    Rechnung gegen die RFC-Vektoren, Zustellung

pruef-struktur prueft jetzt zusaetzlich, ob sich jedes Server-Modul als
ESM laden laesst. node --check auf einer .js-Datei prueft als CommonJS
und meldete "ok", waehrend der Import scheiterte.

Gesamtlauf: 55 von 55 Dateien, 1973 von 1973 Punkten.

Was NICHT geprueft werden konnte und deshalb dasteht: Der Schritt
"Browser holt eine Adresse beim Push-Dienst" braucht eine Verbindung zu
Googles FCM, die ein Pruef-Browser nicht hat. Die Pruefung misst das
zuerst und meldet es als dritten Ausgang, statt gruen zu sein.
Verschluesselung und Zustellung sind getrennt geprueft; diese eine
Strecke beweist sich erst auf dem Server.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 18:05:28 +02:00
DogFatherGitandClaude Opus 5 529c814389 Kalender: mehrere Teilnehmer je Termin und Serie
Wunsch Filipe: "wenn ich im kalender was eintrage will ich dass ich
auch 2 leute markieren kann mit denen der call ist." -- und auf
Rueckfrage: beliebig viele, und auch fuer wiederkehrende Termine.

Bis hierher hatte ein Termin GENAU EIN Gegenueber. Fuer ein Gespraech
zu dritt musste man zwei Termine anlegen und hatte zwei Wahrheiten
ueber dieselbe halbe Stunde.

DAS IST KEINE ANZEIGE, SONDERN EINE RECHTEREGEL. An der
Teilnehmerliste haengt die Sichtbarkeit: Wer eingetragen ist, sieht den
Termin. Deshalb zwei Grenzen, beide serverseitig:
  * Eintragen darf man nur, wen man ohnehin sehen darf (Leitung jeden,
    ein Scout seine betreuten Creator, ein Creator sich selbst).
  * Zuordnungen verschiebt nur die Leitung -- sonst koennte sich jemand
    selbst in fremde Termine eintragen und sie sich damit sichtbar
    machen.

Gebaut nach dem Muster von datei_personen: eigene Tabellen
termin_teilnehmer und serie_teilnehmer statt weiterer Spalten.
teilnehmer_id bleibt das Haupt-Gegenueber und wird beim Speichern immer
in die Liste mit aufgenommen.

Vorhandene Termine werden beim Start uebernommen. Zusaetzlich liest die
Abfrage das Haupt-Gegenueber IMMER mit dazu -- die Liste stimmt damit
auch dann, wenn die Uebernahme nicht gelaufen ist (Sicherung
zurueckgespielt, Neustart ausgeblieben). Genau das ist beim Bauen
aufgefallen: Ein alter Termin zeigte "niemand dabei", obwohl ein
Gegenueber eingetragen war.

Serien vererben ihre Teilnehmer an jede erzeugte Auspraegung -- sonst
saehe der zweite Mensch den woechentlichen Call einmal und danach nie
wieder.

Neue Pruefung pruef-teilnehmer.mjs mit 40 Punkten: beide sehen ihn,
Fremde nicht, kein Selbsteintragen, Hinzufuegen und Entfernen, Serien,
Unsinn in der Liste. Mit Gegenprobe, die nachweist, dass die Messung
"nicht sichtbar" ueberhaupt erkennt. Im echten Browser gegengeprueft:
Schalter da, zwei angehakt, gespeichert, beide zurueck, keine
Konsolenfehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 14:21:09 +02:00
DogFatherGitandClaude Opus 5 8759a46e5b iPhone-Symbole: quadratisch und deckend statt abgerundet
Gemeldet: "auf dem pc und auf dem handy soll alles funktionieren, auf
dem einen klappt es und auf dem anderen nicht."

Der Grund ist, dass PC und Handy ihr Symbol an drei verschiedenen
Stellen holen -- und jede stellt andere Anforderungen:

  Browser-Reiter   das normale Symbol, runde Ecken erlaubt
  Android          die maskable-Fassung, aussen wird beschnitten
  iPhone           <link rel=apple-touch-icon> -- und iOS rundet SELBST
                   ab und fuellt alles Durchsichtige mit SCHWARZ

Unser 180er brachte seine eigene Rundung mit, also durchsichtige Ecken.
Auf dem iPhone wurden daraus schwarze Zipfel, die dann ein zweites Mal
beschnitten wurden. Auf dem PC sieht dieselbe Datei tadellos aus --
genau daher der Unterschied zwischen den Geraeten.

Jetzt wird die 180er-Fassung randvoll und deckend gebaut. Nachgemessen
an allen zehn: 0,00 Prozent durchsichtig, Ecken voll deckend. Die
normale Fassung bleibt abgerundet (4,75 Prozent) -- auch das gemessen,
damit der Fix nicht die andere Seite kaputtmacht.

Dazu eine Pruefung in pruef-struktur.mjs: alle sechs Groessen je App
vorhanden, und jede Seite nennt beide Symbolarten. Fehlt das
iPhone-Symbol, nimmt iOS einen Bildschirmausschnitt der Seite.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 12:46:32 +02:00
DogFatherGitandClaude Opus 5 e9709a5aa8 Maskable-Symbole: das ganze Symbol verkleinern, nicht nur das Logo
Gemeldet von Filipe: "die symbole sehen aber auf meinem pc nicht aus
wie auf dem browser mit diesen geilen farben." Er hatte recht -- die
maskable-Fassungen sahen aus wie eine schlechte Kopie: blass, das
Medaillon riesig, der Doppelring ueber den Rand hinaus. Und genau die
nimmt das Betriebssystem fuer installierte Apps.

Der erste Entwurf verkleinerte nur das LOGO (52 statt 68 Prozent) und
liess den Hintergrund unveraendert. Das geht bei einer glatten Flaeche
gut und bei allem anderen schief: Medaillon, Doppelring, Raster und
Eckband rechnen ihre Kreise in Prozent der FLAECHE. Wird aussen ein
Fuenftel weggeschnitten, sitzt der Ring nicht mehr, wo er hingehoert.

Jetzt wird das fertige Symbol als Ganzes auf 78 Prozent verkleinert und
in eine Huelle aus dem dunkelsten Ton der App gesetzt. Damit bleibt
jedes Verhaeltnis erhalten -- Ring zu Flaeche, Logo zu Ring, Verlauf zu
Kante. Weggeschnitten wird nur ruhige Randfarbe.

Nachgeprueft mit einer Vorschau, die den Zuschnitt nachstellt: rund
(Android) und abgerundetes Quadrat (Windows/iOS). Beide sehen jetzt
praktisch aus wie die Fassung im Browser.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 12:12:54 +02:00
DogFatherGitandClaude Opus 5 37c2930af0 Kundenportal war nicht installierbar: Manifest hinter der Zugangswand
Live gemessen nach dem Deploy: Symbole 200, portal.webmanifest 302 auf
zugang.html. Der Browser bekam HTML statt JSON und bot "App
installieren" gar nicht erst an -- die ganze Arbeit am Symbol waere
fuer das Portal wirkungslos geblieben, ohne dass irgendwo etwas rot
geworden waere.

Das ist zum DRITTEN Mal derselbe Fehler an derselben Stelle
(app.webmanifest, verwaltung.webmanifest, jetzt portal.webmanifest).
Zweimal stand die Begruendung danach im Code -- beim dritten Mal wurde
sie trotzdem uebersehen. Ein Kommentar verhindert nichts.

Deshalb zusaetzlich eine Pruefung in pruef-struktur.mjs: Jedes Manifest
unter /webdesign/ MUSS in der Ausnahmeliste von webdesign-gate.js
stehen. Gegengeprobt -- Eintrag entfernt, Pruefung schlaegt an.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 11:56:43 +02:00
DogFatherGitandClaude Opus 5 7fe6a51233 ZockerAnstalt und Analyse: Symbole unter den richtigen Namen
Der erste Entwurf legte sie unter eigenen Namen daneben
(dogfather-zocker-192.png). Die Dateien lagen da, die Manifeste zeigten
weiter auf icon-192.png -- das neue Symbol waere nie angekommen. Der
Kopiervorgang meldete trotzdem 6 Symbole kopiert.

Jetzt werden die vorhandenen Namen ueberschrieben. Damit greifen
Manifest und HTML ohne weitere Aenderung, und es gibt keine zweite
Stelle, an der ein Verweis uebersehen werden kann. Fehlt ein erwarteter
Name, wird das gemeldet statt still uebergangen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 11:48:17 +02:00
DogFatherGitandClaude Opus 5 3decdab2f2 Laufprotokoll der Browserpruefung gehoert nicht ins Repo
Es entsteht bei jedem Lauf neu und ist ein Ergebnis, kein Quelltext.
Beim vorigen Commit versehentlich mitgenommen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 11:42:27 +02:00
DogFatherGitandClaude Opus 5 51c3d4402b Audit des Creator Workspace, App-Symbole fuer alle Apps der Domain
AUDIT (Auftrag: vollstaendiger Durchgang, Fehler direkt beheben)

Ausgangslage waren 40 Pruefungen mit 1616 Einzelpunkten, alle gruen.
Acht neue Pruefungen kamen dazu; sie haben gefunden, was die alten nicht
sehen konnten.

Der schwerste Fund: Die Zugangsschranke verglich req.path EXAKT gegen
eine Liste. Express raeumt Punkt-Segmente selbst weg, mehrfache
Schraegstriche aber nicht. Damit kam //workspace/start.html OHNE
Anmeldung mit HTTP 200, und ein Creator bekam ueber
/workspace//personen.html die Verwaltungsseite. Die DATEN waren nie
betroffen (nachgemessen: 404 bzw. 401). Behoben durch Normalisierung
UND eine Umkehr der Logik -- jetzt ist jede .html geschuetzt ausser der
Anmeldeseite, statt nur die in der Liste. Eine vergessene neue Seite
steht damit nicht mehr versehentlich offen.

Weiter behoben:
  * Kaputter/abgebrochener Rumpf ergab 500 in HTML statt 400 in JSON --
    die Oberflaeche ruft ueberall a.json() und lief in einen zweiten
    Fehler; der Knopf hing ohne Meldung.
  * POST /zustand/sichern war der einzige von 60 schreibenden Wegen
    ohne Herkunftspruefung.
  * workspace-sicherung.js gab interne Pfade in Fehlermeldungen nach
    aussen; alle 23 anderen Module antworten neutral.
  * admin_notiz war als einziges von 14 Feldern ohne <label>.
  * Der aktive Filter hatte keinen sichtbaren Fokus (CSS-Spezifitaet
    0,3,0 schlug 0,2,0) -- genau der Knopf, auf dem man steht.
  * h1 -> h3 ohne Zwischenstufe auf zwei Seiten.
  * HSTS ging auch ueber http mit (RFC 6797, 7.2 verbietet das).
  * upgrade-insecure-requests galt auch auf 127.0.0.1 -- dadurch war
    WebKit/Safari ueberhaupt nicht pruefbar, also der Browser, den
    jedes iPhone benutzt.
  * pruef-grosscheck las readdirSync(".") und pruefte aus server/
    gestartet NULL oeffentliche Seiten -- meldete aber "ok".

Neue Pruefungen: struktur, schranke, haerte, alle-wege,
barrierefrei-workspace, breiten, tempo-workspace, browser.
Jede mit Gegenprobe und mit der geprueften Anzahl in der Bedingung.
Vier davon sind beim Bauen durch die eigene Gegenprobe aufgeflogen und
haetten sonst dauerhaft gruen gemeldet, ohne etwas zu messen.

APP-SYMBOLE (Wunsch: alle Apps der Domain, jede anders, ausser
safeaddress)

Zehn Apps, zehn Stile, zehn in OKLCH gerechnete Farben. Zusammen haelt
sie dasselbe Logo, dieselbe Eckenrundung und eine gemeinsame gedeckte
Farbreihe. Beim Bauen wird gemessen, ob sich das Logo vom Grund abhebt
(19 bis 58 Helligkeitsstufen).

Dabei aufgefallen: Das Kundenportal hatte kein eigenes Manifest und
trug Namen und Symbol der Webdesign-Seite. Der Workspace hatte gar
keins und war als App nicht installierbar. Beide haben jetzt eins.

Geaendert wurde AUSSCHLIESSLICH das Symbol. Ein Zwischenstand hatte
auch die Themenfarben gesetzt; das war mehr als bestellt und wurde
zurueckgenommen.

Werkzeuge: tools/logo-freistellen.mjs, tools/app-symbole.mjs,
tools/app-symbole-einbinden.mjs -- alles im Browser gerechnet, kein
Bildprogramm, keine neue Abhaengigkeit.

Gitea und Nextcloud sind bereits live und nachgeprueft.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-05 11:42:17 +02:00
Claudian b941080694 Die Namensliste ging beim Scrollen zu
FILIPE, mit Bildschirmfoto der Personenauswahl: "wenn ich da scollen will geht das immer zu"

Er hatte recht, und es war eine einzige Zeile in workspace/assets/js/wahl.js:

    addEventListener('scroll', schliessen, true);

Das dritte Argument bedeutet "in der Einfangphase lauschen" -- und damit auf JEDES
Scroll-Ereignis im ganzen Dokument, auch auf das in der Liste selbst. Wer die Namensliste
herunterrollen wollte, schloss sie im selben Moment. Bei acht Eintraegen faellt das kaum
auf; beim ganzen Manager- und Scout-Team ist die Liste unbedienbar.

Zumachen MUSS sie trotzdem, wenn die Seite scrollt: Sie haengt an position:fixed und
schwaemme sonst neben ihrem Knopf davon. Deshalb wird jetzt unterschieden, WO gescrollt
wurde -- ein Scroll-Ereignis der Seite hat document als Ziel, das ist nicht in der Liste
enthalten, also schliesst sie wie bisher.

Dazu overscroll-behavior: contain in gate.css. Ohne das reicht der Browser das Rollen am
Listenende an die Seite weiter, die Seite bewegt sich -- und daran schliesst die Liste zu
Recht. Der Fehler waere zur Haelfte zurueck gewesen.

GEPRUEFT im echten Browser am echten wahl.js und gate.css (pruef-wahl-scrollen.mjs, 7
Pruefungen), und zwar BEIDE Haelften der Zusage: Liste scrollt -> bleibt offen, Seite
scrollt -> geht zu. Eine Reparatur, die nur die eine Richtung sichert, waere die naechste
Ueberraschung.

Gegenprobe: alte Zeile testweise eingesetzt -> 2 Pruefungen fallen durch. Reparatur ->
alle 7 gruen.

Die Pruefung hat sich beim Bauen DREIMAL geweigert, etwas zu bestaetigen, das sie nicht
gemessen hatte -- "die Liste ist gar nicht laenger als ihr Fenster", "scrollTop = 0". Jedes
Mal lag es an meinem Aufbau (zu hohes Fenster, fehlende Stylesheets, Mausrad ohne Wirkung),
nie an der Reparatur. Gruen gemeldet haette sie es nie.

Der Versionsstempel muss mit: Cloudflare haelt JS und CSS vier Stunden im Browser fest.
Ohne neue Adresse saehe Filipe die Reparatur bis zu vier Stunden lang nicht.

HINWEIS ZUM COMMIT: In diesem Verzeichnis lag zum Zeitpunkt der Arbeit fremde,
uncommittete Arbeit -- vier Server-Dateien (heute 16:41-16:53 geaendert), sieben neue
Pruefdateien, ~110 Bildschirmfotos, eine Fokusring-Korrektur in gate.css und eine
Beschriftung in profil.html. Nichts davon ist hier drin. Bereitgestellt wurde ausdruecklich
nach Pfad, und die beiden Dateien mit gemischtem Inhalt (gate.css, profil.html) wurden dafuer
aus HEAD geholt und nur um den eigenen Anteil ergaenzt. Die fremde Arbeit steht unveraendert
im Arbeitsstand.
2026-09-04 19:18:02 +02:00
DogFatherGitandClaude Opus 5 15fe9bb197 Zwei Pruefungen an die getroffenen Entscheidungen angepasst
Der komplette Pruefstand vor der Vorstellung (38 Laeufe im Workspace)
lief bis auf zwei durch. Beide Fehlschlaege waren KEINE Fehler in der
Anwendung, sondern Pruefungen mit veraltetem Weltbild -- Folgen von
Entscheidungen, die Filipe selbst getroffen hat:

1. pruef-workspace-seiten (32 von 32 rot) pruefte, dass JEDE Seite ihre
   EIGENE Buehne hat (Aufgaben = Werkstatt, Kalender = Nachtstadt, ...).
   Am 03.09.2026 hat Filipe die neun Szenen durch EIN Bild ersetzt.
   Jetzt wird geprueft, was weiterhin wichtig ist: dass ueberhaupt ein
   Hintergrundbild ANKOMMT (ein data-buehne ohne CSS-Regel waere still
   wirkungslos) -- und dass es auf JEDER Seite dasselbe ist. Ohne die
   zweite Bedingung waere sie auch gruen, wenn irgendwo eine alte Szene
   zurueckkaeme.

2. pruef-zustand-ansicht erwartete, dass ein Manager den Systemzustand
   sieht. Seit dem 02.09.2026 gilt "die manager sollen diese kategorien
   garnicht sehen"; der Zustand gehoert zur Automationen-Seite.

Beide UMGEDREHT statt geloescht. Eine geloeschte Pruefung hinterlaesst
keine Spur davon, dass hier einmal etwas anderes galt -- und niemand
merkt, wenn eine Sperre spaeter versehentlich wieder faellt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-03 10:02:09 +02:00
DogFatherGitandClaude Opus 5 80639d0bab Abnahme vor der Vorstellung: eine tote Kachel gefunden und geschlossen
Morgen wird die Seite als Produkt vorgestellt. Deshalb eine Pruefung,
die es bisher nicht gab: server/pruef-live-abnahme.mjs geht gegen die
ECHTE Domain statt gegen einen Server auf diesem Rechner.

Der Unterschied ist nicht theoretisch. Zwischen "laeuft bei mir" und
"laeuft im Netz" liegen Caddy, Cloudflare, der Cache-Stempel, das
Zertifikat und der Dienst-Neustart -- genau dort ist am 18.08. und am
22.08.2026 schon zweimal etwas haengengeblieben, das lokal tadellos war.

Sie geht 26 Seiten in DREI Gestalten durch:
  RECHNER      1440 px
  HANDY        390 px, mit Fingerbedienung
  INSTALLIERT  als App vom Startbildschirm -- ohne Adresszeile und ohne
               Zurueck-Knopf des Browsers. Dort faellt auf, was man mit
               Browserleiste nie merkt.

Gemessen je Seite: Konsolenfehler, fehlgeschlagene Anfragen, seitlicher
Ueberlauf, tote Verweise, fehlende Bilder, ueberhaupt Inhalt, Titel.
Dazu die App selbst: Manifest, Name, Start ohne Browserleiste, alle vier
Startbildschirm-Symbole einzeln abgefragt, Hintergrunddienst angemeldet
und aktiv.

DER EINE ECHTE FUND: Auf links.html war die Kachel "Merchandise / Shop"
ein <a href="#"> -- sie sah aus wie ein Verweis, liess sich anklicken
und tat nichts. Auf der Kachel steht "Folgt in Kuerze"; genau dann darf
sie nicht klickbar sein. Ein Knopf, der nichts tut, ist schlimmer als
gar keiner: Man drueckt ihn zweimal und haelt die Seite fuer kaputt.
Jetzt ein <div> mit denselben Klassen -- gleiches Aussehen, kein
Versprechen mehr, das die Seite nicht halten kann.

ZWEI FEHLALARME, beide in MEINER Pruefung, beide aufgeschrieben:
  * Sie meldete "fehlende Bilder" auf zwei Seiten. Es waren versteckte
    Platzhalter (<img hidden> ohne src), die JavaScript erst fuellt --
    der Normalzustand. Jetzt zaehlen nur sichtbare Bilder mit Quelle.
  * Sie meldete "manifest.json nicht ladbar", waehrend curl gleichzeitig
    200 lieferte. Die Probe stand noch auf about:blank und holte von
    dort -- fremde Herkunft, der Browser lehnt ab. Sie hat also nicht
    die Seite geprueft, sondern sich selbst. Erst navigieren, dann
    messen.

Die beiden anderen href="#" auf der Seite (index.html, streamplan.html)
bleiben: Sie sind versteckt und werden von JavaScript gefuellt, sobald
es etwas zu verlinken gibt. Geprueft wird nur, was man auch sieht.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-03 09:48:38 +02:00
DogFatherGitandClaude Opus 5 95f54fd211 Die ganze Oberflaeche spricht jetzt dieselbe Sprache wie die Zeichen
"bring alles auf dieses niveau -- texte, kacheln, hinweise, titel,
buttons, einfach alles."

Berechtigt, und der Fehler war meiner: Die Kachelzeichen sind Koerper
geworden, alles andere blieb flach. Ein plastisches Zeichen neben einem
gemalten Knopf sieht nicht nach "teilweise gut" aus, sondern nach
Versehen.

Ab jetzt gelten fuer JEDES Bauteil dieselben drei Regeln -- dieselben,
nach denen auch die Zeichen gebaut sind:

  1. LICHT VON OBEN. Eine helle Kante an der Oberkante, in der Mitte am
     staerksten, nach aussen auslaufend. Echtes Licht auf einer Kante
     sieht so aus; eine durchgehend gleich helle Linie ist ein Strich.
  2. TIEFE NACH UNTEN. Ein versetzter Schatten -- wie bei jedem
     Gegenstand, der auf etwas liegt. Ohne ihn klebt ein Element auf der
     Seite, statt darauf zu liegen.
  3. VERLAUF STATT FLAECHE. Oben eine Spur heller als unten. Kaum zu
     sehen, aber ohne ihn bleibt jede Flaeche tot.

Und eine vierte, nur fuer Bedienbares:
  4. WAS MAN DRUECKT, GEHT HINEIN. Beim Klick kehrt sich die Woelbung um:
     Licht nach unten, Schatten nach oben. Das ist der Unterschied
     zwischen "es passiert etwas" und "ich habe etwas gedrueckt".

ANGEFASST -- beide Seiten, nicht nur eine:

  Workspace (gate.css, gilt auf jeder Seite)
    Knoepfe, Schritte, Abmelden, Stufenknoepfe, Suchknopf
    Eingabefelder, Auswahlfelder, Suchfeld -- die bekommen die Woelbung
      ABSICHTLICH ANDERSHERUM: Ein Feld, in das man schreibt, ist eine
      Mulde, kein Knopf. Licht unten, Schatten oben.
    Marken, Rollenabzeichen, Zaehler, Filterchips
    Hinweisflaechen, Notizen, Call-Raum, Codekasten
    Ueberschriften und Schilder

  Oeffentliche Seite (main.css)
    Knoepfe (btn, btn-primary, btn-outline) samt geschliffener Kante
    Marken und Abzeichen
    Ueberschriften

Der Textschatten aendert die FARBE nicht -- die Kontrastpruefung misst
weiter denselben Wert --, legt den Text aber auf die Seite, statt ihn in
den Hintergrund zu mischen.

Alles gedeckt. Diese Bauteile stehen zu Dutzenden auf einer Seite, und
was einzeln beeindruckt, blendet im Dutzend.

GEPRUEFT, zehn Laeufe, alle gruen: Buehne 38 (Textkontrast an echten
Bildpunkten), Rollen 97, Handy 50, Formulare 19, Grosscheck 15,
Lesbarkeit 14 -- dazu die vier Laeufe der oeffentlichen Seite
(Startseite, Design, Barrierefreiheit, Tastaturbedienung).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-03 07:20:47 +02:00
DogFatherGitandClaude Opus 5 788a5fbd81 Die Zeichen sind jetzt KOERPER, keine Zeichnungen mehr
"wir kommen nicht voran, ich will das viel realistischer."

Berechtigt: Ich habe viermal dieselbe flache Zeichnung anders
BELEUCHTET -- Verlauf, Fuellung, Glanz, Schlagschatten. Das Verfahren
war das Problem, nicht die Einstellungen. Also gewechselt.

JEDES ZEICHEN HAT JETZT EINE HOEHE. Aufbau von hinten nach vorn:

  tiefe 3/2/1   dieselbe Silhouette, dreimal, je einen halben
                Rasterpunkt nach rechts unten versetzt und heller
                werdend. Das Auge liest die drei versetzten Kanten als
                EINE schraege Seitenwand -- genau so zeichnet man einen
                Quader von Hand. Drei Lagen sind gemessen: bei zwei
                sieht es aus wie ein Druckfehler, ab fuenf wie ein
                Schlagschatten.
  deck          die Oberseite: oben fast weiss, unten im Farbton. Die
                Flaeche, auf die das Licht faellt.
  glanz         die Spiegelung darauf.
  saum + linie  die Details.

Dafuer bekam jedes Zeichen eine SILHOUETTE (KOERPER in bereiche.js) --
den geschlossenen Umriss des Gegenstands, getrennt von den Details.
Die Details bekommen bewusst KEINE Tiefe: Ein aufgedruckter Strich
steht nicht hervor.

DER SAUM ist die Loesung eines Problems, das erst durch die Tiefe
entstand: Dieselbe Linie liegt ueber ZWEI Untergruenden. Der Querstrich
im Kalender liegt auf der hellen Deckflaeche, die Wellen der
LIVE-Analyse frei auf der dunklen Plakette. Eine dunkle Linie
verschwindet dort, eine helle auf dem Deck -- was immer man waehlt, die
Haelfte ist weg. Dunkler Saum plus helle Linie loest beides: auf dem
Deck liest man eine eingravierte Rille, auf der Plakette traegt die
helle Linie. Ein Zeichen, zwei Untergruende, eine Loesung.

GEPRUEFT: Buehne 38 (Textkontrast an echten Bildpunkten), Startansicht
133, Rollen 97, Handy 50, Grosscheck 15, Lesbarkeit 14. Alle gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-03 02:43:45 +02:00
DogFatherGitandClaude Opus 5 44716fa54b Aus Zeichnungen werden Gegenstaende -- Kacheln der GANZEN Seite
"ich will dass die krank realistisch sind, und alle kacheln durch die
ganze website perfektionieren."

DIE ZEICHEN bekommen die Beleuchtung, die man aus der Wirklichkeit
kennt -- vier Lagen statt einer:

  KOERPER        gefuellte Grundform, oben satt, unten auslaufend
  EIGENSCHATTEN  eine dunkle Lage, die von unten hereinkriecht. Ein
                 Koerper ist unten dunkler als oben; ohne das bleibt
                 jede Flaeche eine Farbflaeche
  GLANZ          die Spiegelung, schmal ueber die obere Haelfte
  KONTUR         mit ZWEI Lichtquellen: oben das Hauptlicht (fast weiss),
                 in der Mitte der Eigenton, unten STREULICHT vom
                 Untergrund. Der letzte Stopp ist der Unterschied
                 zwischen "Zeichnung" und "Ding" -- ohne ihn laeuft jede
                 Form nach unten ins Dunkle aus.

Dazu ein SCHLAGSCHATTEN auf die Plakette, versetzt nach unten statt
mittig. Er ist der Grund, warum das Zeichen ueber der Flaeche schwebt
statt darauf zu liegen.

DIE PLAKETTE bekommt eine KOERNUNG -- eine sehr feine, unregelmaessige
Struktur. Das ist der Unterschied zwischen "am Rechner gemacht" und
"Gegenstand": Eine makellos glatte Farbflaeche gibt es in der
Wirklichkeit nicht, und das Auge erkennt das sofort, auch wenn niemand
sagen koennte woran. Eingebettetes Rauschen, keine Bilddatei -- kostet
nichts zu laden und kann nicht fehlen.

DIE KACHELN, und zwar BEIDE Systeme:

  workspace  .kachel  -- bekam Tiefe nach unten (fehlte ganz: die Kachel
             klebte auf dem Hintergrund statt darauf zu liegen) und eine
             Lichtkante, die in der Mitte am hellsten ist und nach
             aussen auslaeuft. Echtes Licht auf einer Kante sieht so
             aus; eine durchgehend gleich helle Linie ist ein Strich.

  oeffentlich .card (133 Vorkommen auf der Website) -- war eine Flaeche
             mit einem Rand. Jetzt: Verlauf statt Flaeche, Lichtkante
             oben, Schatten nach unten. Beim Ueberfahren hebt sie sich,
             der Schatten wird laenger, die Kante heller.

Alles bleibt gedeckt. Diese Kacheln stehen zu Dutzenden auf einer Seite
-- was einzeln beeindruckt, blendet im Dutzend.

GEPRUEFT: Buehne 38 (Textkontrast an echten Bildpunkten), Startansicht
133, Rollen 97, Handy 50, Grosscheck 15, Lesbarkeit 14, dazu die drei
Laeufe der oeffentlichen Seite (Startseite, Design, Barrierefreiheit).
Alle gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-03 02:21:34 +02:00
DogFatherGitandClaude Opus 5 46e62fbaf7 Die Kachelzeichen komplett neu -- Plaketten statt getoenter Quadrate
Rueckmeldung: "ich will dass die viel krasser aussehen, du veraenderst
immer nur minimal." Berechtigt. Drei Durchgaenge lang habe ich an
Details gedreht (Verlauf, dann Fuellung) -- jeder fuer sich richtig, in
der Summe kaum sichtbar. Das hier ist der Umbau.

DIE PLAKETTE war ein leicht getoentes Quadrat mit duennem Rand. Sie ist
jetzt ein KOERPER:
  * 58 statt 52 px (gross: 70 statt 62)
  * Verlauf ueber die Diagonale statt flacher Toenung
  * Lichtkante oben INNEN, Schattenkante unten innen -- zusammen eine
    Woelbung, das Feld wirkt gepraegt statt gemalt
  * ein Hof in der eigenen Farbe darunter
  * ein Glanzbogen darueber, der von links oben einfaellt

DAS ZEICHEN war eine duenne Kontur. Es besteht jetzt aus DREI Lagen mit
je eigenem Verlauf:
  KOERPER   gefuellte Grundform, oben satt, unten fast weg -- eine
            gleichmaessig gefuellte Form ist ein Aufkleber, eine
            auslaufende ist ein Koerper
  GLANZ     schmaler heller Streifen quer ueber die obere Haelfte,
            genau auf dem Koerper. Die Spiegelung.
  KONTUR    oben fast WEISS, unten im Farbton. So sieht Metall aus, auf
            das Licht von oben faellt -- das ist der Grund, warum die
            Zeichen jetzt plastisch wirken statt gezeichnet.
Dazu 29 statt 25 px und Strichstaerke 2,05 statt 1,65: Die Zeichen
sollen TRAGEN, nicht andeuten. Zaghaft war genau das Problem.

Alle drei Verlaeufe stehen EINMAL im Dokument und arbeiten mit
currentColor -- sie nehmen den Ton jeder der siebzehn Kacheln an. Drei
Verlaeufe statt einundfuenfzig.

Alles bleibt gedeckt: Ein leuchtender Kasten waere in einer dunklen
Oberflaeche eine Lampe, und Lampen schaut man nicht stundenlang an.
Im Wasserzeichen hinter dem Kacheltext und im Kontrastmodus bleiben
Koerper und Glanz aus.

GEPRUEFT: Buehne 38 (Textkontrast an echten Bildpunkten -- die
kraeftigeren Plaketten duerfen die Lesbarkeit nicht antasten),
Startansicht 133, Rollen 97, Handy 50, Grosscheck 15, Lesbarkeit 14.
Alle gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-03 01:59:59 +02:00
DogFatherGitandClaude Opus 5 7d77f3ea6b Die Zeichen bekommen eine Flaeche -- aus Konturen werden Piktogramme
"ich will die symbole in den kacheln noch viel krasser und geiler."

Bis eben war jedes Zeichen eine reine Kontur. Sauber, aber neutral --
siebzehn gleich starke Umrisse nebeneinander, wie aus jedem
Symbolbaukasten.

Jetzt besteht jedes aus ZWEI Teilen:

  FUELLUNG   eine gefuellte Grundform, gedeckt hinterlegt
  LINIE      die scharfe Zeichnung darueber, mit dem Verlauf von gestern

Das ist der Unterschied zwischen einem Symbol und einem Piktogramm.
Sobald ein Teil FLAECHE hat, bekommt das Zeichen ein Vorn und ein
Hinten, und das Auge erkennt es, ohne es zu lesen.

Gefuellt wird immer das, WORUM ES GEHT -- nie alles:

  Kalender    der Kopf des Blattes (daran erkennt man ihn aus drei Metern)
  Aufgaben    die mittlere Spalte -- "in Arbeit", dort passiert etwas
  Dashboard   zwei der vier Felder ueber Eck, das gibt Rhythmus
  Ordner      der Korpus ohne die Lasche, damit die Stufe sichtbar bleibt
  Berichte    aus drei Strichen werden drei SAEULEN (ein Balkendiagramm
              hat Balken)
  Start-Check nur der Haken -- ein gefuelltes Klemmbrett waere ein Kasten
  Personen    der Kopf; bei zwei Personen nur der VORDERE, daraus
              entsteht die Tiefe
  Buch        die linke Seite -- eine im Licht, eine im Schatten
  Technik     die drei Griffe
  LIVE        der Sender in der Mitte; gefuellte Wellen saehen aus wie
              ein Auge
  Trichter    nur der obere Teil, sonst kippt das Zeichen nach unten

20 % Deckkraft sind gemessen, nicht geraten: darueber wird das Zeichen
zum Fleck und die Linie darin unsichtbar, darunter sieht man die Flaeche
gar nicht. Beim Ueberfahren geht sie auf 30 %, als kaeme Licht dazu.

WARUM NICHT EINFACH DICKER -- der Unterschied zum gescheiterten Versuch
von gestern: Der legte eine dicke, WEICHGEZEICHNETE KOPIE DER LINIE
darunter und hat damit jede Luecke zugeschmiert (aus dem Kalender wurde
ein leerer Kasten). Eine Flaeche ist etwas anderes als ein aufgeblasener
Strich: Sie liegt INNERHALB der Kontur und laesst die Zwischenraeume
unberuehrt. Genau deshalb funktioniert es jetzt.

Im Wasserzeichen und im Kontrastmodus bleibt die Flaeche aus -- dort
wuerde sie den Text hinterlegen bzw. zu einem massiven Block werden.

GEPRUEFT: Buehne 38 (Textkontrast an echten Bildpunkten -- die Flaeche
darf die Lesbarkeit nicht antasten), Startansicht 133, Rollen 97,
Handy 50, Team 30, Grosscheck 15, Lesbarkeit 14. Alle gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-03 01:41:16 +02:00
DogFatherGitandClaude Opus 5 a9815e291a Ein Hintergrund fuer alles -- und Zeichen, die endlich vollstaendig sind
ZWEI AUFTRAEGE.

1. DER NEUE HINTERGRUND
   Das Dogfather/Spicy-Media-Bild loest die neun Buehnen ab. Der Login
   behaelt ausdruecklich sein eigenes Bild (body.gate, unangetastet) --
   dort wird der Zugangscode eingegeben, und genau das sollte bleiben.

   Nicht einfach hingelegt: Das Bild ist sehr kraeftig, vor allem die
   rote Haelfte. Ohne Behandlung fiel der Textkontrast auf 3,03:1
   (noetig sind 4,5:1) -- ausgerechnet in der Personenverwaltung und auf
   dem Aufgabenbrett. Gemessen hat das server/pruef-buehne.mjs, das den
   Kontrast an bis zu 36 echten Bildpunkten je Seite nachrechnet.

   In vier Schritten angepasst und jedes Mal nachgemessen:
     -0,14 Helligkeit -> 4,12:1   (immer noch zu wenig)
     -0,20 Helligkeit -> 4,48:1   (zwei Hundertstel zu wenig)
     -0,23 Helligkeit -> 4,63:1   bestanden, alle Seiten
   Dazu Saettigung 0,72 und Gamma 0,91 -- dieselbe Behandlung, die auch
   die alten Buehnen bekommen haben (tools/buehne-bauen.mjs arbeitet mit
   Helligkeit 0,66 bis 0,88). Das Bild bleibt ein Bild und wird nicht
   zur Tapete, aber Text steht darauf lesbar.

2. DIE ZEICHEN
   Sie bekommen einen VERLAUF: oben hell, nach unten gedaempft -- eine
   Lichtquelle ueber dem Zeichen, wie in der echten Welt. Dazu ein
   weicher Schlagschatten in der eigenen Farbe. Der Verlauf ist EINMAL
   definiert und arbeitet mit currentColor: ein Verlauf fuer siebzehn
   Kachelfarben.

   ZWEI SACKGASSEN AUF DEM WEG, beide aufgeschrieben statt weggeraeumt:

   a) Erst lag unter jeder Linie eine dicke, weichgezeichnete Kopie --
      "Licht, das die Linie wirft". Bei einem grossen Symbol traegt das.
      Hier nicht: Ein Zeichen ist 24 Einheiten breit und 25 px gross,
      eine Einheit ist also ein Pixel. Linie 1,65 plus Schein 2,5 fuellt
      jede Luecke, die enger als vier Einheiten ist -- aus dem Kalender
      wurde ein leerer Kasten. Gesehen habe ich das erst bei dreifacher
      Vergroesserung; auf dem normalen Schirm sah es nur "satter" aus.
      Die Lage ist wieder weg.

   b) Der Verlauf lief zunaechst in OBJEKTKOORDINATEN. Damit wird er auf
      den Umriss jedes einzelnen Pfades gerechnet -- und eine waagerechte
      Linie hat die Hoehe null. Der Verlauf ist dann entartet, und der
      Browser zeichnet den Pfad GAR NICHT. Verschwunden waren dadurch:
      die Querlinie im Kalender, alle drei Regler-Striche in Technik,
      die Grundlinie der Berichte. Jetzt laeuft er in Benutzer-
      koordinaten ueber die festen 24 Einheiten -- was ohnehin richtiger
      ist, denn das Licht kommt von oben und nicht von jedem Strich
      einzeln.

   AUSSERDEM ECHT REPARIERT: Das Technik-Zeichen hatte drei Griffe aus
   Boegen der Laenge null ("a1.4 1.4 0 1 0 0-.02z") -- sie wurden nie
   gezeichnet. Uebrig blieben drei nackte Striche, die aussahen wie ein
   Menue-Symbol. Jetzt sind es echte Kreise. Kalender und Dashboard haben
   mehr Luft zwischen ihren Linien bekommen, damit sie bei 25 px nicht
   zu einer Flaeche verschmelzen.

GEPRUEFT: Buehne 38 (Kontrast an echten Bildpunkten), Startansicht 133,
Rollen 97, Handy 50, Grosscheck 15, Lesbarkeit 14 -- alle gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-03 01:26:52 +02:00
DogFatherGitandClaude Opus 5 0404f8c0c1 Dreizehn Lecks: eine Managerin sah die Creator aller anderen
Wunsch: "jeder manager soll auch immer nur seine und die seiner scouts
zugeteilten creator und creator daten sehen. und nicht die der anderen."

DIE KETTE WAR GEBAUT -- SIE WURDE NUR NICHT BENUTZT
Manager -> seine Scouts -> deren Creator steht seit dem 01.09.2026 an
genau einer Stelle (betreuteIds). Die Frage war eine andere: Benutzt sie
auch JEDER Weg, der Creator-Daten herausgibt? Ueber zwanzig Stellen
prueften die ROLLE statt der ZUTEILUNG -- "ist Leitung? dann alles".

NICHT GELESEN, SONDERN GEMESSEN
server/pruef-manager-sicht.mjs baut zwei Managerinnen mit vollstaendig
getrennten Creators. Bei der fremden heisst ALLES "GEHEIM..." -- Aufgabe,
Termin, Bereichseintrag, Datei, Steckbrief, Content-Saeule. Danach wird
jede der 31 Leseschnittstellen abgefragt und die ganze Antwort danach
durchsucht. Ein Leck faellt damit auf, egal wo es sitzt und egal, ob ich
es beim Lesen uebersehen haette.

GEFUNDEN: DREIZEHN. Alle geschlossen:

  Kalender          fremde Fristen -- besonders unangenehm, weil es
                    nicht wie ein Leck aussieht: eine kleine orange
                    Marke mit einem Titel, in dem fremde Vorhaben stehen
  Personenauswahl   alle Namen im Zuweisungsfeld
  Dateien           alle Namen in der Freigabe-Auswahl
  Uebersicht        Gesamtuebersicht ueber ALLE Creator
  Report            Auswahl UND Auswertung ueber den ganzen Bestand
  Start-Check       alle Creator zur Auswahl
  Steckbriefe       Bild, Kanaele, "ueber mich" von allen
  Profile           alle Profile, samt interner Notiz
  Schulung          Schulungsstand aller Creator
  Suche             Creator-Profile aller -- die unauffaelligste Stelle:
                    Man sucht etwas anderes und bekommt fremde Namen
  Content-Balance   Themensaeulen fremder Kanaele (die Abfrage daneben
                    war korrekt eingeschraenkt, DIESE hatte eine eigene
                    Bedingung)
  darfCreator       eine einzige Zeile -- sie hing an Profil,
                    Start-Check und Uebersicht gleichzeitig. Es reichte,
                    eine Nummer in die Adresse zu schreiben.

Die Antwort steht jetzt an EINER Stelle: sichtbareCreatorIds und
sichtbarePersonenIds in workspace.js. Rueckgabe null heisst "alle" und
gilt allein DogFather -- bewusst kein leeres Feld: Eine leere Liste
bedeutet "niemand", und die Verwechslung der beiden macht aus einer
Sperre eine Freigabe.

ZWEI DINGE, DIE ICH MIR SELBST NACHTRAGEN MUSS

1. Beim Stopfen fehlte einmal ein Import. Der Weg warf einen Fehler,
   antwortete 503 -- und weil in einer Fehlermeldung kein "GEHEIM" steht,
   meldete die Pruefung "kein Leck". Sie war gruen, weil der Weg KAPUTT
   war. Die Pruefung zaehlt jetzt beides: nichts durchsickern UND
   antworten.

2. Ein Fehlalarm: Die Suche gibt den SUCHBEGRIFF in ihrer Antwort
   zurueck. Wer nach "GEHEIM" sucht, findet das Wort zwangslaeufig --
   auch bei null Treffern. Ich haette um ein Haar ein Leck "repariert",
   das es nie gab. Das Echo wird jetzt entfernt, bevor gemessen wird.

FOLGEN, bewusst in Kauf genommen:
  * Ein Manager ohne Zuteilung sieht keinen Creator. Die Uebersicht sagt
    ihm das jetzt in einem Satz, statt leer zu bleiben.
  * Er kann nur noch IN SEINEN Creator-Bereichen schreiben (darfCreator).

ZWEI PRUEFUNGEN UMGEDREHT statt geloescht -- eine geloeschte Pruefung
hinterlaesst keine Spur davon, dass hier einmal etwas anderes galt:
pruef-uebersicht ("Manager sieht dasselbe" -> "nur seine zugeteilten",
mit beiden Faellen) und pruef-scout-zuteilung ("sieht die ganze
Personenliste" -> "landet auf der Startseite").

GEPRUEFT: 43 neue Pruefungen, dazu 23 bestehende Laeufe gruen --
Startansicht 133, Rollen 97, Kalender 84, Serien 67, Steckbrief 65,
Handy 50, Sicht 48, Ampel 47, Content 45, Aufgabenbrett 44, Schulung 41,
Bereiche 37, Scout-Zuteilung 36, Uebersicht 35, Personenliste 33,
Freie Namen 32, Team 30, Protokoll-Loeschen 20, Personenformular 20,
Formulare 19, Betreuung 18, Code 17, Grosscheck 15.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-03 01:01:00 +02:00
DogFatherGitandClaude Opus 5 d8720debc1 Personen & Automationen gehoeren DogFather allein -- und das Formular laeuft nicht mehr ueber
ZWEI MELDUNGEN.

1. "DAS SIEHT JA MAL RICHTIG SCHEISSE AUS"
   Die Rollenauswahl im Formular "Neue Person" lief unten aus dem
   Formular heraus und legte sich ueber die Personenliste darunter.

   Die Ursache war keine schlampige Gestaltung, sondern eine Regel, die
   fuer etwas anderes gemacht ist: `.neu__raster > *` (aufgaben.css) gibt
   JEDEM direkten Kind ein Raster mit fester Zeilenhoehe von 44 px, damit
   Beschriftung und Eingabefeld in allen Spalten auf einer Linie sitzen.
   Fuer ein einzeiliges Feld ist das genau richtig. Die Rollenauswahl ist
   aber kein Feld, sondern ein Block aus vier hohen Karten -- die passten
   nicht in 44 px, und was nicht hineinpasst, steht eben daneben.

   Das Formular ist jetzt aus dem Spaltenraster geloest und liest sich
   von oben nach unten: NAME (schmal, ein Name braucht keine 1200 px),
   ROLLE (volle Breite, vier gleich breite Karten), Knoepfe. Das ist
   nicht nur reparierter Ueberlauf, sondern auch die Reihenfolge, in der
   man die Sache tatsaechlich entscheidet.

   Dazu zwei Kleinigkeiten, die den Unterschied machen: Das Namensfeld
   war ohne Rasterzeile 60 px hoch geworden (`height: 100%` einer Zeile,
   die es nicht mehr gibt) -- hoeher als die Knoepfe daneben, was nach
   Versehen aussieht, weil es eins war. Und die gewaehlte Rolle traegt
   jetzt ein HAEKCHEN, nicht nur einen etwas helleren Rahmen: Genau die
   Art Unterschied, die im Kontrastmodus verschwindet und fuer
   farbunsichere Augen nie existiert hat.

2. "DIE MANAGER SOLLEN DIESE KATEGORIEN GARNICHT SEHEN"
   "Personen & Zugaenge" und "Automationen" gehoeren ab sofort allein
   DogFather.

   Bei den Personen war es ohnehin ein Widerspruch im eigenen Haus: In
   der Rollenauswahl steht seit jeher "Manager -- dieselben Rechte wie
   DogFather, AUSSER der Personenverwaltung". Die Kachel stand trotzdem
   bei ihm, und die Schranke liess ihn hinein: Codes erneuern, sperren,
   Zuteilungen aendern. Eine Beschreibung, die etwas anderes sagt als die
   Software, ist schlimmer als beides einzeln -- man weiss danach nicht
   mehr, welcher von beiden man glauben soll.

   Umgesetzt auf DREI Ebenen, weil Wegnehmen was man SIEHT kein
   Rechteentzug ist:
     1. die Kachel erscheint nicht mehr
     2. die Seite leitet zur Startseite zurueck (GESCHUETZT)
     3. die Schnittstellen antworten mit 404 -- Personenverwaltung,
        Systemzustand, KI-Schalter

   FOLGE, die bewusst in Kauf genommen wird: Die Betreuungs-Zuteilung
   liegt auf der Personenseite. Ab jetzt kann also NUR DogFather
   festlegen, wer welchen Creator betreut.

GEPRUEFT: server/pruef-personen-formular.mjs, 20 Pruefungen. Das Aussehen
wird gemessen, nicht angesehen: Endet die Rollenauswahl innerhalb des
Formulars? Ragt eine Karte seitlich hinaus? Liegt etwas ueber der Liste?
Sind alle vier Karten gleich breit (272-272 px)? Traegt die gewaehlte ein
Haekchen? Dazu zwei Gegenproben: DogFather kommt weiterhin an beide
Bereiche (sonst waere jede Sperr-Zeile auch bei einem fuer ALLE kaputten
Bereich gruen), und die Managerin arbeitet ansonsten unveraendert weiter.
Bestehende Laeufe gruen: Rollen 97, Handy 50, Sicherung 41, Personenliste
33, Team 30, Formulare 19, Code 17, Grosscheck 15.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-03 00:00:41 +02:00
DogFatherGitandClaude Opus 5 e7b277b7fc Die Startseite log nicht -- sie war nur alt. Und Protokolle lassen sich loeschen
Drei Meldungen aus einer Nachricht.

1. "WIRD IMMER NOCH ANGEZEIGT"
   Ein Protokoll war geschrieben, die Startseite meldete trotzdem weiter
   "1 Gespraech hat noch kein Protokoll".

   NACHGESTELLT statt vermutet: Der Server lag die ganze Zeit richtig.
   Der Hinweis erscheint, sobald ein vergangenes Gespraech kein Protokoll
   hat, und verschwindet in dem Moment, in dem eines geschrieben ist --
   nachgemessen, beides.

   Falsch war der BILDSCHIRM. Browser legen eine verlassene Seite
   vollstaendig beiseite (bfcache) und holen sie beim Zurueckgehen
   unveraendert hervor, mitsamt allen Zahlen vom ersten Laden. Kein
   Skript laeuft dabei erneut. Wer ein Protokoll schreibt und dann auf
   "Zurueck" tippt, sieht zwangslaeufig den Stand von vorher.

   Das ist kein Schoenheitsfehler: Eine Zahl, die etwas Falsches
   behauptet, ist schlimmer als gar keine -- man glaubt ihr ja. Und sie
   kostet danach Vertrauen in ALLE Zahlen.

   kopf.js laedt eine zurueckgeholte Seite jetzt neu. Nur dann
   (`event.persisted`), nicht bei jedem Anzeigen -- sonst waere es eine
   Endlosschleife. Gilt fuer jede Workspace-Seite, nicht nur die
   Startseite.

2. PROTOKOLLE LOESCHEN -- NUR DOGFATHER
   Bewusst istDogFather und nicht istLeitung: Ein Manager hat sonst
   ueberall dieselben Rechte, hier ausdruecklich nicht. Wer ein Protokoll
   entfernen darf, kann nachtraeglich bestimmen, was besprochen wurde.

   Geloescht wird NUR das Protokoll. Das Gespraech bleibt im Kalender und
   rutscht wieder zu "Protokoll fehlt" -- die Handlung ist damit
   umkehrbar: neu schreiben, fertig. Die daraus entstandenen AUFGABEN
   bleiben ebenfalls stehen; sie sind echte Arbeit, die jemand uebernommen
   hat, und mit einem Klick auf ein Protokoll zu verschwinden waere ein
   stiller Datenverlust an ganz anderer Stelle.

   Der Knopf steht nur bei DogFather. Ein Knopf, der bei anderen
   erscheint und dann abgewiesen wird, ist eine Einladung zum Aergernis.

3. IMMER NUR EINS OFFEN
   Vorher liessen sich beliebig viele Protokolle gleichzeitig aufklappen
   -- die Seite wurde so lang, dass die Liste darunter aus dem Blick
   geriet. Ein neu geoeffnetes Gespraech schliesst jetzt das vorherige.
   Beim Laden ist alles zu.

GEPRUEFT: server/pruef-protokoll-loeschen.mjs, 20 Pruefungen. Die
Zurueck-Pruefung misst an EINZAHL gegen MEHRZAHL ("1 Gespraech HAT" gegen
"2 Gespraeche HABEN") -- die Zahl selbst steht in einem eigenen Feld und
taucht im Fliesstext nicht auf. Drei Gegenproben: dass der Hinweis nach
dem Reparieren ueberhaupt noch anschlaegt (sonst waere "verschwunden"
auch bei kaputtem Hinweis gruen), dass eine Managerin 403 bekommt und das
Protokoll danach unveraendert dasteht, und dass der Loeschknopf bei ihr
gar nicht erst gezeichnet wird. Bestehende Laeufe gruen: Startansicht 133,
Rollen 97, Kalender 84, Serien 67, Protokoll-Klappe 52, Handy 50, Ampel
47, Freie Namen 32, Code 17.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-02 23:45:45 +02:00
DogFatherGitandClaude Opus 5 fc26597083 Wer sich einen neuen Code gibt, sperrt sich nicht mehr selbst aus
Gemeldet: "ich hab einen neuen code fuer mich gemacht aber ich wurde raus
gekickt bevor ich den neuen code kopieren konnte jetzt komm ich nicht mehr
rein."

DER FEHLER
Ein neuer Code beendete ALLE Sitzungen der Person -- auch die, in der der
Code gerade auf dem Bildschirm stand. Der naechste Aufruf der Seite lief in
ein "nicht angemeldet", die Umleitung zur Anmeldung nahm den Kasten samt
Code mit, und der Code ist nirgends noch einmal abrufbar (in der Datenbank
steht nur seine Pruefsumme). Ergebnis: ausgesperrt, Rueckweg nur ueber
einen Eingriff auf dem Server.

Bitter daran: Die Rueckfrage sagte das sogar an ("Deine aktuelle Sitzung
wird sofort beendet") -- als Warnung formuliert, nicht als Fehler erkannt.
Ein Ablauf, den man nur mit gutem Timing ueberlebt, ist keiner.

DREI RIEGEL, NICHT EINER

1. DIE EIGENE SITZUNG BLEIBT. codeNeu() beendet weiterhin alle Sitzungen
   der Person -- ausser der einen, aus der heraus der Code gerade erneuert
   wird. Sicherheitlich kostet das nichts: Wer den Code tauscht, hat sich
   mit genau dieser Sitzung soeben ausgewiesen und haelt sie in Haenden.
   Alle ANDEREN Geraete fliegen weiterhin hinaus -- das ist der Sinn der
   Uebung und wird eigens geprueft.

2. DER KASTEN BLEIBT STEHEN. Solange ein frischer Code angezeigt wird,
   springt die Personenseite nicht mehr von selbst zur Anmeldung. Selbst
   wenn eine Sitzung aus einem anderen Grund endet (Zeitablauf, Neustart),
   bleibt der Code lesbar, bis er weggeklickt ist -- mit einem Hinweis,
   ihn jetzt abzuschreiben. Ein Code, den man nicht mehr lesen kann, ist
   schlimmer als gar keiner.

3. EINE TUER VON AUSSEN: tools/notfall-code.sh. Setzt auf dem Server einen
   neuen Code, zeigt ihn an und loescht die Sperre nach acht Fehlversuchen
   gleich mit. Weigert sich, als root zu laufen (sonst gehoerten die
   Hilfsdateien der Datenbank danach root und der Dienst koennte nicht
   mehr schreiben). Dokumentiert als "Fall 0" in WIEDERHERSTELLUNG.md --
   dem ersten Fall, den man aufschlaegt, wenn nichts kaputt ist ausser
   dem Zugang.

   KEIN fest hinterlegter Notfall-Code in der Anwendung: Der waere eine
   Hintertuer, die dauerhaft offensteht, fuer jeden der sie findet, ohne
   dass es auffiele. Dieser Weg verlangt Zugang zum Server, hinterlaesst
   einen Protokolleintrag, und es gibt nichts zu erraten.

Die Rueckfrage sagt jetzt, was wirklich passiert, statt vor etwas zu
warnen, das nicht mehr eintritt.

GEPRUEFT: server/pruef-code.mjs, 17 Pruefungen -- darunter die Gegenprobe,
dass das ZWEITE Geraet derselben Person sehr wohl abgemeldet wird (ohne sie
waere die Reparatur auf dem Weg, einen neuen Code zur Formalie zu machen),
dass der alte Code nicht mehr gilt, dass beim Erneuern eines FREMDEN Codes
weiterhin alles hinausfliegt, und dass die Seite nach dem Erzeugen nicht
zur Anmeldung springt. Das Notfall-Skript ist mit beiden Wegen getestet
(Treffer und unbekannter Name). Bestehende Laeufe gruen: Rollen 97,
Personen-Liste 33, Sicht 48.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-02 23:05:29 +02:00
DogFatherGitandClaude Opus 5 540b5d8838 Freie Namen, ein Call-Knopf, und drei Seiten, die fuer Scouts kaputt waren
DREI SACHEN AUF EINMAL, alle aus derselben Sitzung.

1. AUSSUCHEN ODER SELBST EINTRAGEN
   Wunsch: "ich will da auch sachen selber noch eintragen koennen, also
   aussuchen und selbst eintragen."

   Jede Personenauswahl (Kalender, Aufgaben, Bereiche, Content, Dateien)
   nimmt jetzt auch einen getippten Namen an -- eine Agentur, eine Marke,
   einen Gast ohne Konto. Dazu bekommt JEDE Auswahl ab acht Eintraegen
   ein Suchfeld: tippen statt scrollen.

   Gebaut IM vorhandenen Auswahl-Bauteil (wahl.js), nicht daneben. Der
   erste Anlauf war ein zweites Bauteil -- es hat sich prompt mit dem
   ersten gebissen, beide haben denselben <select> eingepackt. Ein
   zweites haette ausserdem anders ausgesehen und waere beim naechsten
   Umbau nur an einer von zwei Stellen nachgezogen worden.

   Der freie Name steht in einer EIGENEN Spalte je Feld; die Verknuepfung
   bleibt leer. Entweder eine Person ODER ein Name, nie beides.

   Filipe hat ausdruecklich auch bei den Creator-Feldern freie Namen
   gewollt, nachdem der Nachteil benannt war: Der Eintrag gehoert dann zu
   keinem Konto. Damit daraus kein STILLER Ausfall wird, faellt jede
   Abfrage, die bisher den Namen der verknuepften Person las, jetzt auf
   den freien Text zurueck (externSql) -- gekennzeichnet als "(extern)".
   Der Eintrag verschwindet dadurch aus keiner Liste, keiner Suche und
   keiner Uebersicht.

2. WO FUEHRE ICH DEN CALL?
   Den Knopf gab es, aber nur wenn jemand von Hand einen Link ins
   Ortsfeld getippt hatte UND das Gespraech noch bevorstand. Stand dort
   "Hier", war nichts zum Anklicken da.

   Jetzt hat das Team einen festen Call-Raum (Discord-Sprachkanal), den
   das Management einmal hinterlegt. Danach hat JEDER Call den Knopf --
   und er bleibt, solange das Gespraech laufen kann, nicht nur bis zur
   Startzeit. Ein eigener Link am Termin schlaegt den festen Raum.

3. WAS EINE ROLLE SIEHT, MUSS AUCH FUNKTIONIEREN
   Gemeldet: "cigdem kriegt als manager gewisse sachen nicht auf die sie
   sieht, check jede rolle ab."

   Neue Pruefung server/pruef-rollen.mjs schickt SECHS Rollen-Zustaende
   ueber alle 16 Seiten und misst Konsolenfehler, fehlgeschlagene
   Serveraufrufe, tote Verweise, haengende Ladeanzeigen und wortlos leere
   Seiten. 96 Durchgaenge.

   Der sechste Zustand ist der wichtige: eine Rolle OHNE zugeteilte
   Creator -- der Normalfall am ersten Tag und Cigdems echte Lage.
   Genau dort fielen die Seiten durch, waehrend dieselben Seiten MIT
   Zuteilung tadellos waren.

   VIER ECHTE FEHLER GEFUNDEN UND BEHOBEN:

   a) Eine Aufgabe, die eine Managerin ohne Creator anlegte, war fuer sie
      im selben Moment unsichtbar -- creator_id und verantwortlich_id
      leer, und "von mir selbst angelegt" stand in keiner
      Sichtbarkeitsregel. Nur DogFather sah sie noch. Kein Fehler, keine
      Meldung, die Aufgabe war einfach weg. Dasselbe bei den
      Bereichseintraegen. Beide Regeln kennen jetzt erstellt_von.
      Niemand sieht dadurch etwas Fremdes -- nur das Eigene.

   b) Ein Scout ohne zugeteilten Creator bekam auf die GESAMTE
      Report-Seite 404, obwohl sie fuer ihn verlinkt ist. Die Seite
      antwortet jetzt sauber und leer, statt sich zu verweigern.

   c) Start-Check und Report blieben fuer immer auf "wird geladen"
      stehen, wenn es nichts zu laden gab.

   d) Das Creator-Profil war fuer Scouts ohne Zuteilung wortlos leer --
      der erklaerende Satz stand nur in der grauen Unterzeile.

   Ausserdem meldete die bestehende Lesbarkeitspruefung zwei neue
   Beschriftungen von mir als zu klein fuers Handy (10,88 statt 11,5 px).
   Behoben, und dieselbe Groesse an der Serien-Karte gleich mit -- dort
   waere es erst aufgefallen, sobald jemand eine Wiederholung anlegt.

GEPRUEFT: pruef-rollen 97, pruef-freie-namen 32 (mit Gegenproben:
Ben sieht Cigdems Aufgabe NICHT; die Saeulen-Zuordnung nimmt
ausdruecklich KEINEN freien Namen). Alle bestehenden Laeufe gruen:
Startansicht 133, Kalender 84, Serien 67, Protokoll 52, Handy 50, Sicht
48, Ampel 47, Content 45, Aufgabenbrett 44, Sprung 43,
Personen-Loeschen 40, Bereiche 37, Uebersicht 33, Workspace-Seiten 32,
Formulare 19, Betreuung 18, Grosscheck 15, Lesbarkeit 14.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-02 20:10:09 +02:00
DogFatherGitandClaude Opus 5 23429627ef Termine, die von allein weiterlaufen -- bis man sie abstellt
Wunsch: "ich will dass ich da im kalender auch sachen machen die jede
woche jeden monat automatisiert immer weiter laufen so wie ich es
auswaehle bis ich es selber deaktiviere. aktiviere diese option fuer
jeden."

Acht Rhythmen (taeglich, werktags, woechentlich, alle zwei Wochen,
monatlich am Datum, am Monatsletzten, am n-ten Wochentag, jaehrlich),
wahlweise mit Enddatum oder ohne Ende. Fuer JEDE Rolle -- Creator und
Scouts legen sie fuer sich selbst an, mit denselben Grenzen wie beim
einzelnen Termin.

WARUM ECHTE TERMINE UND KEINE GERECHNETEN AUSPRAEGUNGEN

Der naheliegende Weg waere gewesen, die Wiederholung nur beim Anzeigen
auszurechnen. Das waere ein stiller Ausfall geworden: ACHT andere Stellen
lesen die Tabelle termine direkt -- Calls, Protokolle, Berichte,
Hinweise/Ampel, Suche, Aufgaben ("naechster Termin"), Uebersicht und die
Startseite. In all diesen Ansichten haette schlicht nichts gestanden, und
aufgefallen waere es erst Monate spaeter. Stattdessen macht ein
Nachfueller aus der Regel echte Zeilen (Horizont 180 Tage) -- jedes andere
Modul sieht sie mit, ohne dass dort eine Zeile geaendert werden musste.
Beim geplanten ICS-Abo gilt dasselbe.

KEIN ZEITGEBER. Der Nachfueller laeuft beim Serverstart, beim Anlegen und
Aendern und beim Oeffnen des Kalenders (dort hoechstens alle fuenf
Minuten). Er ist idempotent. Ein Cron waere eine weitere Sache gewesen,
die stillschweigend ausfallen kann.

WAS BEIM ABSTELLEN PASSIERT: Vergangene Termine und alles, was jemand von
Hand angefasst hat (verschoben, umbenannt, abgehakt), bleibt stehen. Nur
die unberuehrte Zukunft wird aus dem Kalender genommen. Wer einen
einzelnen Tag loescht, streicht nur diesen einen -- der Tag wird als
Ausnahme vermerkt, sonst legte der Nachfueller ihn wieder an.

ZEITUMSTELLUNG: Gerechnet wird auf reinen Datumstexten in UTC-Mittag, die
Uhrzeit wird als Text angehaengt. 18:00 bleibt dadurch 18:00 und wandert
im Oktober nicht auf 17:00.

DIE SICHTBARKEITSREGEL STEHT JETZT NUR NOCH EINMAL (workspace.js,
termineSichtbar) statt zweimal fast gleich. Zwei Fassungen waeren
auseinandergelaufen -- und dann haette eine Wiederholung jemandem etwas
gezeigt, was der einzelne Termin ihm verbirgt.

IN DER OBERFLAECHE beschriften sich die Rhythmen nach dem gewaehlten
Datum ("jeden Dienstag", "jeden Monat am letzten Dienstag") statt
abstrakt ("woechentlich"), inklusive des ehrlichen Hinweises, dass "jeden
31." den Februar auslaesst. Darunter steht der ganze Satz in Worten. Eine
neue Leiste "Laeuft von allein" zeigt alle laufenden Wiederholungen mit
Abstell-Schalter -- was von selbst weiterlaeuft, muss man sehen koennen.

GEPRUEFT: 67 Pruefungen (server/pruef-serien.mjs), darunter von Hand
nachgeschlagene Datumsangaben fuer jeden Takt (der letzte Dienstag im
Dezember 2026 ist der 29., nicht der 22.; der 29. Februar nur in
Schaltjahren) und vier GEGENPROBEN, die beweisen, dass die Pruefungen
auch "nicht in Ordnung" sagen koennen. Die Anzahl der Pruefungen steht in
der Bedingung, nicht nur im Meldetext. Bestehende Pruefungen unveraendert
gruen: Kalender 84, Sicht 48, Startansicht 133, Protokoll 52, Ampel 47,
Handy 50, Aufgabenbrett 44, Sprung 43, Personen-Loeschen 40, Uebersicht
33, Workspace-Seiten 32, Formulare 19, Betreuung 18, Grosscheck 15,
Lesbarkeit 14.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-02 17:12:00 +02:00
DogFatherGitandClaude Opus 5 1d54058760 Die Sicht wirkt jetzt auch auf die SEITE, nicht nur auf die Listen
Gemeldet: "ich hab oben die Ansicht von Tili ausgewaehlt und seh immer
noch die Seite genau wie meine." Das stimmte.

Der Umschalter war HALB gebaut. Die Daten dahinter folgten der Auswahl
(Aufgaben, Kalender, Dateien, Bereiche, Hinweise, Suche) -- die Seite
drumherum nicht: /workspace/api/ich lieferte immer den Angemeldeten, und
daraus baut die Startseite ihre Kacheln, die Rollenzeile und die
Begruessung. DogFather sah einen fremden Arbeitsplatz in seiner eigenen
Verkleidung.

ZWEI FEHLER STECKTEN DARIN.

1. /api/ich verschwieg die gewaehlte Sicht. Es liefert sie jetzt als
   eigenes Feld `sicht` -- NEBEN den eigenen Angaben, nicht an ihrer
   Stelle. Die Oberflaeche braucht beides: WER BIN ICH (Kopfleiste,
   "das bin ich" in Listen, alles was schreibt) und WESSEN ARBEITSPLATZ
   SEHE ICH (was gezeigt wird). Die beiden zu vermischen waere der
   sichere Weg dazu, dass irgendwann etwas unter fremdem Namen
   gespeichert wird.

2. EIN FEHLER DER REIHENFOLGE, und der war der eigentliche Grund.
   In kopf.js wurde `aktiv` (welche Sicht laeuft) erst in
   sichtAufbauen() gesetzt -- das laeuft ueber werZeigen(), also
   NACHDEM die Seite ihre erste Abfrage abgeschickt hat. Ausgerechnet
   /api/ich, aus dem Kacheln, Rolle und Begruessung entstehen, ging
   damit IMMER ohne die gewaehlte Sicht hinaus. Beide Zeilen waren fuer
   sich richtig; im Quelltext sieht man so etwas nicht.

WAS JETZT PASSIERT: In der Sicht auf einen Creator verschwinden die
Leitungs-Kacheln (18 -> 14, kein "Personen & Zugaenge", keine
"Automationen"), die Gruppe heisst "Wissen" statt "Team & System" --
genau wie bei ihm -- und unter dem Gruss steht "Arbeitsplatz von Tili ·
Creator". Plakette und Name oben bleiben die eigenen: Man ist weiterhin
man selbst, man sieht nur einen anderen Arbeitsplatz.

WARUM DIE PRUEFUNG DAS UEBERSEHEN HAT, und das ist die Lehre: Sie hat
geprueft, dass WENIGER AUFGABEN erscheinen -- und das stimmte ja. Sie
prueft genau die Haelfte, die fertig war. Jetzt prueft sie auch, dass
die Leitungs-Kacheln verschwinden, die Gruppentitel mitgehen und
dransteht, wessen Arbeitsplatz man ansieht.

Beim Schreiben dieser Pruefung dieselbe Falle noch einmal: Sie mass
"seine eigenen Kacheln", waehrend aus einem Abschnitt davor noch die
Scout-Sicht lief (die ueberlebt den Seitenwechsel, das ist gewollt), und
meldete einen Fehler, den es nicht gab. Eine Pruefung, die ihren eigenen
Ausgangszustand nicht herstellt, misst den Nachhall der vorigen.

31 von 31 Dateien, 1236 von 1236 Einzelpunkten.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-02 16:21:39 +02:00
DogFatherGitandClaude Opus 5 60c3edb10d Verirrte Datei aus dem Worker-Ordner entfernt -- Wert vorher geprueft
Im Ordner cloudflare-worker/ lag seit dem 12.08.2026 eine Datei, deren
Name ein verunglueckter Windows-Pfad ist
("UsersqcigaDocumentsObelixDogFather.tmp-new-code.txt"). Darin: eine 14
Zeichen lange Zufallszeichenfolge. Sie kam mit dem Commit "Alle
ausstehenden Aenderungen fuer den Server-Umzug uebernommen" herein --
offensichtlich versehentlich.

WARUM SIE AUFFIEL: Von aussen sah sie aus wie ein offen liegender
Zugangscode. Ueber die Website war sie zwar nicht erreichbar (sie liegt
ausserhalb der ausgelieferten Ordner, 404) -- aber aus Gitea konnte sie
JEDER ohne Anmeldung abrufen: 200, 14 Bytes.

WAS DIE PRUEFUNG ERGAB, und deshalb steht sie hier fuer immer
nachlesbar, damit niemand sie ein zweites Mal machen muss:

  * Der Wert ist KEIN gueltiger Zugangscode. Geprueft mit genau der
    Rechnung, die auch die Anmeldung benutzt (scrypt mit dem Salz jeder
    Person, timingSafeEqual gegen den gespeicherten Hash):
    6 Personen geprueft, 0 Treffer.
  * Die Website-Schranke von vor dem Go-Live ist nicht mehr in Betrieb --
    der Dienst dogiweb setzt ueberhaupt keine Zugangscodes.
  * Der Wert steht an keiner anderen Stelle im Code.

Er war also wertlos. Geloescht wird die Datei trotzdem: Sie SIEHT aus
wie ein Geheimnis, und der Naechste, der sie findet, macht dieselbe
Untersuchung noch einmal.

Zur Klarheit, falls das je wieder aufkommt: Ein Loeschen im Verzeichnis
entfernt nichts aus der Historie. Waere der Wert gueltig gewesen, waere
das Loeschen die falsche Antwort gewesen -- dann haette der Code
GEAENDERT werden muessen. Hier ist beides unnoetig, weil er nie gegolten
hat.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-02 15:59:30 +02:00
DogFatherGitandClaude Opus 5 bd42a0462e Cache-Stempel nachgezogen -- sonst waere die Aenderung nicht angekommen
Der vorige Commit aenderte personen.js, aber die HTML-Dateien trugen
weiterhin den alten Stempel (?v=202609020145). Cloudflare liefert
Dateien vier Stunden lang aus dem Zwischenspeicher: Die neue Auswahl
"Gehoert zu" waere bis in den Vormittag hinein bei niemandem
angekommen -- und der Deploy haette dabei fehlerfrei ausgesehen.

Aufgefallen ist es nur, weil in `git status` keine einzige HTML-Datei
stand. Genau das ist das Merkmal: Wer JS oder CSS aendert und danach
keine geaenderten HTML-Dateien sieht, hat den Stempel vergessen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-02 02:17:19 +02:00
DogFatherGitandClaude Opus 5 9134b30d06 Scouts gehoeren zu einem Manager ODER zu DogFather -- wie bei den Creatorn
Wunsch vom 02.09.2026: "ich will die Rollen, welcher Scout welchem
Manager oder DogFather gehoert, wie es bei den Creator ist, drunter."

Die Auswahl stand bisher nur dann unter einem Scout, wenn es ueberhaupt
einen Manager gab -- und es gibt zurzeit keinen. In der Personenliste war
davon also nichts zu sehen. Jetzt steht sie immer da, mit Managern UND
DogFather zur Wahl, genau wie "Betreut von" bei den Creatorn.

DIE BEIDEN FAELLE BEWIRKEN VERSCHIEDENES, und das ist Absicht:
  MANAGER    Der Eintrag entscheidet ueber SICHTBARKEIT -- er sieht
             danach die Leads dieses Scouts und dessen Creator.
  DOGFATHER  Der Eintrag haelt nur die ZUSTAENDIGKEIT fest. An den
             Rechten aendert er nichts; DogFather sieht ohnehin alles.

Genau diese Unterscheidung gilt bei den Creatorn seit dem 31.08. auch.
Vorher hatte ich einen Scout unter DogFather abgewiesen mit der
Begruendung, der Eintrag bewirke nichts. Das war zu eng gedacht: Er
beantwortet die Frage "wen frage ich?", und das ist der Zweck dieser
ganzen Liste.

Ein Scout unter einem Scout bleibt ausgeschlossen -- eine Ordnung, die
es nicht gibt.

ZWEI DINGE MITGEZOGEN, damit die Liste nicht zwei Sprachen spricht:
  * Der Leerwert heisst wieder "— niemand —" wie bei den Creatorn.
    "— direkt bei DogFather —" sah aus wie eine Zuordnung und war
    keine -- derselbe Fehler war bei den Creatorn schon einmal behoben
    worden, weil dieselben Leute dadurch gleichzeitig als "ohne
    zustaendige Person" gezaehlt wurden.
  * Die Rolle steht nur dann in Klammern, wenn sie etwas hinzufuegt.
    "Dogfather (DogFather)" waere zweimal dasselbe Wort.

Die Pruefung haelt jetzt BEIDES fest: dass die Zuteilung an DogFather
geht -- und dass sie niemandem mehr Sicht gibt. Verschwimmt dieser
Unterschied je, faellt es dort auf.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-02 02:16:42 +02:00
DogFatherGitandClaude Opus 5 d7cf598b00 "Heute" war zwei Stunden lang gestern -- und vier Fehler auf Handy und PC
Auftrag: "einen grossen Check machen, ob alles klappt, auf dem Handy und
PC." Dafuer ein neuer Rundgang (pruef-grosscheck.mjs), der stumpf ueber
alles geht: 19 Workspace-Seiten mal 4 Rollen mal 2 Bildschirmgroessen
plus 33 oeffentliche Seiten, zweimal. 208 Seiten, 50 774 Elemente.

Solche Rundgaenge finden andere Fehler als gezielte Pruefungen: nicht
den falsch gerechneten Wert, sondern die Seite, die bei genau einer
Rolle ueberlaeuft.

=== DER WICHTIGSTE FUND: "heute" war in UTC gerechnet ===

Der Check lief um 01:10 Uhr. Ortszeit war der 2. September, in UTC noch
der 1. -- und in diesem Fenster rechnete die Anwendung an ZWOELF Stellen
"heute" als toISOString(), also in UTC. Server und Benutzer stehen beide
auf Europe/Berlin.

Was das im Alltag bedeutete, jede Nacht zwischen 0 und 2 Uhr:
  * eine heute faellige Aufgabe galt noch nicht als faellig
  * eine um Mitternacht ueberfaellig gewordene erschien erst um 2 Uhr
  * der Filter "Heute faellig" zeigte den Vortag
  * Datumsfelder schlugen gestern vor
  * der Kalender begann seine Vorgabe einen Tag zu frueh

Also genau dann, wenn nach einem Stream gearbeitet wird.

kalender.js machte es die ganze Zeit RICHTIG -- samt Begruendung, warum
die ARITHMETIK trotzdem in UTC laufen muss (UTC-Mittag ueberlebt die
Zeitumstellung; wer lokal rechnet, verliert am 27. Oktober einen Tag).
Diese Trennung gilt jetzt ueberall, aus je einer Quelle:
  RECHNEN mit Datumsangaben  -> UTC-Mittag, unveraendert
  WELCHER TAG IST HEUTE      -> Ortszeit (heuteLokal/tagLokal im Server,
                                window.heuteLokal in kopf.js)

WIE ES AUFFIEL, und das ist die eigentliche Lehre: Zuerst schlugen zwei
Pruefungen fehl -- und die Ursache lag in IHNEN, sie rechneten selbst in
UTC (36 Stellen in 17 Dateien). Nach deren Reparatur schlugen sie WIEDER
fehl, und erst da zeigten sie auf die Anwendung. Wer beim ersten Mal
aufgehoert haette ("ist ja nur die Pruefung"), haette den echten Fehler
nie gesehen.

Nachtrag desselben Musters: pruef-uebersicht legte den Termin weiterhin
in UTC an, waehrend die Erwartung schon auf Ortszeit stand. Wer eine
Datumsrechnung umstellt, muss BEIDE Seiten umstellen -- die, die
schreibt, und die, die prueft.

=== VIER FEHLER AUF HANDY UND PC ===

1. Ein langer Creator-Name ("SpongBobSchwammKopf") schob die Startseite
   auf dem Handy um 48 Pixel aus dem Bild -- ein Wort ohne Trennstelle,
   und die Seite liess sich seitlich wegschieben. Trifft echte Namen:
   Creator heissen selten "Tim".

2. Die klebende Speicherleiste verdeckte auf dem Handy ein Textfeld.
   Beim Tippen sieht man die eigene Zeile nicht. Behoben mit
   scroll-margin-bottom (WCAG 2.2, 2.4.11 "Focus Not Obscured").

3./4. Zwei Beschriftungen waren mit 9,6 px (Uebersicht: "ueberfaellig",
   "dringend", "offen") und 9,9 px (Kalender: "heute") zu klein. Fuers
   Handy gab es laengst eine Ausnahme -- nur der Rechner war vergessen
   worden. Ausgerechnet die Woerter, die den Zahlen ihre Bedeutung geben.

=== WAS KEINE FEHLER WAREN ===

Der erste Durchgang meldete 19 Maengel, die keine waren. Alle einzeln im
Quelltext nachgeprueft und dem Rundgang beigebracht:
  * Kacheln und Kopfzeilen "abgeschnitten" -- das Wasserzeichen ragt
    ABSICHTLICH ueber den Rand (steht so im Quelltext)
  * "verdeckt: wahl2__echt" -- das echte <select> liegt absichtlich
    unsichtbar unter seinem Knopf
  * "zurueck-knopf__text abgeschnitten" -- das uebliche Muster fuer
    "nur fuer Vorleseprogramme"
  * drei "zu kleine" Verweise -- WCAG 2.5.8 nimmt Verweise im Fliesstext
    AUSDRUECKLICH aus. Eine Pruefung, die ihre eigene Messlatte nicht
    kennt, misst nichts.

Beim vierten Punkt haette ich fast an der falschen Stelle repariert.

Und statt die Sticky-Meldung abzuschalten (dann faende sie auch echte
Ueberdeckungen nie mehr), wurde sie GENAUER: Ueberdeckt etwas Klebendes
ein Eingabefeld, ist das nur in Ordnung, wenn das Feld genug
scroll-margin-bottom hat, um darunter hervorzukommen. Aus einer vagen
Meldung wird eine pruefbare Zusage.

Vier Gegenproben belegen, dass der Rundgang ueberhaupt etwas finden
kann: ein zu breites Element, ein winziger Knopf, ein winziger Verweis
AUSSERHALB eines Satzes und ein wirklich abgeschnittenes Wort werden
alle gemeldet. Ohne diesen Nachweis waere "alles in Ordnung" wertlos.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-02 01:51:28 +02:00
DogFatherGitandClaude Opus 5 9e523d3ea2 Manager sehen nur noch ihre zugeteilten Scouts -- und deren Creator
Wunsch vom 01.09.2026: "er soll nur die Scouts sehen, die ihm zugeteilt
sind." Dazu entschieden: Ein Manager sieht dann AUCH die Creator dieser
Scouts, und zuteilen darf NUR DogFather.

WAS VORHER WAR. Ein Manager sah die gesamte Scout-Pipeline -- alle Leads
aller Scouts, dazu dieselben in der Suche und in "Was ist dran". Das war
die letzte Stelle, an der "nur DogFather sieht alles" noch nicht galt.

EINE EIGENE TABELLE, KEIN ZWEITER EINTRAG IN `betreuung`.
Dort heisst die Spalte creator_id, und der ganze uebrige Code liest sie
als "das ist ein Creator". Ein Scout darin waere technisch moeglich und
fachlich eine Luege gewesen: betreuteIds() gaebe Scout-Nummern zurueck,
die anderswo als Creator behandelt wuerden. Solche Abkuerzungen raechen
sich genau dann, wenn niemand mehr weiss, dass sie getroffen wurden.

DIE KETTE steht an EINER Stelle (betreuteIds in workspace.js):
Manager -> seine Scouts -> deren Creator. Ohne sie muesste jeder Creator
einem Manager einzeln zugewiesen werden, und beim ersten vergessenen
faende er ein Loch in seiner Uebersicht, ohne zu merken, dass es eines
ist. Dieselbe Regel gilt fuer Leads in Pipeline, Suche und Hinweisen --
aus einer Quelle (pipelineIds), nicht dreimal abgeschrieben.

ZUTEILEN DARF NUR DOGFATHER, und das ist keine Foermlichkeit: Diese
Zuteilung ERWEITERT die Sicht eines Managers. Duerfte er sie selbst
setzen, koennte er sich seine eigene Sichtbarkeit vergeben -- eine
Grenze, die der Begrenzte selbst verschieben kann, ist keine. Deshalb
istDogFather und ausdruecklich NICHT istLeitung.

In der Personenliste steht bei jedem Scout "Gehoert zu", sichtbar nur
fuer DogFather. Der Leerwert heisst "— direkt bei DogFather —" und nicht
"— niemand —": Ein Scout ohne Manager ist nicht unbetreut, er haengt an
DogFather. Das ist ein Zustand, kein Mangel.

NEBENBEI KORRIGIERT: In der Zustaendigkeits-Route stand als Kommentar
noch "ein Eintrag auf DogFather oder Manager aendert an den Rechten
nichts, die Leitung sieht ohnehin jeden Creator". Das gilt seit heute
nicht mehr -- fuer einen Manager entscheidet dieser Eintrag jetzt sehr
wohl ueber die Sichtbarkeit. Ein falscher Kommentar ist schlimmer als
keiner: Er wird geglaubt.

DIE PRUEFUNG STELLT DEN MISSBRAUCH AN DEN ANFANG. Diese Aenderung
erweitert Sichtbarkeit -- alles andere heute hat sie eingeschraenkt. Ein
Fehler dort zeigt jemandem zu wenig und faellt auf; ein Fehler hier
zeigt zu viel und faellt niemandem auf. Geprueft wird deshalb: Manager
und Scout duerfen nicht zuteilen (404, und es wird auch wirklich nichts
geschrieben), Scout an Scout und Scout an DogFather werden abgewiesen,
ein Creator laesst sich nicht zuteilen. Danach die Kette in beide
Richtungen, das Zuruecknehmen, und dass ein Scout zu genau einem Manager
gehoert.

Ein lehrreicher Fehlschlag in der Pruefung selbst: Sie meldete, die
Auswahl fehle in der Oberflaeche. Sie fehlte nicht -- die Personenliste
ist nach Rollen ZUGEKLAPPT, und die Pruefung hatte nicht aufgeklappt.
Sah aus wie ein Produktfehler, war einer der Pruefung. Sie klappt jetzt
auf und stellt vorher fest, dass wirklich alle sieben Zeilen dastehen.

Gesamtlauf: 30 von 30 Dateien, 1210 von 1210 Einzelpunkten.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 22:44:30 +02:00
DogFatherGitandClaude Opus 5 15cfee1078 Gesamtlauf aller Pruefungen -- und eine Pruefung, die nichts geprueft hat
Auf "CHECK MAL AB, DASS ALLES PERFEKT LAEUFT. ALLES."

Ergebnis: 29 von 29 Pruefdateien in Ordnung, 1178 von 1178 Einzelpunkten
bestanden. Der Server laeuft ohne Neustart, alle 33 oeffentlichen Seiten
antworten, keine Schnittstelle gibt ohne Anmeldung etwas heraus.

DER EIGENTLICHE FUND WAR EINE PRUEFUNG, DIE NICHTS GEPRUEFT HAT.

tools/alles-pruefen.mjs zaehlt nicht nur gruen/rot, sondern die ANZAHL
der Einzelpruefungen je Datei -- und pruef-kopf-messen.mjs kam auf NULL.
Sie war nie eine Pruefung, sondern ein reines Messwerkzeug: Sie druckte
Zahlen und endete IMMER mit Exitcode 0. Weil sie "pruef-..." heisst, lief
sie bei jedem Gesamtlauf mit und meldete brav "bestanden". Sie konnte
gar nicht fehlschlagen.

Mit blossem Auge war das nicht zu sehen: Der Lauf war gruen, die Datei
stand unauffaellig zwischen den anderen. Aufgefallen ist es nur, weil
die Zahl mitgezaehlt wurde -- ein gruener Lauf ist eben kein Beweis,
solange nicht auch die Anzahl stimmt.

ZWEI KONSEQUENZEN:

1. Die Datei prueft jetzt wirklich. Die beiden Zahlen, um die es geht,
   wurden laengst gemessen und werden nun auch beurteilt:
     UEBERSTAND          muss 0 sein
     ABMELDEN ERREICHBAR muss wahr sein -- es ist der einzige Weg wieder
                         heraus; liegt er ausserhalb des Bildes, sitzt
                         man fest.
   Die Messwerte bleiben in der Ausgabe: Sie sagen bei einem Fehlschlag
   sofort, WELCHES Teil zu breit ist.
   Gegenprobe gemacht: Mit einer unerfuellbaren Bedingung meldet sie
   "3 Breiten gemessen, 3 beanstandet" und endet mit 1. Sie kann also
   anschlagen -- vorher nicht.

2. Der Laeufer wertet "0 Pruefungen" ab jetzt als FEHLER, nicht als
   Erfolg. Sonst haette dieselbe Falle beim naechsten Mal wieder
   jemanden getaeuscht.

Der Laeufer selbst bleibt im Ordner tools/: Er faengt einen Fehlschlag
je Datei ab (statt beim ersten abzubrechen), zeigt die Dauer mit -- eine
Pruefung, die ploetzlich dreimal so lange braucht, wartet meist auf
etwas, das es nicht mehr gibt -- und druckt am Ende die Gesamtzahl.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 21:59:36 +02:00
DogFatherGitandClaude Opus 5 da49a227f1 Aufgabenbrett fuer alle Rollen -- und nur DogFather sieht alles
Wunsch vom 01.09.2026: "verbessere die Aufgabenseite auch bei Manager,
Scout und Creator. Die Scouts und Manager sollen vielleicht ein bisschen
mehr Optionen haben, wie ihre zugeteilten Creator. Aber auch die Creator
sollen es besser und detaillierter sehen. ABER NUR DIE ROLLE DOGFATHER
SOLL WIRKLICH WEITERHIN ALLEINE ALLES SEHEN KOENNEN."
(Nachgereicht: "die Seite Aufgabe auch bei DogFather machen bitte.")

ZWEI FEHLER IN DEN RECHTEN, die beim Nachsehen herausfielen:

  1. Ein MANAGER sah auf dem Aufgabenbrett GAR NICHTS. Die Regel kannte
     nur admin, creator und scout; er fiel in den Zweig "unbekannte
     Rolle sieht nichts". Ein leeres Brett sieht aus wie "nichts zu
     tun", nicht wie ein Fehler -- deshalb ist das lange niemandem
     aufgefallen.

  2. In Kalender, Dateien, Bereichen und Content sah derselbe Manager
     dagegen ALLES (istLeitung). Zwei entgegengesetzte Antworten auf
     dieselbe Frage, in einem Programm.

Jetzt gilt ueberall dasselbe: NUR DogFather sieht alles. Manager und
Scout sehen ihre eigenen Sachen plus die Creator, die ihnen zugeteilt
sind. Dafuer arbeitet betreuteIds() jetzt auch fuer Manager -- die
Datenbank konnte das laengst (in `betreuung` steht eine beliebige
Person), nur diese eine Funktion hat alle ausser Scouts abgewiesen.

GEAENDERT WURDE NUR, WER WAS SIEHT. Was ein Manager DARF -- freigeben,
aendern, Personen verwalten -- haengt weiterhin an istLeitung und ist
unberuehrt. Ohne diese Trennung haette ein Wunsch nach weniger Sicht
stillschweigend die halben Rechte mitgenommen; die Pruefung haelt beides
ausdruecklich fest.

DIE SEITE SELBST bekommt drei Zeilen ueber dem Brett, und die
Reihenfolge ist die Aussage:
  1. WAS BRENNT -- ein Satz beim Reinkommen. Ein Brett aus vier Spalten
     beantwortet das nicht; man muesste alle vier durchsehen, um zu
     wissen, dass nichts brennt.
  2. AUSSCHNITTE -- "Nur meine", "Heute faellig", "Ueberfaellig", jeder
     mit seiner Zahl am Knopf. Ein Filter, der sich erst nach dem Klick
     als leer herausstellt, kostet zweimal Aufmerksamkeit. Sie filtern
     das Brett, statt eine zweite Liste aufzumachen: Vier Spalten
     nebeneinander sind der Wert dieser Seite.
  3. DIE CREATOR -- fuer Betreuer und DogFather, je mit offener Anzahl
     und einer Warnzahl fuer Ueberfaelliges. Bei DogFather heisst die
     Reihe "Alle Creator", sonst "Deine Creator": "deine" waere bei ihm
     eine falsche Auskunft.

Ein Creator bekommt weder "Nur meine" noch die Creator-Reihe -- bei ihm
ist ohnehin alles seins, und ein Filter mit einem einzigen Eintrag ist
ein Knopf, der nichts tut. Die Liste der Creator stammt aus den
Aufgaben selbst und nicht aus einer Personenabfrage: Dann stehen dort
genau die, die man ohnehin sehen darf, und niemals einer mehr.

NEBENBEI (Screen 1): In der Betreuer-Auswahl stand "Dogfather
(DogFather)" -- zweimal dasselbe Wort, nur anders geschrieben. Die
Klammer entfaellt jetzt, wenn sie dasselbe sagt wie der Name.

pruef-aufgabenbrett.mjs prueft die ABGRENZUNG zuerst und an konkreten
Aufgaben, deren Titel verraten, wem sie gehoeren -- eine huebschere
Filterleiste ist eine Annehmlichkeit, eine Rechteregel, die zu viel
zeigt, ist ein Schaden. Dazu eine Gegenprobe zur alten Regel (der
Manager sieht ueberhaupt etwas) und die ausdrueckliche Bestaetigung,
dass er weiterhin anlegen darf.

Ein Fehler steckte in der Pruefung selbst: Sie erwartete beim Creator
eine feste Zahl und haing damit von ihrer eigenen Reihenfolge ab -- der
Abschnitt davor legt eine weitere Aufgabe an. Sie vergleicht jetzt gegen
die Schnittstelle. Das ist ohnehin die bessere Frage: nicht "sind es
drei", sondern "verschluckt die Seite etwas".

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 21:38:38 +02:00
DogFatherGitandClaude Opus 5 1c00770ec5 Lesbarere Kaesten, und DogFather kann durch fremde Augen sehen
SCREEN 1 -- "die Kachel ganz leicht dunkler, so dass man den Text besser
gelesen bekommt, aber nicht zu viel, so dass man den Hasen noch sieht."

Die Erklaerkaesten stehen jetzt auf 88 statt 78 Prozent Deckung. Zehn
Punkte sind gemessen der Unterschied zwischen muehsam und ruhig lesbar
und lassen zwoelf Prozent Bild durch -- das Motiv bleibt sichtbar.

"und die von wichtigen und neuen PDFs sollen auch staerker sein."
Das war kein Geschmack, sondern ein Fehler: `background` ist eine
Eigenschaft, keine Schicht. Die Zeile
`background: rgba(232,192,125,.045)` hat die Flaeche nicht getoent,
sondern ERSETZT -- uebrig blieben viereinhalb Prozent Deckung. Damit war
ausgerechnet die Anleitung, die jeder lesen soll, die am schwersten
lesbare der Seite. Die Toenung liegt jetzt als Schicht darueber.
Gemessen: 88 % gegen 78 % bei einer gewoehnlichen.

pruef-lesbarkeit.mjs misst das an ECHTEN PIXELN (Text kurz unsichtbar
machen, Flaeche fotografieren, WCAG-Formel) und kennt ZWEI Grenzen --
lesbar genug UND durchsichtig genug. Mit nur einer haette sie eine
schwarze Flaeche am besten gefunden, und das wollte niemand.

SCREEN 2 -- "ich will alles von den Scouts und Managern einsehen
koennen, auswaehlen, was und von wem. Ueberall, auf jeder Seite.
NUR WIR BEIDE SOLLEN DIESE OPTION HABEN."

Die naheliegende Loesung waere ein Filter je Seite gewesen -- also eine
zweite Rechteregel in zwoelf Modulen, und eine Rechteregel an zwoelf
Stellen stimmt irgendwann an elf. Stattdessen wird die PERSON getauscht,
nicht die Regel: Waehlt DogFather einen Scout, laufen alle vorhandenen
Sichtbarkeitsregeln unveraendert mit diesem Scout. Er sieht exakt dessen
Arbeitsplatz -- nicht mehr, nicht weniger. Keine neue Rechteregel.

Im Browser genauso: EIN Ort statt vierzehn. fetch wird einmal umgeleitet
und haengt den Wert an jede lesende Abfrage an. Neue Seiten sind damit
von selbst dabei; man kann es nicht vergessen.

Drei Sicherungen: nur admin (auf dem SERVER geprueft, nicht in der
Oberflaeche), nur lesend (geschrieben wird immer im eigenen Namen --
sonst staende im Protokoll der falsche Name), nur aktive Personen.

Und ein Rahmen um die Seite, solange eine fremde Sicht laeuft. Der
gefaehrlichste Fall ist nicht, dass man nicht umschalten kann, sondern
dass man vergisst, dass man umgeschaltet hat -- und drei Aufgaben statt
dreissig fuer den Bestand haelt.

FUENF FEHLER, DIE DIE PRUEFUNGEN GEFUNDEN HABEN:

1. steckbrief.html hat die Kopfleiste NIE gefuellt -- dort stand
   monatelang "…" statt des eigenen Namens. Aufgefallen, weil der
   Umschalter dort fehlte. Jetzt zusaetzlich ein Netz darunter: Meldet
   sich nach kurzer Zeit niemand, holt der Kopf sich selbst, wer
   angemeldet ist. Die naechste neue Seite kann es nicht mehr vergessen.

2. Der Umschalter sprengte die Kopfleiste (46 px am Rechner, 117 px am
   Handy) und drueckte den Abmelden-Knopf hinaus.

3. Ich habe das <select> gestaltet -- wahl.js ersetzt aber jedes
   Auswahlfeld durch einen eigenen Knopf und schrumpft das echte Feld
   auf einen Pixel. Meine Regeln haben es wieder auf 20 x 44 aufgeblasen,
   wo es als unsichtbares Hindernis ueber dem Knopf lag. Gestaltet wird
   jetzt der Knopf.

4. Ein Wettlauf: Auf profil.html rufen zwei Dateien werZeigen() auf.
   Beide kamen an "gibt es den Umschalter schon?" vorbei, bevor eine ihn
   angehaengt hatte -- zwei Umschalter uebereinander. Die Sperre gehoert
   vor das erste await.

5. Die Handy-Pruefung bemaengelte ein Schild, das dort per display:none
   gar nicht erscheint. Sie sieht jetzt nur noch sichtbaren Text an --
   eine Pruefung, die Unsichtbares anmahnt, gewoehnt man sich ab zu
   lesen.

Dazu zwei Messfehler in den Pruefungen selbst: getComputedStyle liefert
ein LEBENDES Objekt (die Farbe wurde gelesen, nachdem der Text
unsichtbar gemacht war -- gemeldet wurden 1,17:1 fuer tadellos lesbaren
Text), und ein Vergleich lief still ins Leere, weil die Vergleichsdaten
fehlten.

pruef-sicht.mjs stellt den MISSBRAUCH an den Anfang: Scout, Manager und
Creator haengen ?sicht= an und muessen ignoriert werden; erfundene
Nummern, Buchstaben und ein Einschleusversuch fallen auf die eigene
Sicht zurueck; eine abgeschaltete Person liefert keine Sicht mehr; und
was DogFather bei fremder Sicht anlegt, steht unter SEINEM Namen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 21:20:23 +02:00
DogFatherGitandClaude Opus 5 24c66a022e Kennzahlen-Reihe auf der Content-Seite entfaellt
Wunsch: "diese Kisten sind jetzt ueberall zu viel, die sollen weg."

Nur die Content-Seite (nachgefragt): Vorrat, Veroeffentlicht, Ideen,
ohne Hook, ueberfaellig. Die Reihe auf der Startseite und die Kisten im
Report bleiben -- letztere sind der Report.

Sie hatte auch inhaltlich kein Recht mehr: Die Strecke direkt darunter
zeigt Ideen, Produktion und Veroeffentlichtes ohnehin mit Zahl an jeder
Spalte. Zwei Zaehler fuer dieselbe Sache auf einem Bildschirm sind einer
zu viel -- und laufen frueher oder spaeter auseinander.

WAS BLEIBT UND WARUM:
Die Abfrage /workspace/api/content/kennzahlen wird NICHT entfernt. Aus
derselben Antwort speist sich der Saeulen-Balken darunter (70/20/10).
Die Funktion heisst jetzt zahlenLaden() statt kennzahlenLaden() --
der alte Name zeigte auf etwas, das es nicht mehr gibt.

Entfernt sind neben der Reihe auch .kachel, .kachel__wert, .kachel__name
und .kachel__zusatz aus content.css. Achtung fuer spaeter: "kachel" gibt
es auch in start.css und report.css, mit ganz anderen Regeln. Diese drei
Kopien haben nichts miteinander zu tun; ein Kommentar an der Fundstelle
sagt das jetzt.

DIE PRUEFUNG WURDE UMGEDREHT, NICHT GELOESCHT.
pruef-content-ansicht.mjs sicherte bisher "fuenf Kennzahlen" -- jetzt
sichert sie, dass keine da ist. Eine geloeschte Pruefung merkt niemand,
wenn jemand die Reihe spaeter versehentlich wieder einbaut.

Die Zahlen selbst bleiben geprueft: pruef-content.mjs prueft Vorrat,
ohne Hook und ueberfaellig weiterhin an der Schnittstelle (Abschnitt 5
und 6). Entfallen ist ihre Anzeige, nicht ihre Richtigkeit.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 20:37:04 +02:00
DogFatherGitandClaude Opus 5 a129525cb7 Kennzahlen werden Wege, Team wird sichtbar, Felder bekommen ein Aussehen
Vier Wuensche vom 01.09.2026, dazu ein gemeldeter Fehler.

SCREEN 1 -- "wenn man auf die Kisten drueckt, sofort zu der Seite,
diesem Punkt". Jede Kennzahl im Bericht fuehrt jetzt dorthin, wo die
gezaehlten Dinge stehen: nicht nur auf die richtige Seite, sondern auf
die richtige Stelle (?zeigen=...). Ein gemeinsamer Helfer in kopf.js,
damit nicht jede Seite ihr eigenes Sprungverhalten erfindet.

  Auf dem Aufgabenbrett wird HERVORGEHOBEN, nicht gefiltert -- das Brett
  lebt davon, dass man die vier Spalten nebeneinander sieht. Bei den
  Dateien wird gefiltert, denn die Seite hat ohnehin eine Filterleiste,
  und deren Knoepfe zeigen dann mit an, wo man steht. Der Kalender
  schaltet auf die Liste um: In der Monatsansicht liesse sich
  "vergangen" gar nicht sinnvoll markieren.

  Immer mit einem Weg zurueck ("Alles zeigen"), der auch den Parameter
  aus der Adresse nimmt. Eine Seite, die gefiltert bleibt, ist eine
  Falle: Man kommt spaeter wieder, sieht drei von zwanzig Aufgaben und
  haelt das fuer den Bestand.

  Drei Entscheidungen gegen den ersten Entwurf:
  * KEIN ?creator= im Verweis. Das sah hilfreich aus und waere eine
    Luege gewesen -- keine Zielseite liest den Wert.
  * Kisten mit Null fuehren NIRGENDWOHIN. Ein Weg zu null Dingen ist
    eine Enttaeuschung, kein Angebot.
  * "neu angelegt" fuehrt ohne Ausschnitt aufs Brett: Der Bericht zaehlt
    einen Zeitraum, das Brett kennt keinen. Eine Hervorhebung, die nicht
    dieselbe Menge trifft, ist schlimmer als keine.

SCREEN 2 -- "ich will, dass wir Manager, Scouts, DogFather auch die
Fotos, Namen und so alles sehen". Der Steckbrief war eine Einbahnstrasse:
Jeder pflegte seinen, niemand bekam ihn je zu Gesicht. Die
Schnittstelle dafuer lag fertig da und wurde von keiner Seite
aufgerufen. Jetzt steht "Das Team" auf steckbrief.html und (fuer
Creator) auf profil.html -- nach Rollen gruppiert, mit Bild, Rolle,
eigenem Satz und Kanaelen. Wer wen sieht, entscheidet weiterhin der
Server; ein Creator sieht seine Betreuung, nicht die anderen Creator.

SCREEN 4 -- "das soll richtig geil aussehen und nicht so einfach, auch
die Schrift". Die Ursache war kein Geschmack, sondern ein Loch im
Aufbau: Jede Seite gestaltete ihre Felder mit einem EIGENEN Selektor,
und wer ein Feld anderswo hinsetzt, faellt durch alle Netze. Genau so
stand "Ein Satz ueber dich" als grauer Kasten in MONOSPACE da --
<textarea> faellt ohne `font: inherit` auf Schreibmaschinenschrift
zurueck. Jetzt gibt es eine Grundlage fuer jedes Feld, und die drei
wortgleichen Kopien in aufgaben/profil/bereich sind weg.

  Der erste Anlauf setzte dort auch `width: 100%` -- die Pruefung
  meldete sofort Felder von 28 statt 362 Pixeln. Breite ist LAYOUT und
  gehoert der Seite; eine Grundlage mit Staerke null verliert jeden
  Breitenstreit, also darf sie ihn nicht anfangen.

SCREEN 5 -- "wieso seh ich mein Bild da nicht?" Ein lehrreicher Fehler:
Der Server liefert das Bild laengst mit, und im Quelltext dort steht
ausdruecklich "es steht in der Kopfleiste JEDER Seite UND IN DER
BEGRUESSUNG". Die Absicht war aufgeschrieben, die Haelfte nie gebaut --
und aufgefallen ist es nicht, weil ein Buchstabe im Kreis nicht falsch
aussieht, nur eben nicht wie man selbst.

PRUEFUNGEN. Zwei neue (pruef-sprung, pruef-team), eine erweiterte
(pruef-formulare). Sie haben vier echte Fehler gefunden, die mit blossem
Auge nicht zu sehen waren:
  * Der Sprung auf "dringend" hob auch ERLEDIGTE dringende Eintraege
    hervor -- man klickt auf "2" und bekommt drei markiert.
  * Auf einer leeren Zielseite erschien gar keine Erklaerung (frueher
    Ausstieg uebersprang sie). Das ist der wichtigere Fall: Wer auf eine
    Zahl klickt und im Leeren landet, glaubt, er sei falsch abgebogen.
  * Das Kachel-Merkzeichen stiess mit der Zahl zusammen.
  * Die Feldpruefung fand vier RICHTIGE Felder falsch (die Kanaele holen
    ihren Rahmen vom Umschlag mit dem "@"). Eine Pruefung, die
    Richtiges anmahnt, gewoehnt man sich ab zu lesen.
Und zwei Faelle, in denen die Pruefung sich selbst belogen haette: null
gefundene Felder galten als "in Ordnung", und der Ueberdeckungsvergleich
war nach einer Aenderung ohne ein einziges Vergleichselement gruen.
Beide zaehlen jetzt mit, wie viel sie tatsaechlich angesehen haben.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 20:18:39 +02:00
DogFatherGitandClaude Opus 5 d4cd8952b7 Checklisten-Gruppen klappen zu, Markierungen tragen ein Zeichen
Zwei Wuensche vom 01.09.2026:
  "neben den Titel soll immer ein Knopf sein, wo man die Liste aufmacht
   oder wieder zumacht -- die sollen auch zu, damit die Seiten nicht so
   lang sind."
  "und wenn die Ansprechpartner was markieren, sollen die da so ein
   Zeichen haben, so dass die sehen, da ist was -- und es soll auch oben
   in der Kachel angezeigt werden."

ZUKLAPPEN. Der Gruppenkopf ist jetzt ein Knopf ueber die volle Breite
(49 px hoch, mit dem Daumen treffbar), nicht ein Pfeilchen daneben. Zu
ist der Standard. Gemessen: Die LIVE-Analyse ist damit 1100 statt 2081
Pixel lang -- 47 Prozent gespart. Welche Gruppen offen waren, merkt sich
der Browser; sonst waere jede Bewertung ein Ruecksprung an den Anfang.

DAS ZEICHEN. Eine Raute mit Ausrufestrich, in Bernstein. Drei
Entscheidungen, keine davon Geschmack:
  * FORM -- alles andere auf dem Bildschirm ist rund oder eckig. Eine
    Spitze nach oben gibt es sonst nirgends, und deshalb findet das Auge
    sie zwischen zwanzig Kacheln ohne Suchen.
  * FARBE -- immer dieselbe, nie die der Kachel. Ein Zeichen, das die
    Farbe wechselt, muss gelesen werden; eines, das immer gleich
    aussieht, wird erkannt. Rot waere falsch: "verbessern" ist ein
    Auftrag, kein Fehler.
  * BEWEGUNG -- ein Atmen ueber 3,2 s, kein Blinken; bei "weniger
    Bewegung" bleibt der Schein stehen statt zu verschwinden.

Es steht an drei Stellen, immer aus derselben Quelle (bereiche.js): am
Gruppenkopf, am Punkt selbst und oben auf der Kachel der Startseite.

WARUM DAS ZEICHEN AM GRUPPENKOPF PFLICHT IST. Ohne es waere Zuklappen
ein Rueckschritt gewesen: Der Betreuer markiert etwas, die Gruppe ist zu,
und der Creator erfaehrt es nie. Die Zahl "zu verbessern" in der Bilanz
klappt die betroffenen Gruppen jetzt auf und springt hin.

Auf der Kachel nur fuer Creator. Fuer einen Betreuer waere es die Liste
dessen, was er selbst angehakt hat -- sie waechst mit seiner Arbeit, und
nur der Creator kann sie abbauen. Ein Zaehler, den man nicht auf null
bringen kann, wird ignoriert, und dann sind auch die daneben nichts wert.

ZWEI FEHLER, DIE DIE PRUEFUNG GEFUNDEN HAT:
  1. Das Zeichen stiess auf der Kachel mit der Zahl zusammen (zwei
     Pixel). Behoben an der Ursache: Besteht die Zahl NUR aus
     Markierungen, ist sie dieselbe Auskunft ein zweites Mal und
     entfaellt; sonst ruecken Zahl und Pfeil nach unten.
  2. Danach war die Pruefung wertlos -- sie verglich mit einem Element,
     das es nun nicht mehr gab, und war ohne einen einzigen Vergleich
     gruen. Sie zaehlt jetzt die geprueften Nachbarn mit; null Nachbarn
     ist ein Fehler, kein Erfolg.

Geprueft wird SICHTBARKEIT, nicht Vorhandensein: Ein zugeklappter Punkt
steht weiterhin im Dokument, alle bisherigen Pruefungen waeren gruen
geblieben, auch wenn der Creator seine Markierung nie zu Gesicht
bekaeme. Dazu Gegenproben: keine Markierung ohne Grund, und wer nichts
markiert bekommen hat, sieht auch kein Zeichen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 19:42:34 +02:00
DogFatherGitandClaude Opus 5 f9bf7d5674 "Hilfe anfordern" entfaellt - es bleibt die Rueckmeldung
Wunsch: "die sollen nirgendwo Hilfe anfordern koennen, sondern nur
Rueckmeldungen."

Es ist auch die klarere Loesung. Es gab zwei Wege, dasselbe zu sagen:
einen Knopf, der eine Aufgabe erzeugte, und ein Textfeld, das eine
Nachricht schrieb. Zwei Wege fuer eine Sache heisst, dass niemand weiss,
welcher der richtige ist -- und dass Antworten mal als Aufgabe und mal
als Nachricht landen. Wer dann nachsieht, findet die Haelfte nicht.

Geblieben ist die Rueckmeldung an jedem Punkt: ein Verlauf, in dem beide
Seiten schreiben koennen, sichtbar dort, wo es hingehoert -- am Punkt
selbst und nicht in einer zweiten Liste.

VOLLSTAENDIG entfernt, nicht nur ausgeblendet:
  * der Knopf in der Checkliste
  * der Knopf und die Marke "Hilfe moeglich" im Vorlagenblock
  * die Route /workspace/api/vorlagen/hilfe
  * 47 Datenfelder hilfe: true/false in den Vorlagen
  * die zugehoerigen Stile
  * der Abschnitt in pruef-vorlagen.mjs

Datenfelder, die nichts mehr bewirken, und Routen, die niemand mehr
aufruft, werden beim naechsten Mal fuer lebenden Code gehalten und
mitgepflegt. Das ist teurer als das Entfernen heute.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 17:19:19 +02:00
DogFatherGitandClaude Opus 5 9267797d1d Feste Checklisten statt Vorschlaege - in allen vier Bereichen
"Die neuen Sachen in der LIVE-Analyse soll der Creator FEST sehen, auch
schoen kategorisiert, und der Ansprechpartner soll anklicken koennen,
wenn er findet, dass der Creator was verbessern sollte."

DER UMBAU. Vorher waren die Punkte Vorschlaege zum Uebernehmen: Man
holte sie sich und bekam einen eigenen Eintrag. Das war falsch gedacht.
Eine Checkliste, die man erst anfordern muss, ist keine Checkliste --
und schlimmer: Zwei Creator haetten unterschiedliche Listen gehabt, je
nachdem wer sich was geholt hat. Genau das macht einen Vergleich
unmoeglich, und um Vergleich geht es bei einer Betreuung.

Jetzt stehen dieselben Punkte fuer JEDEN fest da, gruppiert:

  LIVE       21 Punkte -- vor, waehrend, nach der Sendung
  Community  14 Punkte -- Moderation vorbereiten, aufbauen, wenn es kippt
  Technik    12 Punkte -- Einrichtung, Ausfall, was geholfen hat
  Content    13 Ideen  -- nach Saeule (70/20/10) statt nach Ablauf

Jede Gruppe hat einen Satz, der erklaert, wofuer sie da ist. Eine
Ueberschrift allein sagt das nicht.

Was sich je Creator unterscheidet, ist nur der STAND -- und den setzt
die Betreuung: "Passt so" oder "Verbessern". Ein Creator kann sich nicht
selbst bewerten; koennte er es, stuende alles auf gruen. "Verbessern"
verlangt einen Satz, WAS zu verbessern ist -- eine Bewertung, mit der er
nichts anfangen kann, ist nicht streng, sondern nur entmutigend.

Der Creator sieht alles: die Stufe, den Grund, den Namen und den
Zeitpunkt. Und er kann an JEDEM Punkt antworten -- das ist der Kanal,
ueber den er ueberhaupt etwas sagen kann.

Oben steht eine Bilanz in einer Zeile: wie viele passen, wie viele sind
zu verbessern, wie viele hat noch niemand angesehen. Das ist die Frage,
die beide Seiten zuerst haben.

STABILE SCHLUESSEL statt Positionen. Ein Stand haengt am Schluessel des
Punktes ("ton-geprueft"), nicht an seiner Nummer. Haenge er an der
Position, waere beim Einfuegen eines Punktes in der Mitte jede Bewertung
dahinter am falschen Punkt -- und niemand wuerde es merken, weil beides
plausibel aussieht. 64 Punkte haben jetzt einen.

"Offen" loescht den Stand, statt ihn auf "offen" zu setzen: Ein
Datensatz, der nichts aussagt, ist Ballast.

ZWEI EIGENE FEHLER, VON DEN PRUEFUNGEN GEFUNDEN:

1. DAS CSS LAG IN DER FALSCHEN DATEI. Ich hatte es in content.css
   geschrieben -- bereich.html laedt die gar nicht. Die Punkte standen
   auf drei von vier Seiten nackt und ohne Flaeche da. Es ist derselbe
   Fehler wie im August bei .knopf-still, .schalter und .kopf-zeile, und
   es gibt seitdem eine Warnung dazu in start.css. Sie hat nichts
   genutzt, solange keine Pruefung sie nachhielt.

   Jetzt gibt es eine: Sie misst, ob eine Karte wirklich eine Kante und
   Polsterung hat -- nicht nur, ob das Element existiert. Gegenprobe
   gemacht: Klasse umbenannt, Pruefung meldet "STIL FEHLT".

2. Ein Betreuer sah beim Oeffnen keine Bewertungsknoepfe, weil noch kein
   Creator gewaehlt war -- und musste erst raten, dass er oben jemanden
   auswaehlen soll. Jetzt nimmt der Server den ersten betreuten Creator,
   wenn keiner angegeben ist.

Die alte Oberflaechenpruefung fuer den Vorschlaege-Block wurde entfernt
statt angepasst: Sie verlangte etwas, das es nicht mehr gibt. Eine
dauerhaft rote Pruefung ist schlimmer als keine -- man gewoehnt sich
daran, und beim naechsten echten Fehler sieht niemand hin.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 17:12:55 +02:00
DogFatherGitandClaude Opus 5 372a0eb95a Ampel und Rueckmeldungen - bewerten darf nur die Betreuung
Der Wunsch: "der Creator sieht, ob es gut ist oder schlecht. Und wenn
nicht gut, dann muss es verbessert werden -- und nur der Ansprechpartner
kann das aendern. Die Creator koennen nur Nachrichten hinterlassen zum
Kommunizieren."

ZWEI GETRENNTE DINGE, und sie getrennt zu halten ist der ganze Punkt:

  DIE AMPEL ist eine Beurteilung. Sie gehoert dem Betreuer, und nur er
  setzt sie. Koennte ein Creator sich selbst auf gruen stellen, waere
  sie wertlos -- dann stuende ueberall gruen. Der Server lehnt es mit
  403 ab und sagt dabei, wer es kann.

  DIE NACHRICHT ist ein Gespraech. Sie gehoert beiden. Ein Creator, der
  auf eine Bewertung nicht antworten kann, bekommt ein Urteil statt
  einer Betreuung.

DREI STUFEN, NICHT FUENF. Eine Zahl von 1 bis 5 klingt genauer und ist
es nicht: Niemand kann den Unterschied zwischen 3 und 4 erklaeren, und
am Ende steht ueberall die 3. Die Frage lautet "reicht das schon?", und
darauf gibt es drei ehrliche Antworten -- passt so, noch verbessern,
noch nicht angesehen.

"NOCH VERBESSERN" VERLANGT EINE BEGRUENDUNG. Eine Bewertung, mit der der
Creator nichts anfangen kann, ist nicht streng, sondern nur
entmutigend. Der Server lehnt sie ohne Grund ab; die Oberflaeche fragt
deshalb gleich danach, statt hinterher eine Fehlermeldung zu zeigen.
"Passt so" braucht keinen -- da gibt es nichts zu erklaeren.

Die Stufe steht IMMER an der Karte, auch fuer den Creator, auch wenn sie
"noch nicht angesehen" lautet. Er soll sehen, wo er steht, ohne fragen
zu muessen. Der Grund steht daneben in voller Breite, nicht in einer
Ecke.

FREMDE NACHRICHTEN BLEIBEN STEHEN -- auch fuer DogFather. Ein Gespraech
nachtraeglich umzuschreiben waere schlimmer, als eine unbedachte
Aeusserung stehen zu lassen. Wer etwas richtigstellen will, schreibt
eine neue.

VORLAGEN AUCH FUER COMMUNITY UND TECHNIK (Screens 11 und 12): 14
Moderations-, Aktions- und Konfliktpunkte, 12 Technikpunkte. Ton steht
vorn, weil schlechter Ton der Grund Nummer eins ist, warum Leute einen
Stream verlassen. Die drei Bereiche laufen jetzt ueber EINE Zuordnung
statt drei fast gleicher Bloecke -- sonst weicht der dritte irgendwann
ab. Die beiden alten Zweige wurden entfernt: Toter Code, den man stehen
laesst, wird beim naechsten Mal fuer lebenden gehalten.

Die Ampeln einer ganzen Liste kommen in EINER Abfrage. Zwanzig
Eintraege einzeln zu fragen waeren zwanzig Anfragen, und die Seite
ruckelte sichtbar beim Aufbau. Die Sichtbarkeitspruefung laeuft dabei
je Eintrag, nicht einmal pauschal -- ein fremder Eintrag taucht auch in
der Sammelabfrage nicht auf.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 16:49:56 +02:00
DogFatherGitandClaude Opus 5 5960f6e3c4 Unterweisungen mit beidseitiger Bestaetigung - und einer echten Sperre
Der Wunsch: "jeder Scout, Manager oder DogFather muss mit seinen
Creatorn die PDFs durchgehen, und beide druecken dann auf verifiziert,
so dass wir den Beweis haben -- und das ist danach auch nicht mehr zu
aendern."

Das ist kein Haekchen, das ist ein NACHWEIS. Wer so etwas baut, muss
drei Fragen beantworten, sonst ist er wertlos:

WER hat bestaetigt? Beide Seiten getrennt, mit Name, Zeitpunkt und IP.
Eine einzelne Bestaetigung reicht nicht -- "ich habe es ihm gezeigt" und
"er hat es mir gezeigt" sind zwei verschiedene Aussagen, und erst
zusammen ergeben sie einen Beweis. Bestaetigt nur einer, steht die
Unterweisung sichtbar als HALB da, in einer eigenen warnenden Farbe.
Diese Zwischenstufe sichtbar zu machen ist der Punkt: Ein Nachweis, bei
dem nur einer unterschrieben hat, sieht sonst aus wie fertig -- und man
merkt es erst, wenn jemand danach fragt.

WORAUF genau? Nicht auf "die Regeln", sondern auf eine bestimmte Datei.
Beim Bestaetigen wird der SHA-256 der PDF-Datei mitgespeichert. Tauscht
spaeter jemand die Datei aus, passt der Fingerabdruck nicht mehr, und
die Seite sagt das auch ("Das Dokument wurde seit der Bestaetigung
ausgetauscht"). Ohne diesen Wert waere die Bestaetigung ein Zettel ohne
Bezug.

IST ES UNVERAENDERT? Eine abgeschlossene Bestaetigung laesst sich nicht
mehr aendern und nicht loeschen -- und zwar nicht, weil der Code es
nicht anbietet, sondern weil die DATENBANK es ablehnt. Zwei Trigger mit
RAISE(ABORT). Ein Schutz, der nur im Code steht, ist beim naechsten
neuen Weg zur Datenbank wieder weg.

DER VOLLZUG WURDE DURCHGESPIELT. Aus RunOne stammt die Lehre, dass ein
Weg, den man nicht rueckgaengig machen kann, tagelang live sein und NIE
gelaufen sein kann. Die Pruefung bestaetigt deshalb wirklich, schliesst
ab, und versucht dann eine Aenderung -- ueber die Schnittstelle UND
direkt auf der Datenbank. Beide werden abgelehnt.

Und die GEGENPROBE dazu: Der Trigger wird entfernt, dieselbe Aenderung
versucht -- sie geht durch -- und der Trigger wieder gesetzt. Eine
Sperre, die man nicht hat scheitern sehen, ist keine Sperre.

Der Server entscheidet anhand der ROLLE, welche Seite gesetzt wird --
nicht der Absender. Sonst koennte ein Creator die Bestaetigung seines
Betreuers eintragen, und der ganze Nachweis waere wertlos. Eine bereits
gesetzte Seite wird nie ueberschrieben; ein Datum laesst sich also auch
nicht nachtraeglich verschieben.

Die Dokumente kommen aus der Wissens-Bibliothek, es gibt keinen zweiten
Upload-Weg. Sonst gaebe es Dateien, die nur hier existieren -- und
niemand wuesste, welche Fassung die richtige ist. Eine Unterweisung wird
nie geloescht, nur abgeschaltet: Die Nachweise haengen daran.

EIN EIGENER FEHLER, VON DER BROWSERPRUEFUNG GEFUNDEN: Der Aufruf der
Zusatzbloecke stand NACH einem return. laden() steigt frueh aus, wenn
die Liste leer ist -- und dann wurden Vorlagen und Unterweisungen nie
gebaut. Also ausgerechnet auf der leeren Seite, fuer die sie gedacht
sind. Jetzt stehen sie in einem finally.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 16:39:00 +02:00
DogFatherGitandClaude Opus 5 ca10479909 Fertige Vorlagen zum Uebernehmen - Content-Ideen und LIVE-Punkte
Der Wunsch stand an fuenf Stellen gleichlautend: "ich will dass da schon
fertige Sachen stehen". Eine leere Seite mit einem Knopf "Neuer Eintrag"
verlangt vom Creator genau das, was er noch nicht kann -- zu wissen, was
ueberhaupt hineingehoert.

WARUM VORLAGEN UND KEINE VORAUSGEFUELLTEN DATEN. Man koennte beim
Anlegen eines Creators dreissig Eintraege in seine Datenbank schreiben.
Das waere falsch: Sie waeren ab dem ersten Tag "seine" Eintraege und
damit Altlast; aendert Filipe spaeter eine Formulierung, gilt sie nur
fuer neue Creator; und die Liste saehe voll aus, obwohl noch nichts
geschehen ist -- das Gegenteil einer ehrlichen Uebersicht.

Vorlagen bleiben deshalb VORSCHLAEGE, bis jemand sie uebernimmt. Sie
stehen im Code, gelten fuer alle sofort, und werden erst dann zu Daten,
wenn sie gebraucht werden. Was uebernommen ist, ist ein ganz normaler
Eintrag -- aenderbar, loeschbar, und von spaeteren Aenderungen an der
Vorlage unberuehrt.

INHALTE, fachlich begruendet:
  13 Content-Ideen, jede mit AUSFORMULIERTEM Aufhaenger und Format. Die
  ersten ein bis drei Sekunden entscheiden ueber die Verbreitung --
  "mach was Persoenliches" hilft niemandem, "Das haette ich am Anfang
  gern gewusst" kann man sagen. Verteilt nach 70/20/10 (Wert, Community,
  Eigenwerbung).

  21 LIVE-Punkte in drei Abschnitten. Der mittlere ist der wichtigste
  und gibt es sonst nirgends: Was WAEHREND der Sendung auffaellt, ist am
  naechsten Tag weg. Diese Punkte sind so formuliert, dass man sie in
  einem Moment anklicken kann, in dem man eigentlich keine Zeit hat.

  Die Vorbereitung ist die laengste Liste, weil ein LIVE dort steht und
  faellt. Ton zuerst -- der Grund Nummer eins, warum Leute wieder gehen.

"ICH BRAUCHE HILFE" wird eine AUFGABE, keine Nachricht. Eine Nachricht
ist gelesen und dann weg; eine Aufgabe bleibt stehen, bis sie jemand
erledigt, geht an den zustaendigen Betreuer (ohne Betreuer an die
Leitung -- eine Bitte um Hilfe darf nicht ins Leere laufen), traegt hohe
Prioritaet und eine Frist von drei Tagen. Ohne Frist bleibt sie liegen;
das ist der Unterschied zwischen einer Aufgabe und einem Zettel.

"CONTENT-PLANUNG" HEISST JETZT "CONTENT-IDEEN". "Planung" klang nach
Terminen und Tabellen; was dort wirklich passiert, ist das Sammeln und
Weiterentwickeln von Ideen. Der Kalender daneben plant.

EIN CREATOR DARF SEINE EIGENEN EINTRAEGE AENDERN -- aber nur im Bereich
Content. Wenn er sich eine Idee uebernimmt, ist das SEINE Idee; sie
danach nicht umbenennen zu duerfen waere absurd. LIVE, Technik,
Community und Schutz bleiben die Betreuungsakte, dort aendert er nichts.
Ein erster Anlauf hatte die Ausnahme fuer alle Bereiche erlaubt -- die
Pruefung pruef-bereiche-lesend hat das sofort gemeldet.

DREI EIGENE FEHLER, VON DEN PRUEFUNGEN GEFUNDEN:
- Der Vorlagenblock vergass nach dem Uebernehmen, was schon geholt war:
  Die Liste wird neu geladen, der Block neu gebaut, und die Markierung
  am Element war jedes Mal weg. Jetzt merkt sich das Modul die Auswahl.
- Die Regel fuer eigene Eintraege war zu breit (siehe oben).
- Die Marken im Vorlagenblock waren auf dem Handy 10,2 px klein.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 16:27:30 +02:00
DogFatherGitandClaude Opus 5 6996c5957d Formularzeilen gerade geruckt, Pipeline erklaert sich selbst
DIE VERZOGENE FORMULARZEILE hatte drei Ursachen, nicht eine -- und jede
einzelne haette gereicht, damit es schief aussieht:

1. Die Rasterregel galt nur fuer Kinder mit der Klasse .feld. Der
   Kalender benutzt schlichte <div> ohne Klasse; die fielen hindurch,
   bekamen von zwoelf Spalten je EINE und wurden nur so breit, wie ihr
   Inhalt sie zwang. Daher "Art" schmal und "Beginn" breit.
2. "Dauer (Minuten)" brach in der schmalen Spalte auf zwei Zeilen um --
   und schob das Feld darunter tiefer als seine Nachbarn. Die Klammer
   ist jetzt ein leiser Zusatz, die Beschriftung hat feste Hoehe.
3. Die letzten drei Pixel: Mit grid-template-rows: 1fr auto bestimmte
   jedes Element die Zeilenhoehe selbst. Ein <select> mass sich 3 px
   kleiner als ein <input> und sass dadurch hoeher. Gemessen: Container
   beider Felder identisch (652+71), Eingabe aber 676..720 gegen
   679..723. Drei Pixel klingen nach nichts und sind genau das, was man
   als "verzogen" sieht. Jetzt hat die Zeile feste Hoehe und das Feld
   fuellt sie ganz.

Ergebnis, gemessen statt betrachtet: alle Felder 44 px hoch, alle 362 px
breit, alle Unterkanten auf einer Linie. Auf dem Handy untereinander --
zwei Felder auf 300 px sind zwei Streifen, in die nichts hineinpasst.

NEUE PRUEFUNG pruef-formulare.mjs. Sie prueft nicht "sieht gut aus",
sondern misst: gleiche Hoehe, Unterkanten auf einer Linie, kein Feld
absurd schmal, keine Beschriftung mehrzeilig -- auf fuenf Seiten und
zwei Geraetegroessen. Zwei Messfehler darin selbst gefunden und behoben
(Teilpixel-Rundung, und eine Ausgabe ueber mehrere Zeilen, die die Datei
zerschossen hat).

DIE SCOUT-PIPELINE ERKLAERT SICH JETZT SELBST. Vorher stand im leeren
Zustand ein Satz, der nur wiederholte, was man ohnehin sieht: dass
nichts da ist. Wer die Seite zum ersten Mal oeffnet, wusste danach
weiterhin nicht, wofuer es sie gibt.

Der leere Zustand ist der EINZIGE Moment, in dem jemand garantiert
liest, was dort steht -- spaeter ist die Flaeche von Daten belegt.
Deshalb steht die Erklaerung genau dort und nicht in einer Hilfe, die
niemand aufmacht. Erklaert wird der NUTZEN, nicht die Bedienung: nicht
"hier klicken", sondern warum ein Scout ohne diese Liste Leute verliert
-- naemlich die Interessierten, bei denen drei Wochen nichts passiert
ist, und nicht die, die Nein sagen. Dazu die fuenf Stufen mit je einem
Satz, was sie bedeuten.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 16:11:53 +02:00
DogFatherGitandClaude Opus 5 568909ba87 Bibliothek: nur je einer oben, "wichtig" laeuft ab, Knopf steht fest
NEU & WICHTIG zeigte bis zu sechs Karten mit je vier Knoepfen -- eine
halbe Bildschirmseite, bevor ueberhaupt eine Kategorie zu sehen war.
Jetzt stehen genau ZWEI da: der wichtigste und der neueste. Bewusst je
einer und nicht die ersten zwei -- das sind zwei verschiedene Antworten
("was soll ich unbedingt lesen" und "was ist dazugekommen"). Alles
Weitere ist einen Klick entfernt, der Knopf nennt die Zahl.

"WICHTIG" LAEUFT JETZT EBENFALLS NACH 48 STUNDEN AB. Vorher blieb es
stehen, bis es jemand von Hand abschaltete -- mit dem absehbaren
Ergebnis, dass oben nach ein paar Wochen eine Liste von Dingen steht,
die laengst niemanden mehr angehen. Niemand raeumt so etwas auf.

Der Schalter bleibt trotzdem sinnvoll: Er hebt einen Eintrag fuer zwei
Tage nach oben, auch wenn dieser aelter ist. Er ist damit ein
Scheinwerfer, kein Regal.

DER AKTIONSKNOPF SPRANG -- "einmal rechts, einmal links". Die Ursache
war nicht die Seite, sondern die TEXTLAENGE: Die Kopfzeile ist eine
umbrechende Flex-Zeile, und der Textblock daneben durfte wachsen. Bei
kurzer Unterzeile blieb der Knopf rechts, bei langer ("Community-
Richtlinien, erlaubte und verbotene Inhalte, Altersprüfung, Sperren,
Verwarnungen, Datenschutz, Jugendschutz und sicheres Verhalten")
rutschte er darunter. Es sah aus wie zwei verschiedene Seiten und war
dieselbe Regel.

Jetzt schrumpft der Textblock und der Knopf nicht -- er steht auf jeder
Seite an derselben Stelle. Auf dem Handy rutscht er bewusst darunter und
nimmt die volle Breite, dort ist nebeneinander kein Platz.

NEUE PRUEFUNG pruef-wissen-neu.mjs. Sie legt ausdruecklich Eintraege mit
einem Zeitstempel von VOR DREI TAGEN an. Mit frischen Daten waere alles
neu, ein abgelaufener Eintrag kaeme nie vor, und die Pruefung waere
gruen, ohne den Fall je gesehen zu haben. Geprueft wird ausserdem, dass
das Aufklappen WIRKLICH mehr zeigt -- ein Knopf, der nur seine
Beschriftung aendert, ist keiner.

Zwei Fehler in der Pruefung selbst gefunden: Sie schrieb in Spalten, die
so nicht heissen (datei statt name_datei), und suchte die Karten unter
".karte" -- sie heissen .pdf, und der Ausdruck zaehlte stattdessen die
umschliessenden Container.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 15:50:20 +02:00
DogFatherGitandClaude Opus 5 2bebab156b Profilbild gebaendigt, Steckbrief von der Creator-Akte getrennt
ZWEI FEHLER, DIE FILIPE GESEHEN HAT.

1. DAS PROFILBILD LAG UEBER DER HALBEN SEITE. Als Patrick sein Bild
   hochlud, zog sich ein roter Balken quer ueber die Startseite.

   Ursache: Die CSS-Regel .wer__bild hat GEFEHLT. Das Element wurde in
   kopf.js erzeugt, die Regel dazu nie geschrieben -- und ein <img> ohne
   Groessenangabe nimmt seine natuerliche Groesse an, bei einem Handyfoto
   also mehrere tausend Pixel. Dazu fehlte overflow: hidden am Traeger.

   WARUM KEINE PRUEFUNG DAS GEFUNDEN HAT: Sie lud ein 1x1-Pixel-PNG hoch.
   Klein, schnell, von Hand gebaut -- und voellig unfaehig, irgendetwas
   zu ueberdecken. Die Pruefung war gruen, der Fehler war da, und gesehen
   hat ihn der Nutzer. Sie arbeitet jetzt mit einem 1200x1200-Bild, also
   in der Groesse, die wirklich hochgeladen wird, und misst danach: Bleibt
   das Bild in seinem 28-px-Feld, ragt es irgendwo heraus, laeuft die
   Seite ueber. Gegenprobe gemacht -- ohne die Regel meldet sie
   1200x1200 in 28x28 und 918 px Ueberlauf.

2. ZWEI PERSONEN AUF EINEM BILDSCHIRM. Auf der Profilseite stand oben
   "Profil: SpongBobSchwammKopf" und mittendrin "MEIN PROFIL: Dogfather".
   Niemand konnte sagen, welche Angabe zu wem gehoert.

   Es sind auch wirklich zwei verschiedene Dinge:
     MEIN STECKBRIEF  gehoert MIR -- Bild, ein Satz ueber mich, meine
                      Kanaele. Fuehre ich selbst.
     CREATOR-PROFILE  die BETREUUNGSAKTE eines anderen Menschen --
                      Ziele, 90-Tage-Plan, interne Notizen. Fuehren die
                      Betreuer.

   Scouts, Manager und DogFather haben jetzt zwei getrennte Kacheln und
   eine eigene Seite (steckbrief.html, serverseitig geschuetzt). Ein
   Creator behaelt beides zusammen -- er hat nur eine Seite und sieht
   dort ausschliesslich sich selbst. Die Logik liegt in einer eigenen
   Datei statt hinten an profil.js: Sie gehoert der angemeldeten Person,
   nicht der Akte.

ACHTZEHN KACHELN, ACHTZEHN FARBEN. Die neue Kachel haette sich ihren
Farbton mit den Creator-Profilen geteilt -- zwei Nachbarn in derselben
Farbe. Statt eine Farbe dazuzuerfinden, wurde tools/kachel-farben.mjs
fuer 18 Winkel neu gerechnet, samt der neuen Nachbarschaft im Raster.
Der kleinste Abstand zweier Nachbarn liegt weiterhin bei 100 Grad.
Gefunden hat das die Startseitenpruefung ("17 Farben auf 18 Kacheln").

Die Trennung wird jetzt fuer JEDE Rolle geprueft: welche Kacheln sie
sieht, dass auf der Akte kein fremder Steckbrief steht, und dass die
eigene Seite die richtige Person zeigt. Eine alte Pruefung, die das
Gegenteil verlangte, wurde ersetzt statt stehen gelassen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 15:04:23 +02:00
DogFatherGitandClaude Opus 5 1fe443e59c Handy durchgeprueft, Lichtfarbe je Kachel, haengendes Licht behoben
DAS LICHT BLIEB HAENGEN -- vier Ursachen, jede einzeln behoben:

1. EIN WETTLAUF. Zwischen dem Anmelden eines Bildaufbaus und seinem
   Ablauf konnte der Zeiger die Karte laengst verlassen haben. Dann hatte
   pointerout schon aufgeraeumt, und der Bildaufbau schaltete das Licht
   gleich wieder AN. Der haeufigste Fall und der unauffaelligste.
2. UEBER EINE LUECKE VERLASSEN. Wer eine Karte nicht ueber eine
   Nachbarkarte verliess, sondern ueber den Zwischenraum, loeste kein
   Ereignis aus, das die alte Karte kannte.
3. GESCROLLT, OHNE DIE MAUS ZU BEWEGEN. Die Karte wandert unter dem
   stehenden Zeiger weg -- es kommt gar kein Zeigerereignis. Jetzt wird
   beim Scrollen nachgesehen, ob die beleuchtete Karte noch unter dem
   Zeiger liegt.
4. FENSTER ODER TAB VERLASSEN. Auch dort kommt nichts mehr.

Es gibt jetzt genau EINE beleuchtete Karte -- mehr kann es nicht geben,
es gibt ja nur einen Zeiger. Beim Wechsel geht die alte aus, bevor die
neue angeht.

DIE FARBE GEHOERT ZUR KACHEL. Auf der Wissensseite leuchteten alle sechs
Welten violett, weil die Farbe dort --w heisst und der ganze uebrige
Workspace --ton benutzt. Das Licht griff auf --ton zu, fand nichts und
nahm den Farbton der SEITE. Dieselbe Luecke bei den Aufgaben-Spalten
(--sfarbe), den Kalenderkarten (--afarbe) und den Kalenderzeilen
(--zfarbe). Alle vier setzen jetzt --ton mit.

Nachgewiesen an echten Bildpunkten, nicht am Quelltext: Der Zeiger wird
auf die Aufgaben-Kachel gesetzt (dort muss Rot ueberwiegen: 85 zu 22)
und auf die Kalender-Kachel (dort Blau: 71 zu 13). Ein
Zeichenkettenvergleich haette das nicht gekonnt -- der Browser rechnet
color-mix() aus und schreibt je nach Fassung rgb(), color() oder oklab()
zurueck.

DAS HANDY, komplett durchgeprueft: neue pruef-handy.mjs faehrt alle
fuenfzehn Seiten auf DREI echten Geraetegroessen ab (320 px iPhone SE,
390 px iPhone, 412 px Android) und misst fuenf Dinge, die am Rechner
unsichtbar sind. Gefunden und behoben:

  * ZWEI ECHTE UEBERLAEUFE (startcheck +27 px, automation +33 px). Die
    Seite liess sich waagerecht schieben -- auf einem Telefon der
    schlimmste Fehler. Ursachen: zwoelf Raster mit festem Mindestmass
    (minmax(280px, 1fr) kann nicht schrumpfen -> min(280px, 100%)),
    zwoelf feste Mindestbreiten an Auswahlfeldern, und ein "flex: none"
    an den Stufen-Knoepfen, das jedes Schrumpfen verbot. "width: 100%"
    allein reichte dort nicht.
  * BERUEHRZIELE unter 24 px (WCAG 2.2, Kriterium 2.5.8). Auswahlfelder
    waren 18 bis 22 px hoch. Jetzt 44 px -- die Empfehlung von Apple und
    Google, und der Daumen ist nun einmal breiter als ein Mauszeiger.
    Nebenbei behebt die Schriftgroesse 16 px das Hineinzoomen von iOS.
  * SCHRIFT unter 11,7 px an 52 Stellen. Gesucht wurden sie nicht von
    Hand -- das waere ein Ratespiel gewesen und haette die Haelfte
    uebersehen -- sondern durch Durchsuchen der Stildateien nach
    font-size unter 0,73rem.

ZWEI FEHLER IN DEN EIGENEN PRUEFUNGEN gefunden und behoben: Die
Schriftregel griff zuerst gar nicht (start.css wird VOR den Seitenstilen
geladen, bei gleicher Staerke gewinnt die spaetere -- jetzt mit
body.start qualifiziert), und die Lichtpruefung scrollte 600 px und
stellte nicht zurueck, sodass die folgende Pruefung ins Leere zeigte.

GEGENPROBE gemacht: Mit absichtlich eingebauten Fehlern (8-px-Schrift,
500 px breiter Inhalt) meldet die Handypruefung sofort Rot. Eine
Pruefung, die immer bestaetigt, bestaetigt nichts.

Alle achtzehn Pruefungen laufen gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 14:48:48 +02:00
DogFatherGitandClaude Opus 5 41ffb91688 Das Licht folgt dem Zeiger - auf jeder Karte, und der Rand mit
Auf der Startseite gab es einen Lichtfleck unter dem Zeiger. Jetzt auf
JEDER Karte im ganzen Workspace -- und mit zwei Schichten statt einer:

  der FLECK   ein weicher Schein unter dem Zeiger, in der Farbe des
              Bereichs (--ton). Eine Aufgabenkarte leuchtet orange, eine
              Kalenderkarte tuerkis.
  der RAND    eine helle Stelle, die auf der KANTE mitwandert.

Der Rand ist der Teil, der den Unterschied macht. Er entsteht aus einem
Farbverlauf, von dem eine Maske nur den ein Pixel breiten Saum stehen
laesst: zwei Ebenen, eine ueber die Innenflaeche, eine ueber den ganzen
Kasten, und mask-composite: exclude laesst genau die Differenz uebrig --
den Rahmen. Dadurch leuchtet wirklich die Kante an der Stelle, an der
der Zeiger steht, statt eines Rechtecks, das so tut als ob.

Beides steckt in einem eingefuegten <span class="licht">. Ein eigenes
Element statt ::before/::after am Kasten selbst, weil die bei fast
allen Karten schon belegt sind (Akzentstreifen, Wasserzeichen) -- ein
Pseudo-Element doppelt zu benutzen geht nicht, und der Fehler faellt
erst auf, wenn eines von beiden verschwindet.

Kosten: EIN Zuhoerer fuer das ganze Dokument, gedrosselt auf einen
Bildaufbau, passiv angemeldet. Das Licht-Element wird beim ersten
Ueberfahren eingesetzt, nicht beim Laden -- Karten entstehen laufend
neu, wenn Listen sich aktualisieren. Ohne feinen Zeiger (Handy) und bei
"weniger Bewegung" laeuft gar nichts und es wird auch nichts eingefuegt.

pointerout feuert auch beim Wechsel zwischen Kindern INNERHALB einer
Karte. Ohne die Pruefung auf relatedTarget haette das Licht bei jeder
Bewegung ueber ein Wort hinweg geflackert.

DIE ALTE FASSUNG IN start.js IST WEG. Sie stehen zu lassen haette zwei
Zuhoerer auf denselben Bewegungen bedeutet -- doppelte Arbeit bei jedem
Zeigerzucken, und der Fehler waere erst aufgefallen, wenn jemand die
eine geaendert haette und sich nichts tat.

GEPRUEFT AN ECHTEN BILDPUNKTEN, nicht am Quelltext: Der Zeiger wird in
die Mitte einer Kachel gesetzt, dann wird die Helligkeit der Kante
direkt darueber gegen dieselbe Kante an der fernen Ecke gemessen.
Ergebnis 50 gegen 17. Geht die Maske jemals verloren, waere die ganze
Karte eingefaerbt und der Text unlesbar -- das faellt damit sofort auf.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 11:51:47 +02:00
DogFatherGitandClaude Opus 5 766c75b51d Die Buehnen richtig: nicht mehr beschnitten, hell, mit Lesespur
Drei handfeste Fehler, alle im Screenshot zu sehen gewesen.

1. ABGESCHNITTEN. background-size stand auf "138% auto". Auf einem
   2540 px breiten Schirm wurde das Bild damit 3500 px breit und knapp
   2000 px hoch -- in einem 1300 px hohen Fenster fehlten 700 px, und
   zwar oben. Genau deshalb waren die Figuren riesig und ihre Koepfe
   weg. Jetzt cover mit Verankerung auf 50% 42%: Die Flaeche wird immer
   gefuellt, so wenig wie noetig skaliert, und wenn etwas beschnitten
   werden muss, dann Fussboden und Decke -- nicht die Gesichter.

2. DIE VIGNETTE LAG FALSCH HERUM. Abgedunkelt wurde die MITTE, also
   genau der Teil, in dem die Szene steht. Man sah die Raender des
   Bildes und in der Mitte einen grauen Fleck. Jetzt laufen die RAENDER
   ins Dunkle und die Mitte bleibt klar -- so wie in der Fotografie.

3. ZWEI SCHICHTEN DUNKELHEIT. Zusaetzlich zur Vignette legte der
   Schleier .86/.58/.44 Schwarz ueber dasselbe Bild. Jetzt nur noch
   oben (Kopfleiste) und unten (Seitenende) ein Streifen.

Dazu die Bilder selbst: Helligkeit 0.66 -> 0.88, mehr Kontrast und
Farbe. Sie sind ein BILD, kein Nebel.

DIE LESESPUR ist der eigentliche Kniff. In allen neun Szenen stehen
HasiDog und DogFather AUSSEN, die Mitte ist frei -- danach wurden sie
ausgesucht. Diese Aufteilung wird jetzt benutzt statt bekaempft: aussen
bleibt das Bild hell und scharf, in der Mitte (wo der Text steht) wird
gedaempft. Der klare Rand ist in jedem Fenster mindestens 400 px breit.
Auf dem Handy gibt es daneben keinen Platz, dort deckt die Spur alles.

Was frei stand und keine Karte hatte, bekommt eine Lesezone mit
backdrop-filter: Der Hintergrund bleibt in Farbe und Form sichtbar,
wird an dieser Stelle aber weichgezeichnet. Die billige Loesung waere
gewesen, das Bild wieder abzudunkeln -- damit waere man dort, wo man
angefangen hat.

ZWEITER FUND AN DER EIGENEN KONTRASTMESSUNG. Sie tastete
ausschliesslich NEBEN dem Element ab. Traegt ein Text seine Flaeche
aber selbst (eine Beschriftung mit Hintergrund und Polsterung), liegt
jeder Punkt daneben schon auf dem Bild -- gemeldet wurden 1,89:1,
obwohl der Text auf deckender Flaeche steht und tadellos lesbar ist.
Jetzt wird zuerst in der eigenen Polsterung gemessen, also auf dem,
worauf der Text WIRKLICH liegt. (Der erste Fund an derselben Stelle war
gestern: Sie mass Text gegen Nachbartext.)

Nach dem Aufhellen einmal komplett durchgemessen: von sieben roten
Stellen auf null. Schlechtester Wert jetzt 4,70:1 bei 4,5:1 Norm.

Die Bilder sind groesser geworden (580 KB -> 1,8 MB fuer alle achtzehn),
weil weniger Dunkelheit weniger komprimierbar ist. Geladen wird pro
Seite genau eine: 42 bis 132 KB.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 11:43:08 +02:00
DogFatherGitandClaude Opus 5 299ef0d506 Eigenes Profil fuer jeden - mit Bild und Kanaelen
SCREEN 1: Jeder im Workspace hat jetzt seinen eigenen Steckbrief --
Creator, Scout, Manager und DogFather gleichermassen. Ein Bild, ein Satz
ueber sich, und die Kanaele, auf denen er unterwegs ist. Er steht ganz
oben auf profil.html, ueber der Creator-Akte; die ist etwas anderes
(die fuehren die Betreuer, den Steckbrief fuehrt man selbst).

Das Bild erscheint sofort ueberall: in der Kopfleiste jeder Seite und in
der Begruessung. /api/ich liefert es deshalb gleich mit -- das spart auf
jeder Seite eine zweite Abfrage und verhindert, dass die Plakette erst
als Buchstabe erscheint und dann umspringt. Faellt das Bild aus, steht
der Buchstabe da: Er wird nicht ersetzt, sondern liegt darunter.

WARUM KEINE ECHTE TIKTOK-ANMELDUNG. "Mit TikTok verbinden" klingt nach
OAuth. Das waere: ein Entwicklerkonto mit App-Freischaltung, eine
Pruefung durch TikTok (Wochen, widerrufbar), je Person ein Zugriffstoken,
das ablaeuft, erneuert werden muss und -- wenn es abhandenkommt --
fremden Zugriff auf ein fremdes Konto bedeutet. Dafuer bekaeme man
Follower-Zahlen.

Gebraucht wird hier aber "Wer ist das, und wo finde ich ihn?". Dafuer
genuegt der oeffentliche Name. Gespeichert wird deshalb NUR das Handle --
kein Token, kein Passwort, nichts, was abhandenkommen kann. Daraus wird
ein Verweis auf den Kanal. Vier Kanaele: TikTok, Instagram, YouTube,
Twitch. Sollen spaeter echte Zahlen dazukommen, ist das ein eigenes
Vorhaben -- der Name hier bleibt dann trotzdem richtig.

Eingefuegt werden darf, was Leute wirklich in der Zwischenablage haben:
"@name", "https://www.tiktok.com/@name" oder der Name allein. Alles wird
auf den Namen zurueckgefuehrt, statt eine Fehlermeldung zu zeigen.

SICHERHEIT beim Bild -- die drei Stellen, an denen Profilbilder
typischerweise scheitern:
  1. Der Typ wird an der SIGNATUR der Datei geprueft, nicht am
     Content-Type, den der Absender behauptet. Ein umbenanntes SVG mit
     Skript darin kaeme sonst durch und liefe im Namen der Domain --
     mit der Sitzung des Betrachters.
  2. Auf der Platte bekommt jede Datei einen Zufallsnamen. Kein Name
     kann Pfade verlassen oder etwas ueberschreiben.
  3. Ausgeliefert mit nosniff und einer eigenen, alles verbietenden
     Inhaltsregel.
Das Bild liegt als Datei neben der Datenbank, nicht darin: Bilder in
SQLite blaehen jede Sicherung auf, und die laeuft jede Nacht.

Niemand schreibt einem anderen ins Profil -- auch DogFather nicht. Ein
Satz ueber sich und der eigene Kanalname gehoeren der Person. Wer wen
SEHEN darf, folgt der bekannten Regel: Leitung alle, Scout seine
Creator, Creator sich und seine Betreuer. Fremde Profile sind 404, nicht
403.

ZWEI FUNDE DURCH DIE PRUEFUNG:
- Der TikTok-Link, den die App beim Teilen kopiert, endet auf
  "?lang=de&is_from_webapp=1". Der wurde abgelehnt: "sieht nicht richtig
  aus" -- obwohl es der offizielle Link ist. Jetzt werden Parameter und
  Anker mit abgeschnitten.
- Ein zu grosses Bild ergab "500 Serverfehler" statt einer Ansage. Der
  PayloadTooLargeError lief bis in die allgemeine Fehlerbehandlung
  durch. Jetzt 413 mit dem Satz, wie viel erlaubt ist. Von Hand haette
  das vermutlich nie jemand probiert.

Vor der Schemaaenderung wurde eine geprueft vollstaendige Sicherung
gezogen (VACUUM INTO, Integritaet ok, 6 Personen).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 11:31:34 +02:00
DogFatherGitandClaude Opus 5 fc4fde0f02 Buehnen hell, Flaechen dicht - und die Kopfleiste wieder kurz
DIE BUEHNEN WAREN ZU DUNKEL. Bei 0.42 Helligkeit sah man Schemen, nicht
die Szene -- "zu dunkel und sieht ganz komisch aus" traf es genau. Jetzt
0.66 mit etwas mehr Farbe (1.08), die Vignette schwaecher (0.52 statt
0.72). Die Bilder kommen durch.

MOEGLICH IST DAS NUR MIT DEM GEGENZUG: Alle Kacheln, Karten und Spalten
lagen bei 2 bis 3 Prozent WEISS -- also praktisch durchsichtig. Solange
der Hintergrund fast schwarz war, fiel das nicht auf; sobald er hell
wurde, lief das Bild mitten durch den Text. Jetzt gibt es --flaeche
(rgba(9,13,22,.78)), eine deckende dunkle Flaeche, und 44 Stellen in 16
Dateien holen sich ihre Farbe von dort.

Der Unterschied ist genau der, den Filipe beschrieben hat: Der
Hintergrund bleibt sichtbar, aber ZWISCHEN den Kacheln -- nicht hinter
der Schrift. Wer die Flaechen kuenftig dichter oder durchlaessiger will,
aendert eine einzige Zeile statt 44.

Die Kacheln selbst tragen ihren Farbton jetzt AUF der Flaeche
(color-mix mit --flaeche statt mit transparent) -- dadurch bleibt die
Farbe erkennbar, ohne dass die Kachel durchsichtig wird.

DIE KOPFLEISTE war so breit wie das ganze Fenster, obwohl der Text kurz
ist: .marke__text stand auf flex: 1 1 auto. Das war unsichtbar, solange
der Schriftzug nur Text war -- seit er einen Rahmen traegt, lief die
Pille quer durchs Bild. Jetzt flex: 0 1 auto (schrumpfen ja, wachsen
nein). Gemessen: 375 px statt 1280.

Kontrast an echten Bildpunkten auf allen sechs Seiten nachgemessen --
haelt trotz des viel helleren Bildes (schlechtester Wert 4,69:1).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 11:20:02 +02:00
DogFatherGitandClaude Opus 5 0391a91d14 Neun Buehnen statt einer - jede Seite hat ihre eigene Szene
Filipe hat neun Bilder geschickt, damit es VERSCHIEDENE Hintergruende
gibt. Benutzt wurde davon genau eines (das Studio), die uebrigen acht
lagen unbenutzt im Ordner -- lounge.png und skyline.png sogar schon
fertig kopiert. Zu Recht beanstandet.

Jetzt wird aus jeder Szene eine Buehne, breit und schmal, und jede Seite
bekommt die, die zu ihr passt:

  studio      Startseite            der neutrale Ort, alles beginnt hier
  showbuehne  Dashboard, Review     die grosse Buehne, alles im Blick
  garage      Aufgaben, Technik     Werkstatt, hier wird gearbeitet
  skyline     Kalender              Nacht ueber der Stadt, Zeit
  lounge      Calls, Community      Sitzecke, hier wird geredet
  arena       Dateien, Wissen       Archiv hinter dem Portal
  halle       Profil, Personen      die Halle, in der jemand steht
  wald        Start-Check, Scouting der Weg, den man erst sucht
  portal      Content, LIVE         Durchgang, hier entsteht etwas

Die Zuordnung steht in bereiche.js -- derselben Liste, aus der schon
Zeichen und Farbton kommen. Eine Seite traegt damit dreierlei aus einer
einzigen Quelle: Farbe, Zeichen und Buehne. Sie koennen nicht
auseinanderlaufen.

Alle neun bekommen dieselbe Behandlung (abgedunkelt auf 0.42, Vignette
in der Mitte, wo der Text steht, oben ausgeblendet, wo die Kopfleiste
sitzt). Sie sehen verschieden aus und verhalten sich gleich -- eine
Buehne, die je Seite anders hell waere, waere Unruhe statt Vielfalt.
Der schmale Ausschnitt ist je Szene ein anderer, weil ein auf 9:16
gequetschtes Breitbild nur noch Wand zeigt; genommen wird der Bereich,
in dem die Figuren stehen.

18 Dateien, zusammen 580 KB -- pro Seite werden davon zwei geladen
(breit ODER schmal), also 16 bis 48 KB. Die Rohbilder waren 2,4 MB je
Stueck.

Kontrast auf allen sechs geprueften Seiten an echten Bildpunkten
nachgemessen: haelt (schlechtester Wert 4,59:1).

FUND: Die Buehnenpruefung suchte nach "buehne-breit" und fand die neuen
Namen nicht mehr -- sie meldete "die Buehne liegt im Hintergrund: FEHL",
obwohl sie da war.

Die Seitenpruefung sieht jetzt nach, dass jede Seite ihre Szene hat UND
dass das Bild wirklich ankommt: Ein data-buehne ohne passende CSS-Regel
waere still wirkungslos.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 10:36:16 +02:00
DogFatherGitandClaude Opus 5 8647138bb4 Leere Seiten sehen nicht mehr kaputt aus
Die vier Aufgaben-Spalten waren vier graue Kaesten mit je einem
Gedankenstrich darin. Das sieht aus, als sei die Seite kaputt -- dabei
ist eine leere Spalte ein voellig normaler, oft sogar guter Zustand.

Jetzt hat jede Spalte ihre eigene Farbe nach STATUS (offen blaugrau,
in Arbeit blau, Review gold, erledigt gruen), einen auslaufenden
Streifen oben in dieser Farbe und eine Zeile, die erklaert, was die
Spalte ueberhaupt bedeutet -- "Review" allein sagt einem Neuen nichts.
Statt des Gedankenstrichs steht ein Satz, der die AUSSAGE des
Leerseins traegt: eine leere Review-Spalte bedeutet etwas anderes als
eine leere Erledigt-Spalte.

NEUN KOPIEN DERSELBEN REGEL. .leer-hinweis war in neun CSS-Dateien
definiert -- neunmal fast dasselbe, mit Abstaenden von 22, 26 und 30 px,
weil beim Kopieren jedes Mal etwas anders wurde. Genau davor warnt ein
Kommentar in start.css seit August ("Was auf mehreren Seiten benutzt
wird, gehoert hierher"). Jetzt einmal zentral, und jede Seite hat sie.

Und sie sieht anders aus: Der gestrichelte Rand ist weg. Gestrichelte
Raender sagen "hier fehlt etwas", grau auf grau sagt "unwichtig" --
zusammen also "kaputte Seite". Stattdessen eine ruhige, geschlossene
Flaeche im Farbton der Seite (aus data-ton, also aus der Kachelfarbe)
mit einem leuchtenden Ring. Ein Zeichen waere hier zu laut: Es geht ja
gerade darum, dass nichts da ist -- der Ring markiert die Stelle, ohne
etwas zu behaupten.

Kontrast danach nachgemessen: 4,85:1 im schlechtesten Fall.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 10:28:34 +02:00
DogFatherGitandClaude Opus 5 c46fb413ab Jede Seite traegt Zeichen und Farbe ihrer Kachel
Auf der Startseite hat jeder Bereich sein eigenes Zeichen und seinen
eigenen Farbton -- siebzehn unterscheidbare Bereiche statt siebzehn
Kaesten. Bisher endete das an der Kachel: Wer sie anklickte, landete auf
einer Seite, der man nicht mehr ansah, woher sie kam. Dreizehn Seiten,
alle in demselben Blau, alle ohne Zeichen, alle mit derselben duennen
Textzeile als Kopf.

Jetzt wird die Kachel weitergereicht. Der Kopf jeder Seite bekommt
dieselbe Behandlung wie die Kachel: Plakette mit dem Zeichen, der
Bereichsname mit leuchtendem Strich in der Kachelfarbe, ein groesserer
Titel und dasselbe Zeichen noch einmal riesig und fast unsichtbar als
Wasserzeichen dahinter. Aufgaben ist ueberall orange, Kalender ueberall
tuerkis, Personen ueberall rot-gold.

EINE QUELLE STATT ZWEIER LISTEN. Zeichen, Ton, Rolle und Ziel jedes
Bereichs standen nur in start.js. Sie einfach zu kopieren waere der
sichere Weg dazu, dass "Aufgaben" irgendwann auf der Startseite gelb und
auf der Aufgabenseite gruen ist. Beides liegt jetzt in
assets/js/bereiche.js und wird von start.js UND kopf.js benutzt -- wer
eine Kachel aendert, aendert damit automatisch auch den Kopf der Seite.
Sie koennen gar nicht auseinanderlaufen.

Eingesetzt wird die Plakette von kopf.js, nicht in dreizehn HTML-Dateien:
Das vorhandene Markup wird nur umschlossen, nicht ersetzt. Der Kalender
hat einen eigenen Kopf (dort ist der Zeitraum die Ueberschrift) und wird
ausdruecklich mitgenommen -- sonst waere ausgerechnet die aufwendigste
Seite die einzige ohne Zeichen.

Die Seitenpruefung sieht jetzt auf allen dreizehn Seiten nach, dass Ton
UND Zeichen UND Wasserzeichen da sind, und gibt beides aus (ton=9
zeichen=3/3). Eine Seite, die ihre Zuordnung verliert, faellt damit
sofort auf statt erst beim Hinsehen.

Kontrast danach an echten Bildpunkten nachgemessen: haelt auf allen
sechs geprueften Seiten (schlechtester Wert 4,80:1).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 10:22:22 +02:00
DogFatherGitandClaude Opus 5 c01aeb4225 Kalender neu: vier Ansichten, Anlaesse und Feiertage, selbst gerechnet
Der Kalender war eine Liste mit drei Zeitraum-Knoepfen. Jetzt ist er
aufgebaut wie der Redaktionskalender in VanVans Hub -- mit denselben
Bauteilen, aber auf Creator zugeschnitten.

VIER ANSICHTEN auf DENSELBEN Daten. Der Server liefert einen Zeitraum,
hier wird er nur unterschiedlich dargestellt -- so kann keine Ansicht
etwas anderes zeigen als die andere. Monat (Ueberblick und Rhythmus),
Woche (die sieben Tage gross), Liste (was kommt als Naechstes),
Zeitstrahl (wo sich etwas ballt und wo Luft ist).

ANLAESSE UND FEIERTAGE, vollstaendig selbst gerechnet. Ein leerer
Kalender ist nicht nur kahl, er ist nutzlos: Die Frage beim Planen
lautet nie "was habe ich schon eingetragen", sondern "worauf muss ich
zuarbeiten". Ostern ueber die Gauss-Formel, davon abgeleitet Karfreitag,
Himmelfahrt und Pfingsten, dazu die beweglichen Termine (Muttertag,
Black Friday als Freitag nach dem VIERTEN Donnerstag im November, die
vier Advente rueckwaerts vom vierten). Kein Dienst, keine Liste zum
Nachpflegen, keine Kosten, funktioniert offline und im Jahr 2040.

Bewusst NUR die neun bundesweiten Feiertage. Fronleichnam,
Reformationstag und Allerheiligen gelten je nach Bundesland -- ein
Kalender, der sie ueberall anzeigt, waere fuer die Haelfte der Leute
schlicht falsch. Lieber weniger behaupten als etwas Falsches.

Zwei beschriftete Reihen, die zwei VERSCHIEDENE Fragen beantworten:
"Als Naechstes" blickt ab heute nach vorn und aendert sich beim
Blaettern NICHT, "Im September" beschreibt den Monat, den man ansieht.
Ohne Ueberschriften waeren zwei gleich aussehende Reihen verwirrend.

Farbe folgt der Sache, nicht dem Rang -- dieselbe Regel wie bei den
Kacheln: Call blau, Termin violett, Review gruen, Frist orange. Ueberall
gleich: Pille im Raster, Zeile in der Liste, Filterknopf, Balken im
Zeitstrahl. Die KW-Spalte ist kein Schmuck, sondern die uebliche
Waehrung in Absprachen ("machen wir in KW 42").

NEUE PRUEFUNG pruef-kalender.mjs. Sie prueft nicht nur, DASS etwas
dasteht, sondern die gerechneten Tage gegen nachschlagbare Werte:
Ostersonntag 2026/2027/2030, Karfreitag, Himmelfahrt, Muttertag, Black
Friday und den 4. Advent. Ohne das koennte die Osterformel um einen Tag
daneben liegen und niemand wuerde es merken -- bis irgendwann jemand
Karfreitag arbeitet. Dazu: ganze Wochen im Raster, genau ein "heute",
vier Arten in vier Farben, dass ein ausgeschalteter Filter WIRKLICH
etwas ausblendet (ein Filter, der nur die Knopffarbe aendert, ist
keiner), Blaettern in beide Richtungen und der Tagesdialog.

ZWEI FUNDE:
- "Sep" am Monatsersten lag bei 3,96:1, wenn der Erste zufaellig HEUTE
  ist -- dann liegt der Text auf der aufgehellten Zelle. Nur an echten
  Bildpunkten zu sehen, nie an der Farbangabe.
- Die eigene Pruefung blaetterte stur vorwaerts und erreichte April 2026
  nie (heute ist September). Zehn rote Meldungen, die wie ein
  Rechenfehler in der Osterformel aussahen. Jetzt wird die Richtung aus
  dem offenen Monat bestimmt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 10:14:47 +02:00
DogFatherGitandClaude Opus 5 e65827a6db Begruessung, Schriftzug, hellerer Hintergrund - und "Wissen" fuer Creator
Vier Wuensche auf einmal, alle auf der Startseite und der Kopfleiste.

BEGRUESSUNG. Dort stand "Angemeldet" ueber dem Namen -- eine Zeile, die
nichts hinzufuegt, weil oben rechts ohnehin Name und Rolle stehen. Jetzt
beantwortet der Kopf die drei Fragen, die man beim Reinkommen hat:
WANN (Wochentag, Datum, Kalenderwoche nach ISO 8601, selbst gerechnet),
WER (Plakette mit dem Anfangsbuchstaben in der Farbe der Rolle -- der
einzige runde Koerper der Seite, dadurch sofort als Person lesbar) und
WIE es steht (ein Satz aus denselben Hinweisen, die darunter stehen --
keine zweite Zaehlung). An Tagen mit Charakter kommt ein Wort dazu:
Neue Woche, Endspurt, Wochenende. Der Gruss ist feiner gestuft und der
Name eigenstaendig ausgezeichnet: das Grusswort leise, der Name kraeftig.

SCHRIFTZUG oben links. Vorher alles gleich laut, der Trenner ein
Satzzeichen wie jedes andere. Jetzt hat er Rang: der Name vorn und
kraeftig, der Ort dahinter und leiser, dazwischen eine kleine Raute in
der Hausfarbe. Aufgeteilt zentral in kopf.js statt in sechzehn HTML-
Dateien, und nur die Textknoten -- Verweise bleiben unberuehrt.

HINTERGRUND heller, wie gewuenscht: Der Schleier liegt bei .86/.58/.44
statt .95/.72/.60, dazu zwei sehr weiche Farbschimmer, die dem Bild das
reine Schwarz nehmen. Der Kontrast wurde danach an echten Bildpunkten
nachgemessen und haelt ueberall (schlechtester Wert 4,78:1).

"WISSEN" statt "Team & System" -- aber nur fuer Creator. Von der Gruppe
bleibt fuer ihn genau eine Kachel uebrig; eine Ueberschrift, die Team
und System verspricht und nur die Bibliothek zeigt, verspricht etwas
Falsches.

DREI FUNDE DURCH DIE PRUEFUNGEN:

1. Die Kontrastmessung war kaputt, seit Text neben Text steht. Sie tastete
   stur 4 px rechts vom Text ab -- und traf dort den NACHBARTEXT statt den
   Untergrund (1,10:1 gemeldet, ohne dass etwas schlecht lesbar war). Jetzt
   werden sechs Stellen rings um den Text angeboten und jede zuerst
   gefragt, ob dort wirklich Untergrund liegt. Strenger, nicht lockerer.
   Texte ohne freie Stelle werden GEZAEHLT und ausgegeben, nicht still
   uebersprungen.
2. bereich.html hatte als einzige Seite kein span.marke__text. Damit
   griffen dort weder die Ueberlauf-Kuerzung noch der neue Rang. Stand seit
   Monaten so, aufgefallen erst, als die Seitenpruefung die Marke auf allen
   dreizehn Seiten nachgesehen hat.
3. Die Datumspruefung haette im Maerz stillschweigend versagt: \w kennt
   ohne Unicode-Schalter kein "ae". Ein Fehler, der sieben Monate wartet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 09:55:25 +02:00
DogFatherGitandClaude Opus 5 1816ed9e1f Hinweisliste "Was ist dran": Zeichen, Zahl und Farbe der Zielkachel
Die Zeilen waren die letzten schmucklosen Elemente der Startseite. Jetzt
traegt jede das Zeichen UND die Farbe der Kachel, zu der sie fuehrt --
Hinweis und Ziel gehoeren dadurch sichtbar zusammen, ohne dass die
Zuordnung ein zweites Mal irgendwo steht (sie kommt aus derselben
Tabelle wie die Zahl auf der Kachel). Die Anzahl steht vorn, getrennt vom
Satz und in gleicher Zeichenbreite; beim Ueberfliegen von sieben Zeilen
liest man genau sie zuerst. Ueberfaellig schlaegt Bereichsfarbe und
bleibt rot -- wenn etwas brennt, zaehlt zuerst, DASS es brennt.

Dabei gefunden und behoben: Die Farbtoene hingen an .kachel[data-ton=n].
Um sie fuer die Hinweiszeilen mitzunutzen, wurde .kachel entfernt --
damit gewann aber die Voreinstellung ".kachel { --ton: var(--akzent) }"
weiter unten in der Datei den Wettstreit der Regeln, und alle siebzehn
Kacheln waeren blau gewesen. Die Voreinstellung steht jetzt in :where()
und zaehlt dabei als nicht vorhanden. Die Browserpruefung hat das
gemeldet ("1 Farbe auf 17 Kacheln"), nicht das Auge.

"Nichts offen" ist kein gestrichelter Kasten mehr, sondern eine gute
Nachricht mit ruhigem Gruen und Haken. Weil er jetzt display:flex hat,
war er staerker als das hidden des Browsers und haette IMMER dagestanden
-- eigene Regel dafuer plus eine Pruefung, die beide Richtungen misst.

Neue Pruefungen (pruef-start-ansicht): Zeichen, vorangestellte Zahl,
keine doppelte Zahl im Satz, ganzer Satz fuer Vorleseprogramme, Farbe
gleich der Kachelfarbe an echten Bildpunkten, rot nur bei ueberfaellig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 09:39:59 +02:00
DogFatherGitandClaude Opus 5 e74254263f Buehne aus den echten Marken-Bildern, Kopfleiste und Begruessung neu
Filipe hatte recht, und zwar zweifach: Der Husky war der FALSCHE (ein
fremder Neon-Husky aus dem alten Bilderordner statt DogFather), und zwei
in die Ecken geklebte Bilder ergeben noch kein Bild.

DIE BUEHNE, zweiter Anlauf. Aus den zehn geschickten Bildern das Studio
gewaehlt, in dem HasiDog und DogFather stehen -- nicht aus Geschmack,
sondern wegen der ANORDNUNG: Die beiden stehen weit aussen, die Mitte ist
dunkel. Genau dort steht der Inhalt. Bei den Bildern mit den Figuren in
der Mitte waeren sie hinter dem Text gelandet.

Ueber die ganze Flaeche, fest an der Scheibe, 138 % breit: Bei 100 %
staenden die beiden bei 20 % und 78 % der Breite -- mitten unter der
Textspalte. Vergroessert man das Bild ueber die Scheibe hinaus, wandern
sie nach aussen (rund 9 % und 88 %) und der leere Hallenboden liegt dort,
wo gelesen wird. Ein Bild groesser zu machen, damit WENIGER davon im Weg
ist, klingt verkehrt und ist genau richtig.

Fuer schmale Schirme ein eigener Hochkant-Ausschnitt derselben Szene --
ein auf 9:16 gequetschtes Breitbild zeigt nur noch Wand.
1,9 MB PNG -> 28 KB WebP.

DIE VIER STELLEN aus den Bildschirmfotos:

* Kopfleiste: der DogFather-Kopf davor. Als MASKE aus dem Logo gerechnet
  (Dunkelheit wird Deckkraft), nicht als Bild -- so laesst er sich im
  Stil einfaerben; ein fertig eingefaerbtes Bild muesste man fuer jede
  Farbe neu bauen. 114 KB -> 3 KB.

* "Dogfather · DogFather" war der peinlichste Punkt: Name und Rolle lasen
  sich gleich, es sah nach einem Fehler aus. Jetzt drei unterscheidbare
  Dinge -- Zeichen mit dem Anfangsbuchstaben, Name, Rolle als Marke in
  ihrer Farbe. Gebaut in kopf.js, EINMAL statt vierzehnmal: Jede
  Seitendatei setzte diese Zeile bisher selbst zusammen.

* Begruessung: leuchtender Strich, groesserer Gruss, auslaufende
  Trennlinie. Dieselben Striche vor "WAS IST DRAN" und "DEINE AUFGABEN" --
  so gehoert sichtbar zusammen, was zusammengehoert.

ZWEI EIGENE FEHLER, die die Pruefungen gefunden haben:

1. Ich hatte Verlaeufe IM TEXT gebaut (background-clip mit durchsichtiger
   Schrift). Sah gut aus, und der Kontrasttest meldete 1,05:1 -- zu Recht.
   Durchsichtige Schrift haengt an einer einzigen Technik; faellt sie aus
   (Kontrastmodus, Druck, aeltere Browser), ist der Text UNSICHTBAR statt
   nur anders gefaerbt. Das ist kein Schoenheitsfehler, das ist ein
   Ausfall. Jetzt feste Farbe mit einem Hauch Leuchten.

2. Auf dem Handy stand der Abmelden-Knopf 9 px aus dem Bild. Die Suche
   nach der Ursache ging zuerst in die Irre -- der Test nannte das
   Wasserzeichen, das aber von seiner Kachel sauber abgeschnitten wird
   und nur seine Masse meldet. Gemessen war es die Marke: 209 px + 139 px
   rechte Gruppe passten nicht in 390 px. Auf dem Handy bleibt jetzt nur
   der Kopf stehen; der Markenzug steht zwei Zeilen tiefer ohnehin als
   Ueberschrift. Fuer Vorleseprogramme bleibt er im Dokument.

Und einmal habe ich mir beim Ersetzen eines CSS-Blocks das halbe
Stylesheet geloescht (die Kachel-Regeln lagen zwischen den beiden
Suchmarken). Aufgefallen sofort am Bildschirmfoto, zurueckgeholt aus Git,
danach zeilengenau ersetzt.

118 Pruefungen, alle gruen. Der Kontrast wird weiterhin an echten
Bildpunkten gemessen, jetzt an bis zu 35 Stellen je Seite.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 05:45:59 +02:00
DogFatherGitandClaude Opus 5 fcebbd18bb Workspace: Casper und HasiDog als Buehne im Hintergrund
Filipe: "der hintergrund soll genau so krass und speziell sein, benutz
den hasen und husky."

Gefunden in assets/img: hero-husky.png (Casper, neonblau auf dunkel --
farblich genau die Oberflaeche) und char-hasidog.jpg (HasiDog). Der
Sticker "HasiDog & Casper" hat bestaetigt, dass die beiden zusammen
gehoeren.

Beide sitzen FEST an der Scheibe in den unteren Ecken -- sie scrollen
nicht mit, sondern stehen da wie ein Buehnenbild, vor dem gearbeitet
wird. Die Mitte bleibt frei, dort steht der Inhalt. HasiDog ist weiter
nach aussen geschoben und schwaecher als Casper: Er ist heller, sein
Gesicht zieht den Blick staerker, und direkt neben einer Kachel wuerde
man ihn ansehen statt der Kachel.

AUFBEREITET STATT EINGEBUNDEN (tools/buehne-bauen.mjs):
  * stark abgedunkelt und leicht entsaettigt -- Stimmung, kein Motiv
  * weiche Raender ins Bild EINGERECHNET. Ein hartes Rechteck saehe nach
    aufgeklebtem Foto aus; und eine Maske ueber ein 900-Pixel-Bild kostet
    bei jedem Bildaufbau Rechenzeit.
  * 290 KB PNG -> 41 KB WebP, 110 KB -> 32 KB
Gerechnet mit dem Browser, der ohnehin fuer die Pruefungen da ist -- kein
Bildprogramm, keine neue Abhaengigkeit, 0 EUR.

GEPRUEFT WIRD NICHT, OB ES HUEBSCH IST, sondern ob der Text noch lesbar
ist -- an echten BILDPUNKTEN aus dem fertigen Bildschirmfoto, nicht an
der Farbangabe im Stil. Die weiss naemlich nichts davon, was
dahinterliegt. Gemessen wird der Untergrund direkt NEBEN jedem sichtbaren
Text, auf fuenf Seiten, auf Computer und Handy: bis zu 34 Stellen je
Seite gegen den WCAG-Massstab (4,5:1, bei grosser Schrift 3:1).

UND DAS HAT SOFORT EINEN ALTEN FEHLER GEFUNDEN: Der leiseste Grauton
(--text-still) lag bei 4,43:1 -- knapp UNTER der Norm. Das war schon
lange so, nur hatte es nie jemand nachgerechnet, weil bisher niemand an
echten Bildpunkten gemessen hat. Von #6d7d92 auf #75859a angehoben; jetzt
4,95:1 auf dem hellsten Untergrund. Das gilt fuer JEDE Seite, nicht nur
fuer die mit dem Hintergrundbild.

Schlechtester Wert jetzt: 4,97:1. Alle 30 Messungen bestanden.

Nebenbei eine Falle in den Pruefungen selbst: Ein haengengebliebener
Testserver auf demselben Port fing die Anfragen ab -- der Test sprach mit
einem ALTEN Prozess und dessen anderer Datenbank, und meldete "no such
table". Der Port ist jetzt ein anderer; die Lehre steht hier, weil das
Bild "Server laeuft" trotzdem erscheint und alles richtig aussieht.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-01 00:10:09 +02:00
DogFatherGitandClaude Opus 5 24048ff0e0 Zustaendigkeit: DogFather und Manager zaehlen wie ein Scout
Filipe: "dogfather soll auch zaehlen wie bananastift und patrick."

Beim Nachsehen war das kein Wunsch, sondern ein Fehlerbericht. In der
Auswahl "Betreut von" stand "DogFather" -- aber als LEER-Wert, nicht als
Person. Es sah aus wie eine Zuordnung und war keine. Genau dieselben
Creator zaehlten deshalb gleichzeitig im Hinweis "Creator ohne
zustaendige Person". Zwei Aussagen ueber denselben Sachverhalt, beide auf
demselben Bildschirm, beide fuer sich stimmig.

Jetzt kann jede betreuende Rolle eingetragen werden -- DogFather, Manager
und Scouts, in der ueblichen Reihenfolge. Der Leer-Wert heisst, was er
ist: "— niemand —". Bei DogFather und Manager steht die Rolle in
Klammern dabei; bei aehnlichen Namen ist sonst nicht zu erkennen, wen man
eintraegt. Und "betreut N Creator" steht jetzt an jeder betreuenden
Person, nicht nur an Scouts.

DER WICHTIGE TEIL: An den RECHTEN aendert das nichts.

Die Zustaendigkeit steuert die Sichtbarkeit NUR beim Scout -- die Leitung
sieht ohnehin jeden Creator. Waere das anders, haette eine
Anzeigeeinstellung still Rechte vergeben. Der Test weist beide Richtungen
nach:

  * Tili auf DogFather eingetragen -> KEIN Scout sieht sie.
  * Tili auf Patrick eingetragen   -> nur Patrick sieht sie, BananaStift
                                      weiterhin nicht.
  * Zurueck auf DogFather          -> Patrick verliert die Sicht wieder.
  * DogFather sieht in allen drei Faellen unveraendert beide Creator.

Ein Creator kann nicht zustaendig sein -- das waere eine Rolle, die es
nicht gibt. Und ein Scout kann die Zustaendigkeit weiterhin nicht selbst
setzen (404), sonst haette er die Rechtevergabe in der Hand, die ihn
begrenzen soll.

20 Pruefungen, darunter die Gegenprobe zum Hinweis: Auf "niemand"
zurueckgesetzt MUSS er wiederkommen, sonst waere er wertlos.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 23:40:27 +02:00
DogFatherGitandClaude Opus 5 7673c12136 Startseite: 17 eigene Farben -- gerechnet, nicht gewaehlt
Filipe: "jede kiste soll seine eigene farbe haben. und die kacheln sollen
viel spezieller, viel spezieller sein."

BEIM ERSTEN MAL HATTE ICH ABGELEHNT, und das war zu bequem. Mein Einwand
stimmte zwar -- ein Versuch mit frei gewaehlten Farben ergab Paare mit
1,4 Grad Abstand, also praktisch dieselbe Farbe -- aber daraus "geht
nicht" zu machen, war falsch. Es geht, man muss nur rechnen.

Der Denkfehler war die Annahme, alle 17 muessten sich voneinander
unterscheiden. Das Auge vergleicht aber nur, was NEBENEINANDER liegt.
Also:

1. 17 Toene, gleichmaessig um den Farbkreis (je 21 Grad), gerechnet in
   OKLCH -- dort sind gleiche Abstaende auch fuer das Auge gleich.
   Helligkeit und Farbstaerke konstant, damit keine sich vordraengt. Wo
   die Farbstaerke den darstellbaren Bereich verliesse (Gelb und Gruen
   frueher als der Rest), wird sie gesenkt, bis sie hineinpasst.

2. Die ZUORDNUNG ist eine Suche ueber die tatsaechlichen Nachbarschaften
   im Raster (nebeneinander UND untereinander). Gesucht: die Anordnung
   mit dem groesstmoeglichen kleinsten Nachbarabstand. Ergebnis:
   mindestens 105,9 Grad zwischen allen Nachbarn.

3. Unter allen Anordnungen, die eine harte Untergrenze schaffen, gewinnt
   die passendste: LIVE rot, Technik gelb, Reports gruen, Personen
   rot-gold, Schutz stahlblau.

Steht als tools/kachel-farben.mjs im Repo, mit festem Zufallsstartwert --
derselbe Lauf ergibt dieselben Farben. Wer Kacheln umsortiert, aendert
die Nachbarschaften und muss es neu laufen lassen; das steht auch in
start.js.

Geprueft: Helligkeitsband, Farbstaerke und Kontrast bestehen fuer alle
siebzehn gegen genau diesen Hintergrund.

VIEL SPEZIELLER -- das WASSERZEICHEN:

Jede Kachel traegt ihr eigenes Zeichen noch einmal, riesig, angeschnitten
und fast unsichtbar (7 % Deckung) in der Ecke. Das ist der Grund, warum
siebzehn Kacheln nicht mehr wie siebzehn Kaesten aussehen: Jede bekommt
eine eigene grosse Form, ohne dass ein einziges zusaetzliches Bild
geladen wird -- es ist derselbe Pfad, nur groesser. Bewusst so schwach,
dass man es nicht liest, sondern nur spuert. Beim Ueberfahren wird es
etwas deutlicher und wandert zwei Pixel.

Dazu: Zeichenfeld 46 auf 52 px mit farbigem Schein darunter, Name auf
1,06 rem, mehr Polsterung. Alles mit prefers-reduced-motion abgesichert.

44 Pruefungen. Neu: dass jede Kachel eine EIGENE Farbe hat (17 Farben auf
17 Kacheln, gemessen an der berechneten Strichfarbe) und dass das
Wasserzeichen da und schwach genug ist.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 23:32:22 +02:00
DogFatherGitandClaude Opus 5 ec712a3a32 Personen: jede Rolle einzeln auf- und zuklappbar
Wunsch Filipe: "ich will ueberall die liste zu machen koennen und nur
aufmachen wenn ich sie sehen will."

Jede der vier Rollen klappt jetzt einzeln auf und zu. Das ERSETZT den
Sammelknopf von vorhin ("5 weitere zeigen") -- zwei Mechanismen
nebeneinander, die dasselbe verstecken, waeren eine Einladung zum
Missverstaendnis. Der Knopf oben rechts macht jetzt etwas anderes: alles
auf einmal ("Alle aufklappen" / "Alle zuklappen"), damit man bei vier
Rollen nicht viermal klicken muss.

Der entscheidende Unterschied zum Ausblenden: Die UEBERSCHRIFT mit der
Anzahl bleibt IMMER stehen, auch zugeklappt. Man sieht jederzeit, DASS es
einen Manager gibt -- nur nicht, welchen. Wer eine ganze Rolle spurlos
verschwinden laesst, haelt sie irgendwann fuer leer.

Und zugeklappt zaehlt genau eine Frage: Steckt da etwas Gesperrtes drin,
das ich sehen muesste? Deshalb steht am Manager auch zugeklappt
"1 gesperrt", in Warnfarbe.

Die Ueberschrift IST der Knopf, als echtes <button> -- ein eigener
kleiner Schalter daneben waere ein zweites Ziel fuer dieselbe Absicht,
und ein kleineres. Als Knopf-Element statt div mit Klick-Zuhoerer machen
Tastatur und Vorleseprogramme es ohne Zutun richtig.

Der Zustand wird JE ROLLE gemerkt, nicht als "alles auf/zu": Wer die
Creator zuklappt und die Scouts offen laesst, findet das nach dem Laden
genau so wieder.

30 Pruefungen. Darunter weiterhin der wichtigste: Zuklappen ist eine
ANSICHT, kein Recht -- Tili kann sich anmelden und normal arbeiten,
waehrend ihre Rolle zugeklappt ist, und der Server liefert unveraendert
alle sieben Personen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 23:17:44 +02:00
DogFatherGitandClaude Opus 5 ac81f288c8 Personen loeschen -- mit Vorschau und einer Sicherung unmittelbar davor
Wunsch Filipe: "ich will auch die moeglichkeit haben die loeschen zu
koennen."

Das ist die einzige Handlung im ganzen Workspace, die sich nicht
rueckgaengig machen laesst. Vier Vorkehrungen:

1. NUR DOGFATHER. Nicht die Leitung, nicht ein Manager -- "nur DogFather
   hat alle endgueltigen Rechte" heisst genau hier etwas. Ein Manager
   kann weiterhin sperren; das reicht fuer den Alltag und ist umkehrbar.
   Alle anderen bekommen 404, auch fuer die Vorschau: Die verraet, wie
   viel an einer Person haengt.

2. VORSCHAU. Vor dem Klick steht da, was MITGEHT (Profil, Start-Check,
   Content-Saeulen, Sitzungen, Zustaendigkeit) und was BLEIBT und nur
   seine Zuordnung verliert (Aufgaben, Termine, Bereichseintraege,
   Dateien, Leads). Eine Aufgabe verschwinden zu lassen, weil jemand
   geht, waere Geschichtsfaelschung.

   Dazu die gefaehrlichste Einzelwarnung: Wer einen Scout loescht, nimmt
   seinen Creators die zustaendige Person weg. Die Vorschau nennt sie
   beim Namen.

3. EINE SICHERUNG DIREKT DAVOR -- und wenn sie scheitert, wird NICHT
   geloescht. Damit ist "geloescht" wiederherstellbar. Das ist der
   eigentliche Gewinn aus der Sicherungsarbeit von heute Nachmittag.

4. Der Name muss getippt werden. Nicht als Schikane: Der Loeschknopf
   sitzt neben dem Sperrknopf, und die beiden sind sehr verschieden. Wer
   den Namen tippt, hat die Zeile gelesen, die er trifft.

ZWEI FEHLER, die erst die Pruefung sichtbar gemacht hat:

a) Alle Loeschungen schrieben in DIESELBE Tagessicherung. Nach der
   dritten kannte sie die erste geloeschte Person nicht mehr -- die
   Sicherung haette genau in dem Fall versagt, fuer den sie da ist.
   Loeschungen bekommen jetzt eine eigene Datei ("vorher-…") mit
   Zeitstempel, die nie ueberschrieben wird. Aufgefallen nur, weil die
   Pruefung die ANZAHL der Dateien zaehlt und nicht bloss, ob eine da ist.

b) Der Zeitstempel hatte Sekundenaufloesung -- drei Loeschungen in
   derselben Sekunde ergaben wieder eine einzige Datei. Jetzt mit
   Millisekunden, plus Zaehler als Notloesung. Und: Eine Sicherung vor
   dem Loeschen setzt NICHT den Vermerk fuer den planmaessigen Lauf,
   sonst faellt die naechtliche Sicherung aus.

Dabei auch ein Fehler in meiner eigenen Pruefung gefunden: Sie griff die
alphabetisch erste Datei und nannte sie "die aelteste" -- "vorher-…-2.db"
sortiert aber VOR "vorher-….db", weil "-" kleiner ist als ".". Sie prueft
jetzt die Eigenschaft selbst: JEDE geloeschte Person muss sich aus
irgendeiner Sicherung zurueckholen lassen.

43 Pruefungen, Schwerpunkt auf dem, was NICHT gehen darf.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 23:13:14 +02:00
DogFatherGitandClaude Opus 5 2dbf9fb57a Personen: eine Kategorie je Rolle, Reihenfolge wie auf der Zugangsseite
Wunsch Filipe: "ich will dass die auch immer die gleiche reihenfolge
haben, am besten sogar jeder seine eigene kategorie und die reihenfolge
genau wie in der zugangsseite."

Dabei kam ein echter Fehler ans Licht. Die Abfrage sortierte mit

    ORDER BY p.aktiv DESC, <Rolle>, p.name

Das "p.aktiv DESC" stand VOR der Rolle -- dadurch wanderte jede gesperrte
Person ans Ende der GESAMTEN Liste, quer durch alle Rollen. Im Bild vom
31.08.2026 stand der gesperrte Manager BanaStift deshalb ganz unten unter
den Creators. Die Reihenfolge DogFather-Manager-Scout-Creator, die
ueberall sonst gilt (ROLLEN_SORTIERUNG, CLAUDE.md), war ausgerechnet auf
der Personenseite aufgehoben -- und es sah nach Absicht aus.

Jetzt: Rolle zuerst, dann Gesperrtes ans Ende SEINER Rolle, dann Name.

Dazu vier Abschnitte mit Ueberschrift, Anzahl und demselben Zusatztext
wie auf der Zugangsseite ("Eigene Pipeline & Kontakte" usw.) -- wer sich
eben angemeldet hat, findet hier dieselbe Sprache wieder. Eine Rolle ohne
Personen wird weggelassen: Eine leere Ueberschrift ist kein
Ordnungsmerkmal, sondern eine Luecke.

Die Gruppen-Gestaltung kommt aus start.css und wird nur wiederverwendet
-- dieselbe Sprache wie auf der Startseite, kein zweiter Entwurf.

Nebenbei ein Eigentor: Der Kommentar zur Sortierung stand zuerst INNERHALB
der SQL-Zeichenkette und enthielt Rueckwaerts-Anfuehrungszeichen. Die
beenden ein Template-Literal -- der Server startete nicht mehr. Steht
jetzt darueber, mit einem Hinweis darauf.

30 Pruefungen. Neu darunter: dass die vier Abschnitte in genau dieser
Reihenfolge stehen, dass der gesperrte Manager bei den Managern steht und
nicht bei den Creators, und dass zugeklappt nur der DogFather-Abschnitt
da ist.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 23:05:04 +02:00
DogFatherGitandClaude Opus 5 099d9a7693 Personenliste: standardmaessig nur DogFather, Rest auf Knopfdruck
Wunsch Filipe: "ich will da nur mich und vanvan sehen, ich will dass ich
einen knopf habe wenn ich die anderen sehen will oder nicht."

Drei Dinge, die dabei nicht schiefgehen duerfen:

1. Das ist eine ANSICHT, kein Recht. Wer eingeklappt ist, ist nicht weg
   -- er wird nur nicht gezeigt. Verwechselt man das, haelt man
   irgendwann jemanden fuer geloescht, der noch vollen Zugang hat. Der
   Test weist das ausdruecklich nach: Tili kann sich anmelden und normal
   arbeiten, waehrend sie ausgeblendet ist, und der Server liefert
   weiterhin ALLE sieben Personen. Gekuerzt wird nur die Anzeige --
   waere es serverseitig, wuerde die naechste Auswertung stillschweigend
   Personen uebersehen.

2. Auf dem Knopf steht IMMER, wie viele gerade fehlen ("5 weitere
   zeigen · 1 gesperrt"). "Alle zeigen" allein sagt nicht, wovon man
   gerade nichts sieht -- und dass eine Person gesperrt ist, gehoert zu
   den Dingen, die man nicht uebersehen darf.

3. Der Zustand bleibt erhalten. Ein Knopf, den man nach jedem Laden neu
   druecken muss, ist keine Einstellung, sondern eine Zumutung.

Gesperrte Personen werden beim Aufklappen ganz normal mitgezeigt -- eine
gesperrte Person zu verstecken waere genau die Zeile, die man sehen
muesste.

24 Pruefungen, Computer und Handy.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 22:59:54 +02:00
DogFatherGitandClaude Opus 5 e25a22149b Startseite: groessere Kacheln, und das Licht folgt dem Zeiger
Rueckmeldung Filipe: "richtige richtung, die sollen nur bissl groesser
sein und noch bissl mehr spezieller."

GROESSER, gemessen statt geglaubt:
  Zeichenfeld  38 -> 46 px (Dashboard 56)
  Name        .93 -> 1.02 rem (Dashboard 1.18)
  Polsterung   14 -> 18 px, Ecken 15 -> 18 px
  Kachelhoehe        rund 60 -> ueber 80 px

Dabei fiel der eigene Test von vorhin sofort ein: Bei 880 px Seitenbreite
sind drei Kacheln je 285 px breit, und in den groesseren Schriften brach
der Text an ZWOELF Stellen ab. Statt die Schrift wieder zu verkleinern
bekommt die Startseite die volle Breite (1240 px) -- das ist die
ehrlichere Loesung. Hinweise, Begruessung und Fusstext behalten ihr Mass
von 940 px: Eine Textzeile ueber 1240 px zu ziehen macht sie nicht
lesbarer, sondern anstrengender.

SPEZIELLER, drei Dinge:

1. DAS LICHT FOLGT DEM ZEIGER. Jede Kachel traegt einen weichen
   Lichtfleck. Er sitzt ruhend oben links und wandert unter dem Zeiger
   mit. Kein Blinken, keine Bewegung des Inhalts -- es wird nur an einer
   anderen Stelle heller. Das ist die Kleinigkeit, die man nicht sieht,
   sondern erst beim Benutzen merkt.

   EIN Zuhoerer fuer alle Kacheln statt siebzehn, und in einem Bild je
   Rahmen: pointermove feuert dutzendfach je Sekunde, wer bei jedem
   Ereignis in den Stil schreibt, laesst den Browser umsonst rechnen.
   Beim Verlassen zurueck in die Ruhelage -- sonst blieben die Kacheln
   nach einer Weile alle unterschiedlich beleuchtet stehen. Auf
   Fingerbedienung und bei "weniger Bewegung" bleibt es ganz aus; dort
   gibt es keinen Zeiger, dem etwas folgen koennte. Beides geprueft.

2. Eine feine helle Linie an der Oberkante jeder Kachel. Ein Pixel, kaum
   sichtbar -- aber die Kachel wirkt dadurch von oben beleuchtet statt
   aufgeklebt. Das Zeichenfeld bekommt denselben Lichtrand und einen
   Verlauf, wodurch es gepraegt statt gemalt wirkt.

3. Beim Ueberfahren: farbiger Schein unter der Kachel im eigenen Ton,
   die Kante links waechst von 3 auf 4 px, das Zeichen um 4 Prozent.
   Alles unter zwei Pixeln Bewegung.

Die Trennlinie der Gruppenueberschriften laeuft nach rechts aus, statt
hart abzubrechen -- eine durchgezogene Linie zerschneidet die Seite, eine
auslaufende gliedert sie nur.

47 Pruefungen. Neu darunter: Zeichenfeld und Schriftgroesse werden
gemessen ("sieht groesser aus" ist kein Nachweis), und das Licht wird mit
echten Mausbewegungen an drei Stellen geprueft.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 22:50:17 +02:00
DogFatherGitandClaude Opus 5 d05a772ee4 Startseite: aus siebzehn gleichen Rechtecken wird eine Uebersicht
Sie war eine Wand: siebzehn identische Kaesten, auf jedem stand "OEFFNEN".
Wenn alles gleich aussieht, ist alles gleich wichtig -- also nichts. Und
"OEFFNEN" ist keine Information; dass eine Kachel sich oeffnen laesst,
weiss man.

Drei Aenderungen, jede mit einem Grund:

1. GRUPPEN statt einer Liste. "Taeglich" (was man sowieso jeden Tag
   aufmacht), "Rund um den Creator" (die Betreuungsakte), "Team & System"
   (was den Laden am Laufen haelt). Das sind drei verschiedene Absichten.

2. ZAHLEN STATT "OEFFNEN". Auf der Kachel steht jetzt, wo Arbeit liegt --
   "Aufgaben 4", rot markiert, weil etwas ueberfaellig ist. Gespeist aus
   DERSELBEN Quelle wie die Hinweisliste darueber, kein zweiter Zaehler:
   Zwei verschiedene Wahrheiten uebereinander auf einem Bildschirm waeren
   schlimmer als gar keine Zahl.

3. ZEICHEN UND FARBE. Siebzehn eigene Linienzeichen (eigene Pfade, keine
   Fremdbibliothek -- kleiner als jede Schriftart und keine zusaetzliche
   Lieferkette) und acht Farbtoene.

Warum acht Toene und nicht siebzehn: Ich hatte zuerst eine eigene Farbe
je Kachel gebaut und das Ergebnis pruefen lassen. Zwei Paare hatten einen
Abstand von 1.4 und 5.6 -- also praktisch dieselbe Farbe. Das sieht nach
Zufall aus, nicht nach Absicht. Die acht jetzt verwendeten sind gegen
genau diesen Hintergrund gerechnet und bestehen alle Pruefungen
(Helligkeitsband, Farbstaerke, Farbfehlsichtigkeit dE 8.4,
Normalsicht 19.3, Kontrast). Verteilt so, dass in einer Zeile nie
zweimal derselbe Ton steht. Die Farbe stuetzt nur -- WAS eine Kachel
ist, sagen Zeichen und Name.

Dazu:
- Begruessung nach Tageszeit statt immer "Willkommen".
- Sechs Nullen nebeneinander sind Rauschen: Ist wirklich nichts offen,
  steht dort ein Satz und der Platz gehoert den Bereichen.
- Die Hinweisliste stand mit sieben Zeilen im Weg. Jetzt vier sichtbar,
  Rest auf Knopfdruck -- derselbe Helfer wie im Protokoll und im Verlauf.
- Gestaffelter Einlauf, aber in einer no-preference-Abfrage: Bei
  "weniger Bewegung" entsteht gar keine Animation, nicht nur eine ohne
  Dauer. Geprueft mit reducedMotion.

Beim Ansehen des ersten Bildes fielen abgeschnittene Untertitel auf
("Stammdaten, Ziele, 90-..."). Abgeschnittener Text ist der haeufigste
stille Fehler in einer Kachel, weil er nach Absicht aussieht. Texte
gekuerzt -- und der Test misst jetzt die tatsaechliche Textbreite gegen
die verfuegbare, damit es nicht wiederkommt.

40 Pruefungen, Computer und Handy, alle vier Rollen: Ein Creator sieht
"Mein Profil" statt der Liste aller Creator, keine Personenverwaltung,
keine Pipeline, keine Automationen -- und trotzdem drei saubere Gruppen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 22:36:29 +02:00
DogFatherGitandClaude Opus 5 43ce6551e2 Bereiche nur lesend fuer Creator + Content-Planung als echte Strecke
ZWEI SACHEN, beide auf Wunsch vom 31.08.2026.

1) "die creator sollen da nur die sehen die wir ihnen eintragen, also
   dogfather manager und scout."

Vorher durfte ein Creator in LIVE, Content, Technik, Community und Schutz
selbst anlegen, aendern und eigene Eintraege loeschen. Das hebelt den
Zweck dieser Bereiche aus: Sie sind die Betreuungsakte. Wer eine
LIVE-Auswertung umschreiben oder eine unbequeme Notiz verschwinden lassen
kann, macht die Akte wertlos.

Der Creator SIEHT weiterhin alles, was zu ihm gehoert -- vollstaendig.
Er kann es nur nicht veraendern. Umgesetzt als EINE Schranke vor allen
schreibenden Wegen, nicht als Pruefung an drei Stellen: Genau so ist hier
schon einmal ein Loch entstanden (zwei von drei Stellen abgesichert, die
dritte vergessen). 403 mit klarem Text statt 404 -- der Bereich existiert
fuer ihn ja, er steht davor.

Vorsicht Express 4: Der Platzhalter heisst dort `*`, nicht `*name` wie in
Fassung 5. Mit der falschen Schreibweise haette die Sperre stumm nichts
getan -- kein Fehler, keine Warnung.

37 Pruefungen, Schwerpunkt auf den verneinenden Faellen: alle fuenf
Bereiche, alle drei Wege, dazu der eigene Alteintrag (der frueher
ausdruecklich erlaubt war) und der Nachweis, dass hinterher wirklich
nichts veraendert wurde.

2) Content-Planung: aus der Liste wird eine Strecke.

Recherchiert statt geraten. Die Quellen sind an drei Punkten eindeutig:

  * "A content calendar for creators is a pipeline, not a datebook" --
    eine Wand aus Terminen verbirgt genau die Stellen, an denen es hakt.
  * Die ersten ein bis drei Sekunden entscheiden ueber die Verbreitung.
    Der Hook ist deshalb ein eigenes Feld auf jeder Karte, kein Fliesstext.
  * Regelmaessigkeit schlaegt Menge. Wer regelmaessig senden will, braucht
    VORRAT -- und Vorrat ist die eine Zahl, die eine Ideenliste
    verschweigt.

Daraus:
  * Fuenf Stufen statt drei (Idee, Hook & Skript, Gedreht, Fertig &
    geplant, Veroeffentlicht). Alte 'produktion'-Eintraege werden beim
    naechsten Start auf 'gedreht' umgestellt -- ohne das fielen sie aus
    jeder Spalte heraus.
  * Neue Felder hook, format, saeule_id, geplant (ALTER TABLE, gefahrlos).
  * Content-Saeulen je Creator, drei bis fuenf, nach der 70/20/10-Regel.
    Mit Balance-Balken ueber 90 Tage, nur Veroeffentlichtes -- was auf dem
    Kanal steht, ist die Wahrheit ueber seine Ausrichtung.
  * Die Kachel VORRAT rechnet in TAGE um ("reicht noch etwa 7 Tage").
    "4 Videos uebrig" sagt nichts; die Tage sagen, ob man am Wochenende
    drehen muss. Gezaehlt wird nur Gedrehtes und Fertiges -- eine Idee ist
    kein Video, und wer Ideen mitzaehlt, steht am Donnerstag ohne Material.
  * "Ideen lassen sich direkt in Aufgabe/Termin/LIVE-Thema ueberfuehren"
    -- woertlich aus dem Konzept, das letzte unumgesetzte Stueck. Die Idee
    bleibt bestehen und bekommt einen Vermerk, damit niemand dieselbe
    Aufgabe zweimal anlegt.

Farben: Die fuenf Saeulenfarben sind Daten, keine Dekoration. Geprueft
gegen genau diesen Hintergrund (#0a0d13) auf Helligkeitsband,
Farbstaerke, Farbfehlsichtigkeit (schlechtestes Paar dE 8.4),
Normalsicht-Abstand (19.3) und Kontrast -- alle Pruefungen bestanden.
Immer mit Legende UND Beschriftung; Farbe allein traegt nie die
Information.

Beim Ansehen des ersten Bildes fiel der klassische Brett-Fehler auf: Die
Spalte "Veroeffentlicht" waechst ewig und schiebt alles andere aus dem
Bild. Sie zeigt jetzt die juengsten sechs, der Rest ist einen Klick
entfernt. Die vorderen Spalten bleiben ungekuerzt -- was dort liegt, ist
Arbeit.

Nur EIN Schreiber fuer die Tabelle eintraege: workspace-content.js
enthaelt ausschliesslich, was es woanders nicht gibt (Saeulen,
Kennzahlen, Ueberfuehren). Zwei Module auf denselben Zeilen waeren der
sichere Weg zu zwei Wahrheiten.

45 + 40 Pruefungen, Computer und Handy, dazu die Gegenprobe: Ein Creator
sieht die ganze Strecke, die Kennzahlen und die Verteilung -- und keinen
einzigen Knopf, auch keinen versteckten. Ausgeblendet ist nicht dasselbe
wie nicht vorhanden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 21:45:14 +02:00
DogFatherGitandClaude Opus 5 1f2823f35a Wiederherstellung: Anleitung auf den automatischen Lauf umgestellt
Der Abschnitt beschrieb noch einen Befehl, den man woechentlich selbst
eintippt. Seit die Windows-Aufgabe laeuft, stimmt das nicht mehr -- und
eine Anleitung, die etwas Falsches behauptet, ist schlimmer als keine.

Dazu ein neuer Abschnitt: woran man sieht, dass es noch laeuft.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 18:35:56 +02:00
DogFatherGitandClaude Opus 5 17c605b93d Sicherung: laeuft von allein, und ein stiller Ausfall faellt auf
Filipe zu Recht: "wieso soll ich diesen Befehl einmal die Woche eingeben,
ich will einmal und dass es dann alleine laeuft". Ein Befehl, den man
selbst eintippen muss, wird irgendwann vergessen -- und zwar genau dann,
wenn er zaehlt.

tools/sicherung-einrichten.ps1 legt eine Windows-Aufgabe an: taeglich
statt woechentlich (der Lauf dauert Sekunden, bei woechentlich waere die
Kopie im schlimmsten Fall sechs Tage alt), 13:30 statt nachts (der
Rechner laeuft nicht durch), und mit StartWhenAvailable -- war der
Rechner aus, holt Windows den Lauf beim naechsten Hochfahren nach. Ohne
diesen Schalter fiele jeder verpasste Termin ersatzlos aus. Braucht keine
Administratorrechte und macht am Ende gleich einen Probelauf: eine
Einrichtung, die man nicht ausprobiert, ist eine Vermutung.

Damit entsteht aber ein neues, schlimmeres Problem: Wenn die Aufgabe
still klemmt oder der Rechner wochenlang aus ist, merkt es NIEMAND. Man
glaubt, man haette eine Kopie ausser Haus, und hat sie nicht. Deshalb
meldet sich das Abholskript nach jedem Lauf beim Server zurueck, und die
Automationen-Seite zeigt, wie alt die Kopie ist -- nach zehn Tagen wird
sie auffaellig.

Die Rueckmeldung laeuft ueber einen eigenen langen Schluessel, nicht ueber
einen Zugangscode: Das Skript laeuft unbeaufsichtigt und muesste einen
Code sonst dauerhaft auf der Platte halten. Der Schluessel kann NUR einen
Zeitstempel setzen -- nichts lesen, nichts aendern. Verglichen wird
zeitunabhaengig.

Dabei eine unauffaellige Falle gefunden: In workspace-aufgaben.js steht
`aufgabenRouter.use("/workspace/api", angemeldet)` -- eine Schranke ueber
JEDEN Pfad unter /workspace/api, nicht nur die eigenen. Alle spaeter
eingehaengten Module leben stillschweigend davon. Die Rueckmeldung wurde
dort mit 401 abgewiesen, bevor ihr Schluessel ueberhaupt geprueft wurde;
der richtige Schluessel sah dadurch aus wie ein Fehler in der Pruefung.
Das Sicherungsmodul haengt jetzt VOR dem Aufgabenmodul und bringt seine
eigene Schranke mit -- damit haengt es an keinem anderen Modul mehr.

Ausserdem: Die Ueberschrift im Kasten nennt jetzt den GRUND, aus dem er
gelb ist. Vorher stand dort "Zuletzt gesichert vor 2 Minuten", waehrend
die Farbe wegen der fehlenden Kopie warnte -- die Anzeige widersprach
sich selbst und man sucht den Fehler an der falschen Stelle.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 18:34:46 +02:00
DogFatherGitandClaude Opus 5 3f18e1bd69 Sicherung: Kopie auf den PC, Dateien mit dabei, Wiederherstellungs-Anleitung
Eine Sicherung, die auf derselben Platte liegt wie das Original, ist keine
-- sie hilft gegen versehentliches Loeschen, nicht gegen einen
Plattenausfall. tools/sicherung-holen.sh holt sie deshalb auf den
Arbeitsrechner.

Dabei ist gleich eine zweite Luecke aufgefallen und mitgeschlossen: Die
Datenbank kennt hochgeladene Dateien und die PDFs der Bibliothek nur ueber
ihren Namen -- die Dateien selbst liegen daneben im Dateisystem und
stecken NICHT in der Datenbanksicherung. Nach einem Plattenausfall haette
man eine Bibliothek voller Verweise auf PDFs, die es nicht mehr gibt.
Werden jetzt mitgeholt.

Bewusst nur auf dem PC und nicht zusaetzlich auf dem Server: Eine zweite
Kopie auf derselben Platte haette gegen nichts geholfen, was die erste
nicht schon ueberlebt haette. Und weil scp nie loescht, bleibt ein einmal
geholtes PDF hier erhalten, auch wenn es auf dem Server verschwindet.

Geprueft wird mit Node statt mit dem sqlite3-Programm: Node bringt SQLite
selbst mit, auf diesem Windows-Rechner ist sqlite3 gar nicht da -- die
erste Fassung gab nur Fragezeichen aus und meldete trotzdem "fertig".

WIEDERHERSTELLUNG.md beschreibt drei Faelle (aus Versehen geloescht /
Datenbank zurueckspielen / Server ganz weg), jeweils als EIN Block zum
Kopieren. Der Ruecklauf legt den kaputten Stand mit mv zur Seite statt
ihn zu loeschen -- falls die Sicherung aelter war als gedacht.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 18:24:32 +02:00
DogFatherGitandClaude Opus 5 7a393b9335 Workspace: naechtliche Sicherung, die auch wirklich Daten enthaelt
Bisher wurde nur EINMAL gesichert: direkt vor einer Umstellung der
Datenbank. Faellt die Platte aus, ist alles weg -- die komplette
Betreuung, jede Aufgabe, jedes Protokoll. Das war das einzige Risiko im
System, das auf einen Schlag ALLES kostet.

Beim Nachsehen auf dem Server kam ein zweiter, schwerwiegenderer Befund
dazu:

    workspace.db          4.096 B
    workspace.db-wal  2.101.232 B

Im WAL-Betrieb landen Schreibvorgaenge zuerst im -wal und wandern erst
beim Checkpoint in die Hauptdatei. Der lief seit dem ersten Tag nicht.
Wer also workspace.db kopiert haette -- der naheliegendste Sicherungsweg
ueberhaupt -- haette eine LEERE Datenbank gesichert und es nicht gemerkt.
Der Test stellt genau das nach: aus der reinen Dateikopie waren 0 von 200
Aufgaben lesbar.

Deshalb:
- Nie eine Dateikopie. Immer VACUUM INTO -- SQLite schreibt selbst, liest
  das WAL mit, Ergebnis ist in sich stimmig auch waehrend Schreibzugriffen.
- Vor jeder Sicherung ein erzwungener Checkpoint (TRUNCATE). Haelt die
  Hauptdatei aktuell und das WAL klein.
- Jede frische Sicherung wird SOFORT wieder geoeffnet und geprueft: auf
  Unversehrtheit UND darauf, dass Personen darin stehen -- gegen genau den
  Fall einer technisch einwandfreien, aber leeren Datei. Faellt sie durch,
  wird sie geloescht statt behalten.
- Erst unter Zwischennamen schreiben, dann pruefen, dann umbenennen. Sonst
  stuende nach einem Abbruch eine halbe Datei unter dem richtigen Namen.
- Aufbewahrung 7 taeglich / 4 woechentlich / 6 monatlich, je Art getrennt
  aufgeraeumt, damit taegliche nie die monatlichen verdraengen.

Laeuft IM Prozess, nicht als Systemdienst: keine Aenderung an Systemdateien
noetig, die Datenbank ist ohnehin offen, und es kommt mit jedem git pull
mit. Statt setTimeout auf 24 h wird viertelstuendlich geprueft, ob fuer
HEUTE schon eine liegt -- das ueberlebt Neustarts und Ausfaelle.

Dazu eine Zustandsansicht auf der Automationen-Seite: wann zuletzt, wie
gross, wie viele Personen darin, wie viel wartet noch im WAL, wie viel
Platz ist frei. Plus "Jetzt sichern" von Hand. Nur Leitung, 404 statt 403
fuer alle anderen.

Geprueft (server/pruef-sicherung.mjs, 27 Pruefungen) einschliesslich des
Ernstfalls: Sicherung schreiben, Datenbank zerstoeren, zurueckspielen,
Vollstaendigkeit und Schreibbarkeit nachweisen. Eine ungetestete Sicherung
ist eine Vermutung.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 18:20:35 +02:00
DogFatherGitandClaude Opus 5 1894395d8f Workspace: lange Protokoll-Listen aufklappbar, zugeklappt vier Zeilen
Die "Letzten Ereignisse" im Personen-Bereich fuellten mit 25 Zeilen die
halbe Seite, obwohl sie Nachschlagewerk sind und kein Startbild. Jetzt
stehen nur die vier neuesten da, der Rest kommt auf Knopfdruck.

Der Knopf sagt, was er TUT ("Alle 25 zeigen" / "Nur die letzten 4") und
nennt die Zahl, damit man weiss, was dahintersteckt. Bei hoechstens vier
Eintraegen bleibt er ganz weg -- ein Knopf, der nichts verbirgt, verwirrt
nur. Der Pfeil dreht sich, aria-expanded stimmt.

Gemeinsamer Helfer in kopf.js statt zweimal derselbe Block: Dieselbe
Liste gab es auf der Automationen-Seite ("Zuletzt automatisch passiert"),
die verhaelt sich jetzt genauso. Zugeklappt wird per CSS
([data-klapp="zu"]) statt durch Entfernen von Zeilen -- das Aufklappen
braucht so weder Neuaufbau noch zweite Abfrage.

Der Zuhoerer wird nur einmal gesetzt (die Listen werden bei jeder
Aktualisierung neu aufgebaut), und die Anzahl wird bei jedem Umschalten
frisch gelesen statt in der Fassung des ersten Durchlaufs festzuhaengen.

Geprueft: beide Seiten, Computer und Handy, zweimaliges Hin- und
Herklappen, Gegenprobe mit drei Eintraegen -- server/pruef-protokoll-klappe.mjs

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 13:27:07 +02:00
DogFatherGitandClaude Opus 5 299d2a23b3 Workspace: Dashboard gebaut, Kopfleiste auf dem Handy repariert
Die Kachel "Dashboard" fuehrte nirgendwohin -- man stand ja schon auf der
Startseite. Statt sie zu streichen bekommt sie ein Ziel: die
Gesamtuebersicht je Creator, die das Konzept auf Seite 1 als "UEBERSICHT
-- alles zentral" verspricht und die es bisher nirgends gab. Die
Startseite bleibt der Einstieg (was ist dran, wohin gehe ich), das
Dashboard ist der Ueberblick (wie steht es um wen).

Eine Karte je Creator: ueberfaellig, dringend, offen, in Arbeit, im
Review, erledigt in 30 Tagen, dazu der naechste Termin, der Start-Check
als Balken und wie lange die Person nicht mehr da war. Jede Zahl ist ein
Verweis -- eine Zahl ohne Weg dorthin waere nur ein schlechtes Gewissen.
Sortiert nach dem, was Handlung verlangt, nicht alphabetisch.

Sichtbarkeit serverseitig wie ueberall: Creator sieht sich selbst
("Mein Stand"), Scout nur seine zugeteilten, DogFather und Manager alle.
Der naechste Review-Termin faellt fuer alle ausser der Leitung weg.

Nebenbei zwei alte Fehler gefunden und behoben:

1. Die Kopfleiste schob auf einem 390px-Handy 214 px aus dem Bild -- der
   Abmelden-Knopf war dort auf JEDER Seite unerreichbar. Ursache: In der
   Flex-Zeile fehlte min-width: 0, also konnte nichts schrumpfen. Auf
   kleinen Schirmen weicht jetzt die Marke (steht direkt darunter noch
   einmal als Ueberschrift) und der Zurueck-Knopf zeigt nur den Pfeil.
   Geprueft von 320 bis 1440 px auf 14 Seiten.

2. .inhalt--breit, .kopf-zeile, .zurueck und .leise standen in
   aufgaben.css -- einer Datei, die eine neue Seite gar nicht laden muss.
   Derselbe Fehler wie frueher bei .knopf-still und .schalter. Jetzt in
   start.css, die jede Seite laedt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 13:17:04 +02:00
DogFatherGit eb87e4a484 Workspace: keine Kachel mehr ohne Ziel -- Automationen bekommen ihren Ort
Filipe: "da sind aber noch zwei kategorien zu". Er hat recht, und es
waren zwei VERSCHIEDENE Fehler.

1) "DASHBOARD" war eine Kachel, die man nicht oeffnen kann -- man steht
   ja schon darauf. Sie stand seit dem ersten Tag als Platzhalter da.
   Ein Eintrag, der nirgendwohin fuehrt, sieht aus wie etwas Unfertiges.
   Entfernt.

2) "AUTOMATIONEN" trug noch "Phase 4", obwohl die Automationen laengst
   liefen -- sie waren nur verteilt (Hinweise auf der Uebersicht, Suche
   ueberall, KI-Vorschlaege an drei Stellen) und hatten keinen Ort.
   Jetzt haben sie einen.

DIE SEITE FUEHRT BEWUSST NICHTS EIGENES. Was dort steht, kommt aus den
Bereichen selbst -- eine Seite, die Automationen doppelt abbildet, waere
die naechste Stelle, die irgendwann etwas anderes behauptet als die
Wirklichkeit. Vier Abschnitte:

- Die lokale KI mit Zustand, Modell, Ort, Kosten und Grenzen -- und dem
  einzigen Schalter der Seite. Sie ist die einzige Automation, die
  Rechenzeit kostet, die der Website gehoert.
- "Was gerade anliegt": dieselben Hinweise wie auf der Uebersicht, hier
  vollstaendig statt gekuerzt.
- "Laeuft dauerhaft im Hintergrund": sieben Regeln im Klartext. Jede
  beschreibt etwas, das tatsaechlich im Code steht.
- "Zuletzt automatisch passiert": aus dem Protokoll, gefiltert auf das,
  was ohne Zutun geschah.

DER KI-SCHALTER WIRD AN ZWEI STELLEN GEPRUEFT: beim Anzeigen der
Knoepfe UND in jedem Endpunkt. Eine Oberflaeche, die etwas versteckt,
ist keine Sperre. Geprueft: abgeschaltet liefert der Endpunkt 403, ein
Creator darf gar nicht schalten (403), die Einstellung ueberdauert einen
Neustart (eigene Tabelle, damit sie dieselbe Sicherung bekommt wie alles
andere).

NEBENBEI DERSELBE FEHLER WIE FRUEHER: Der Schalter-Baustein lag in
wissen.css und fehlte prompt auf der neuen Seite -- dort erschien er als
nacktes Kaestchen. Wie damals bei .knopf-still. Liegt jetzt in gate.css.

Geprueft: 17 Kacheln, KEINE mehr ohne Ziel. Creator wird von der Seite
weggeleitet (302). Handy ohne Ueberlauf, 0 Konsolenfehler.
2026-08-31 12:46:22 +02:00
DogFatherGit 63aea61f39 Aufgaben: Rueckmeldungen ("Aufgaben & Feedback")
Letzter offener Punkt aus dem Konzept, Seite 4: Ein Creator hat laut
Rollenbeschreibung "Aufgaben & Feedback" -- die Aufgaben gab es, das
Feedback nicht. Ohne diese Moeglichkeit endet jede Rueckfrage ausserhalb
des Systems, in WhatsApp, und damit ausserhalb dessen, was spaeter noch
nachvollziehbar ist.

Neue Tabelle aufgaben_notizen. Wer die Aufgabe sehen darf, darf auch
mitreden -- geprueft ueber DIESELBE Sichtbarkeitsregel wie fuer die
Aufgabe selbst. Eine eigene Regel gibt es bewusst nicht: Zwei Regeln
fuer dieselbe Sache laufen irgendwann auseinander, und die Aufgaben-
sichtbarkeit ist die schwierigere von beiden (Creator sieht eigene,
Scout die seiner betreuten Creator, Leitung alles).

404 statt 403, wenn die Aufgabe nicht sichtbar ist -- wer sie nicht
sehen darf, soll nicht erfahren, dass es sie gibt.

Die eigene Rueckmeldung darf jeder zuruecknehmen, fremde nur die
Leitung. Eine Rueckmeldung ist keine Abstimmung: Wer sich vertippt hat,
soll das nicht bei jemandem beantragen muessen.

Der Knopf auf der Karte zeigt die ANZAHL, nicht nur ein Symbol -- so
sieht man ohne Aufklappen, wo schon etwas besprochen wurde, und die
Karte bleibt trotzdem ruhig. Aufgeklappt wird direkt an der Karte, nicht
in einem Fenster: Man liest die Rueckmeldungen im Zusammenhang mit der
Aufgabe, sonst fehlt beim Antworten die Haelfte.

Geprueft:
- Chef und Luna schreiben sich gegenseitig, beide sehen beides
- Mika (fremder Creator) bekommt 404 beim Lesen UND beim Schreiben
- ohne Anmeldung 401, fremde Herkunft 403, leerer Text abgewiesen
- Luna kann Chefs Rueckmeldung nicht loeschen, ihre eigene schon
- Chef kann alle loeschen
- Zaehler auf der Karte stimmt, Handy ohne Ueberlauf, 0 Konsolenfehler
2026-08-31 12:37:50 +02:00
DogFatherGit fc0fd0c748 Wissen: "Neu" laeuft nach 48 Stunden von selbst ab
Filipe: neue Anleitungen sollen nach 48 Stunden aus dem oberen Block
verschwinden und nur noch in ihrer Kategorie zu finden sein. Vorher
standen sie dort 30 Tage.

ZUERST EIN PROBLEM IM BESTAND: Es gab gar keinen Zeitpunkt, nur ein
Datum (veroeffentlicht). Eine abends um 23 Uhr eingestellte Anleitung
waere damit nur 25 statt 48 Stunden neu gewesen. Neue Spalte
"hochgeladen" mit dem genauen Zeitpunkt -- eine Spalte anzuhaengen laesst
SQLite anstandslos zu, anders als eine geaenderte CHECK-Regel. Alte
Eintraege ohne Zeitstempel fallen auf Mitternacht des Datums zurueck,
geprueft.

ZWEI DINGE BEWUSST GETRENNT:

NEU laeuft nach 48 Stunden ab, gemessen ab dem Hochladen. Nicht ab
"veroeffentlicht" -- das ist nur ein Datum und darf frei gesetzt werden.

WICHTIG bleibt stehen, bis es jemand abschaltet. Es ist eine
Entscheidung von Hand ("Erscheint zusaetzlich ganz oben unter Neu &
wichtig"), keine Eigenschaft, die von selbst verfaellt -- sonst waere der
Schalter sinnlos. Wer eine angepinnte Anleitung loswerden will, nimmt
sie ueber "Bearbeiten" wieder heraus.

Damit man sieht, WARUM etwas oben steht, gibt es jetzt zwei Marken statt
einer: "Neu" (gruen, laeuft ab) und "Wichtig" (gold, bleibt). Sonst
wundert man sich, wenn eine verschwindet und die andere nicht. Unter der
Ueberschrift steht dieselbe Regel in einem Satz.

Geprueft mit kuenstlich gealterten Eintraegen:
   0 h -> im Block, Marke "Neu"
  47 h -> im Block, Marke "Neu"
  49 h -> raus, aber weiterhin in der Kategorie
 200 h + wichtig -> im Block, nur Marke "Wichtig"
 300 h -> raus, in der Kategorie auffindbar
  ohne Zeitstempel -> faellt auf das Datum zurueck
2026-08-31 12:26:50 +02:00
DogFatherGit 7c81b17f9c Workspace: alle Auswahlfelder ohne Systemmenue
Filipe: "die kisten wo die namen drin stehen um auszuwaehlen -- kannst
du doch ueberall besser aussehen lassen". Betraf nicht eine Stelle,
sondern 28 Auswahlfelder auf zehn Seiten.

Deshalb EIN Baustein (assets/js/wahl.js) statt zehn Einzelloesungen.

WIE ER ARBEITET:
Das echte <select> bleibt im Dokument und behaelt seinen Wert -- es wird
nur unsichtbar. Darueber liegt ein eigener Knopf mit eigener Liste.
Damit funktioniert alles weiter, was schon da war: jedes `feld.value`,
jedes `change`-Ereignis, jedes Formular. Ich musste keine einzige der
zehn Seiten in ihrer Logik anfassen.

Faellt das Skript aus, ist wieder das gewohnte Systemmenue da.

DREI ENTSCHEIDUNGEN, DIE WICHTIG WAREN:

1. Nicht display:none fuer das echte Feld, sondern ein Pixel und
   durchsichtig. Ein per display:none verstecktes Feld faellt aus der
   Formularpruefung des Browsers heraus -- `required` wuerde stumm nicht
   mehr greifen.

2. Die Liste haengt an position:fixed, nicht am Elternelement. In einer
   Karte mit overflow:hidden waere sie sonst abgeschnitten. Passt sie
   nach unten nicht mehr hin, klappt sie nach oben.

3. Ein MutationObserver erfasst Felder, die erst spaeter entstehen, und
   Eintraege, die nachgeladen werden. Neu gezeichnet wird im naechsten
   Bild -- so wird ein direkt nach dem Fuellen gesetztes `feld.value`
   noch mitgenommen. Geprueft an der Scout-Pipeline: Prioritaet und
   Stufe entstehen erst beim Aufklappen, beide werden aufgewertet, der
   gewaehlte Wert kommt beim Speichern richtig an.

Tastatur wie gewohnt: Pfeile oeffnen und blaettern, Buchstaben springen
zum passenden Eintrag, Esc schliesst, Klick daneben auch. Gruppen
(optgroup) werden als Zwischenueberschrift dargestellt.

Geprueft ueber zehn Seiten: 28 Felder, KEINES mehr als Systemmenue,
Werte kommen an, Seite reagiert auf die Auswahl, Handy ohne Ueberlauf,
0 Konsolenfehler.
2026-08-31 12:23:49 +02:00
DogFatherGit e14d9d5042 Wissen: Kategoriewahl und Filter ohne Systemmenue
Filipe: "wieso sieht das so scheisse aus?" -- zum aufgeklappten
Auswahlmenue der Kategorie. Er hat recht, und die Ursache ist dieselbe
wie bei der Rollenwahl: Ein AUFGEKLAPPTES <select> laesst sich nicht
gestalten. Es kommt vom Betriebssystem, in Windows-Weiss, mitten in
einer dunklen Oberflaeche. Bei zwanzig Eintraegen kam dazu, dass man in
so einer Liste nichts findet.

KATEGORIEWAHL: jetzt dieselben sechs Welten, dieselben Farben und
dieselben Symbole wie die Kacheln auf der Seite. Man waehlt aus
demselben Regal, in dem man sonst stoebert. Unter der Wahl steht die
Beschreibung der gewaehlten Kategorie -- die Zeile, die beim
Einsortieren die eigentliche Frage beantwortet.

Nichts ist vorausgewaehlt (ausser man laedt aus einer Kategorie heraus
hoch). Die stille Vorbelegung war der Grund, warum eine PDF ueber
TikTok LIVE Studio unter "TikTok - Grundlagen" landete.

FILTER Stufe und Geraet: ebenfalls Schalter statt Menue. Zwei bis vier
Werte, die sich nie aendern -- dafuer ein Menue zu oeffnen ist ein Klick
zu viel.

Der Tag-Filter bleibt bewusst ein Auswahlfeld: Tags wachsen mit dem
Bestand, eine Schalterreihe waere irgendwann zwei Zeilen lang.

Geprueft: 20 Kategorien in 6 Welten, nichts vorausgewaehlt, Filter
setzen die Adresse (?stufe=einsteiger), Handy ohne Ueberlauf, 0
Konsolenfehler. Testeintrag landete unter der gewaehlten Kategorie.
2026-08-31 12:14:18 +02:00
DogFatherGit 389d8c1213 Workspace: neue Rolle Manager, feste Rollenreihenfolge, echte Rollenwahl
DREI TEILE.

1) ROLLE "MANAGER"

Ein Manager darf alles, was DogFather darf -- mit genau zwei
Vorbehalten: Er kann keine Leitung ANLEGEN und an keiner Leitung etwas
AENDERN. Sonst koennte er sich einen zweiten Vollzugang schaffen oder
DogFather aussperren. "Nur DogFather hat alle endgueltigen Rechte"
heisst genau das.

Umgesetzt ueber istLeitung() an EINER Stelle statt 44 einzelner
Vergleiche auf "admin" im Server und 26 im Browser.

DATENBANK-UMSTELLUNG: CREATE TABLE IF NOT EXISTS fasst eine vorhandene
Tabelle nicht an -- die CHECK-Regel stand also weiter auf den alten drei
Rollen, und ein Manager waere daran gescheitert, obwohl der Code stimmt.
SQLite kann eine CHECK-Regel nicht aendern, also: neue Tabelle, Daten
hinueber, alte weg, umbenennen. Davor schreibt der Server eine
vollstaendige Sicherung (VACUUM INTO, in sich konsistent). Ohne
Sicherung wird NICHT umgestellt.

Geprueft nach der Umstellung: alle 13 Tabellen mit gleicher Zeilenzahl,
PRAGMA integrity_check ok, keine verwaisten Verweise. Die einzige
Abweichung war eine Sitzung mehr -- die eigene Anmeldung, die die
Umstellung ausgeloest hat.

2) EIN SICHERHEITSLOCH, DAS DER TEST GEFUNDEN HAT

Der erste Entwurf sicherte "Person anlegen" und "Person sperren" ab --
und liess "neuer Zugangscode" offen. Ein Manager konnte DogFather einen
neuen Code ausstellen, bekam ihn angezeigt und haette ihn damit aus
seinem eigenen Konto ausgesperrt. Im Test aufgefallen, weil ich den
negativen Fall durchgespielt habe.

Behoben nicht durch eine dritte Einzelpruefung, sondern durch eine
Schranke an JEDEM Weg mit einer :id. Der naechste Weg, der dazukommt,
ist damit automatisch mitgeschuetzt.

Nachgeprueft: Manager bekommt 403 beim Code-Erneuern und Sperren von
DogFather UND von sich selbst, darf aber Creator und Scouts verwalten.

3) FOLGEFEHLER DER MASSENERSETZUNG

Die Regel "niemals den letzten aktiven DogFather sperren" hatte durch
die Umstellung auf istLeitung() ploetzlich auch Manager blockiert --
gezaehlt werden aber nur DogFather-Zugaenge. Jetzt istDogFather().
Geprueft: DogFather kann einen Manager sperren, sich selbst nicht.

4) REIHENFOLGE UND ROLLENWAHL

Ueberall DogFather, Manager, Scout, Creator. "ORDER BY rolle" waere
alphabetisch gewesen (admin, creator, manager, scout) -- also fast genau
falsch herum. Jetzt ein gemeinsamer Sortierausdruck aus workspace.js.

Das Auswahlmenue fuer die Rolle ist weg. Es kam als weisses
Windows-Menue mitten in einer dunklen Oberflaeche und schnitt "Creator"
zu "Crea" ab -- gestalten laesst sich ein aufgeklapptes Systemmenue
nicht. Ersetzt durch vier sichtbare Schalter mit Symbol, Farbe je Rolle
und einer Zeile, was die Rolle bedeutet. Bei "Manager" gegen
"DogFather" ist das der Unterschied zwischen Raten und Wissen.

DogFather und Manager stehen dort nur zur Wahl, wenn DogFather selbst
davorsitzt -- ein Knopf, der immer scheitert, gehoert nicht hin.

Nebenbei: Das Namensfeld war auf eine von zwoelf Spalten gequetscht, weil
seine Umgebung keine .feld-Klasse trug. Alle Formulare daraufhin
durchsucht, keine weiteren Faelle.
2026-08-31 11:32:22 +02:00
DogFatherGit 79f0186494 Wissen: Kategorie muss ausdruecklich gewaehlt werden
Filipes PDF ueber TikTok LIVE Studio landete unter "TikTok - Grundlagen
& Funktionen" statt unter "Streaming mit PC". Ursache ist ein Denkfehler
von mir, kein Bedienfehler:

Das Kategoriefeld war mit dem ERSTEN Eintrag der Liste vorbelegt. Wer es
nicht bewusst aendert, merkt nichts -- die Pflichtangabe hat still fuer
ihn entschieden. Eine Vorbelegung, die niemand gewaehlt hat, ist keine
Voreinstellung, sondern ein Fehler mit Ansage.

Jetzt steht dort "- Kategorie waehlen -", und ohne Auswahl geht das
Formular nicht ab (Browser blockt, zusaetzlich eigene Pruefung).
Vorbelegt wird nur noch, wenn man AUS einer Kategorie heraus hochlaedt
-- dann ist es eine informierte Annahme statt einer geratenen.

Ausserdem: zwanzig Eintraege in einer flachen Liste findet man nicht.
Das Feld ist jetzt nach denselben sechs Welten gruppiert wie die
Kacheln auf der Seite (optgroup). Geprueft: alle 20 waehlbar,
"Streaming mit PC" ist dabei.

Nach dem Veroeffentlichen steht in einer gruenen Zeile, WO die Anleitung
gelandet ist -- sonst merkt man einen Irrtum erst, wenn man sie
irgendwann sucht. Die Meldung nutzt dieselbe Zeile wie Fehler, aber
nicht dieselbe Farbe: Eine Erfolgsmeldung in Warnrot liest sich wie ein
Problem.

Bereits hochgeladene PDFs lassen sich ueber "Bearbeiten" umhaengen --
die Datei bleibt dabei unberuehrt.
2026-08-28 13:40:14 +02:00
DogFatherGit 4d7d9b183b Workspace: KI-Vorschlaege in Calls, Start-Check und Report
Nach der Korrektur von CPUQuota (50% -> 200%) ist die KI benutzbar:
nr_throttled steht bei 0 (vorher 1752 von 1801 Zeitfenstern).

Gemessen NACH der Korrektur:
  To-dos aus einem Protokoll : 12,7 s (vorher: Abbruch nach 45 s)
  Website waehrend der Arbeit: 0,063 s -- Basis war 0,068 s

Kein Einfluss auf die Website, wie bei der ersten Messung mit zwei
echten Kernen vorhergesagt.

DREI KNOEPFE, alle nach demselben Muster:

- Calls: "To-dos vorschlagen" liest, was im Protokoll schon steht, und
  fuellt LEERE To-do-Zeilen. Bereits Getipptes wird nie ueberschrieben.
- Start-Check: "Besser formulieren" macht aus einer Notiz einen
  Aufgabentitel. Der einfache Vorschlag (erster Satz der Notiz) bleibt
  daneben bestehen -- er funktioniert auch ohne KI.
- Report: "In Worte fassen" fasst die Zahlen in drei Saetzen zusammen.

IST DIE KI AUS, GIBT ES DEN KNOPF NICHT. Jede Seite fragt beim Laden
einmal nach. Ein Knopf, der immer eine Fehlermeldung bringt, ist
schlimmer als gar kein Knopf.

Die Absicherung steht in EINER Datei (assets/js/ki.js), nicht dreimal.
Der Knopf sagt waehrend der Arbeit "Die KI denkt ..." und der Funke
pulst -- zwoelf Sekunden ohne Rueckmeldung fuehlen sich sonst an wie
ein Fehler.

Optisch bewusst ANDERS als die normalen Knoepfe: gestrichelter Rand
statt geschliffener Kante. Ein KI-Knopf tut nichts Endgueltiges, er
schlaegt vor -- das soll man auf den ersten Blick sehen.

ZWEI VERBESSERUNGEN AUS DEM TEST:

1. Der Bereichsname wurde dem Modell mitgegeben und klebte prompt in
   der Antwort: "LIVE-Struktur auf 18 und 21 Uhr festlegen". Ohne ihn
   kommt eine echte Handlung heraus. Der Befund allein reicht -- er
   steht ja ohnehin im richtigen Bereich. Variable entfernt, kein toter
   Code.

2. Der Report gab dem Modell "ueberfaellig" ohne Umlaut vor und bekam
   es genauso zurueck. Jetzt richtig geschrieben.

Geprueft: Calls 7 s und drei brauchbare To-dos, Start-Check erzeugt
einen Titel, Report drei Saetze, 0 Konsolenfehler.
2026-08-28 13:34:57 +02:00
DogFatherGit 401c8f006c Workspace: Formulare richtig ausgerichtet, PDF-Ablage und Schalter
URSACHE ZUERST: .feld--breit stand in DREI HTML-Dateien (wissen,
scouting, dateien) -- und war NIE im CSS definiert. Das Raster
(auto-fit, minmax(170px)) gab deshalb jedem Feld dieselbe Breite, egal
was drin steht: Der Titel war so schmal wie eine Auswahl, lange
Kategorienamen wurden abgeschnitten ("1. TikTok - Grundla"), und die
Tag-Vorschlaege stapelten sich zu einer halben Seite.

Jetzt zwoelf Spalten statt auto-fit. Damit laesst sich sagen, WIE breit
ein Feld sein soll: Titel, Beschreibung und Kategorie ueber die ganze
Zeile, Stufe/Geraet/Datum zu dritt nebeneinander. Unter 860 px zwei
Spalten, unter 560 px eine. Die Klasse ist zentral definiert, also
wirken die drei betroffenen Seiten sofort mit.

Gemessen danach: Titel/Beschreibung/Kategorie je 1058 px, die drei
kleinen je 342 px, Kategorie nicht mehr abgeschnitten.

PDF-ABLAGE statt nacktem Dateifeld. Der Browser-Knopf war abgeschnitten
("K...") und sah aus wie ein Fremdkoerper. Jetzt eine eigene Flaeche:
Klicken, Ziehen oder Tastatur. Sie markiert sich gruen, sobald eine
Datei drin ist, prueft schon im Browser auf PDF und schlaegt den
Dateinamen als Titel vor, wenn der noch leer ist.

Sie steht bewusst GANZ OBEN: Man laedt etwas hoch und beschreibt DANN,
was es ist -- nicht umgekehrt.

SCHALTER statt Kaestchen fuer "besonders wichtig". Ein Kaestchen von 17
Pixeln ist auf dem Handy kaum zu treffen und sagt nicht, was passiert.
Der Schalter ist gross, zeigt seinen Zustand in Gold (dieselbe Farbe wie
die Marke "Wichtig" spaeter) und erklaert sich in einer Zeile:
"Erscheint zusaetzlich ganz oben unter Neu & wichtig".

Tag-Vorschlaege kompakter: von zehn Zeilen auf zwei.

Geprueft: Handy ohne Ueberlauf, 0 Konsolenfehler, Dateiwahl markiert die
Flaeche und fuellt den Titel, Schalter schaltet.
2026-08-28 13:20:49 +02:00
DogFatherGit 1f217041ae Workspace: hellere Kategoriekacheln + Dateien gezielt an mehrere Personen
ZWEI WUENSCHE VON FILIPE.

1) KACHELN BESSER SICHTBAR

Erster Versuch war zu zaghaft: die Flaeche ging nur von RGB(17,24,37)
auf (27,36,51), der sichtbare Gewinn kam fast nur vom staerkeren Rand.
Nachgemessen an echten Bildpunkten -- Seite liegt bei RGB(5,7,13).
Jetzt RGB(38,49,67), dazu kraeftigerer Rand und hellerer Symbolring.

Der Beschreibungstext stand auf --text-still und lag damit bei 3,6:1 auf
der neuen Flaeche -- zu blass fuer laufenden Text. Jetzt --text-leise,
gemessen 6,2:1.

Leere Kategorien waren mit opacity .55 fast unlesbar. Jetzt .82 -- sie
sollen erkennbar bleiben, nur zurueckhaltender.

2) DATEIEN AN MEHRERE PERSONEN GEZIELT FREIGEBEN

Bisher hatte eine Datei genau EINEN Bereich (creator_id). Damit liess
sie sich nicht zweien geben, ohne sie zweimal hochzuladen.

Neue Tabelle datei_personen (datei_id + person_id). creator_id bleibt
und behaelt seine Bedeutung: Es sagt, zu wessen BEREICH eine Datei
gehoert -- die neue Tabelle sagt, WER sie sehen darf. Zwei verschiedene
Fragen, deshalb zwei Felder.

Auswahl als einzelne Schalter, nicht als <select multiple>: Dort
verliert man mit einem Fehlklick die ganze Auswahl, und auf dem Handy
ist sie kaum bedienbar. Jeder Name ist ein Schalter, der sichtbar an
oder aus ist, eingefaerbt nach Rolle -- man sieht auf einen Blick, ob
eine Datei an Creator, Scouts oder beide geht.

Wer wen auswaehlen darf:
- DogFather jeden aktiven Menschen ausser sich selbst
- ein Scout NUR die Creator, die er betreut -- sonst koennte er sich
  ueber eine Freigabe Zugang zu fremden Bereichen verschaffen
- ein Creator gar niemanden

Jede Id wird beim Speichern erneut gegen die erlaubte Auswahl geprueft.
Geprueft: Sam schickt die Ids 3, 6 und 7 mit (alles Creator, die er
NICHT betreut) -- die Datei landet bei niemandem. Kein Fehler, kein
Zugang: die Ids fallen still durch das Raster.

Weiter geprueft:
- Chef sieht alle drei Testdateien, Luna nur ihre, Sam nur seine,
  Patrick keine
- Creator bekommt 403 beim Aendern von Freigaben
- Freigaben sind an jeder Datei sichtbar ("Sichtbar fuer ..."), auch
  fuer die, die sie nicht aendern duerfen -- niemand soll raten muessen
- Die Auswahl wird nach dem Hochladen geleert, sonst bekaeme die
  naechste Datei stillschweigend dasselbe Publikum
2026-08-28 13:14:11 +02:00
DogFatherGitandClaude Opus 5 c0ac041122 Workspace: Versionsstempel gegen Cloudflares 4-Stunden-Cache
Filipe sah die neue Kachel nicht, obwohl sie live war. Ursache gemessen,
nicht vermutet:

  Node liefert   : Cache-Control: no-cache        (richtig)
  Cloudflare macht: Cache-Control: max-age=14400  (ueberschreibt es)
  cf-cache-status: REVALIDATED, Server: cloudflare

Cloudflares voreingestellte Browser-Cache-Zeit von vier Stunden setzt
sich ueber das no-cache des Servers hinweg -- aber NUR bei JS und CSS.
HTML kommt mit DYNAMIC und no-cache immer frisch durch (geprueft an
/creator.html und /workspace/).

Genau deshalb genuegt ein Stempel in der HTML: neue Seite -> neue
Adresse -> Cloudflare kennt sie nicht -> frische Datei. Keine
Cloudflare-Einstellung noetig, nichts, worauf ich warten muss.

tools/workspace-stempel.mjs setzt den Stempel auf alle 71 Verweise in
13 Dateien. Der Lauf ist wiederholbar: ein vorhandener Stempel wird
ersetzt, nicht angehaengt (geprueft).

Gehoert ab jetzt vor jeden Deploy, der JS oder CSS im Workspace anfasst.
Ich verlasse mich damit nicht mehr aufs Erinnern -- das war der
eigentliche Fehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-28 13:03:33 +02:00
DogFatherGit 9b2e09be58 Workspace: Wissens-Bibliothek -- 20 Kategorien, Suche, Filter, Pflege
Wichtigster Befund vorab: Es gab noch KEINEN PDF-Bereich. Weder auf der
Website (28 Seiten, keine davon Anleitungen) noch auf dem Server (null
PDFs). Es wurde also nichts umstrukturiert, sondern neu gebaut -- und
damit war die erste Frage nicht "wie sieht es aus", sondern "wie kommen
die PDFs spaeter rein". Eine feste Liste im Code waere nach drei Monaten
unbrauchbar gewesen.

Filipe hat entschieden: eigener Pflegebereich, und alles nur fuer das
Team. Die Bibliothek liegt deshalb komplett im Workspace hinter dem
Login, nicht auf der oeffentlichen Seite.

GLIEDERUNG
Zwanzig Kategorien woertlich nach Vorgabe, gebuendelt in sechs Welten:
TikTok, Streaming einrichten, Bild & Ton, Uebertragung & Aussehen,
Inhalt & Menschen, Hilfe & Schnellstart. Zwanzig gleich aussehende
Kacheln untereinander findet niemand -- gebuendelt sucht man erst die
Welt, und darin stehen nur noch drei bis vier. Jede Welt hat ihre eigene
Farbe, jede Kategorie ihr eigenes Symbol (20 verschiedene, keins
doppelt). Die Kategorietexte sind Filipes Beschreibungen -- sie
beantworten beim Einsortieren die einzige Frage, die man wirklich hat.

Die Gliederung steht im Code, nicht in der Datenbank: Sie ist eine
bewusste Ordnung, keine Nutzdaten.

JEDE PDF HAT GENAU EINE HAUPTKATEGORIE, alles Weitere laeuft ueber Tags
-- so gibt es jede Anleitung nur einmal, sie ist aber ueber mehrere
Begriffe auffindbar. Genau wie gefordert.

SUCHE UND FILTER
Suche ueber Titel, Beschreibung, Tags und Dateiname. Filter nach Stufe
(Einsteiger / Fortgeschritten / Profi), Geraet und Tag. Zwei Details:
- "PC" zeigt auch die Anleitungen, die fuer BEIDE Geraete gelten --
  sonst filtert man sich versehentlich die Haelfte weg.
- Der Tag-Filter sucht mit Kommas drumherum, sonst wuerde "pc" auch bei
  "pc-spiele" anschlagen.
Sobald gesucht oder gefiltert wird, verschwinden die Kacheln und es
erscheinen Treffer. Wer sucht, will nicht erst noch klicken.

Der Zustand steht in der Adresse (?k=obs&tag=...). Damit funktionieren
Zurueck-Taste, Neuladen und Weiterschicken. Eine Kategorie, die man
niemandem verlinken kann, ist keine Seite. Geprueft.

SICHERHEIT
Hochgeladen wird nur, was WIRKLICH eine PDF ist -- geprueft an den
ersten Bytes (%PDF-), nicht an der Dateiendung. Die kann jeder
umbenennen. Geprueft mit einer als .pdf getarnten HTML-Datei mit Skript:
abgewiesen. Nur weil diese Pruefung existiert, darf die Datei ueberhaupt
im Browser angezeigt werden statt bloss heruntergeladen -- und selbst
dann mit strenger CSP und nosniff.

Lesen darf jeder Angemeldete, pflegen nur DogFather. Geprueft: Creator
und Scout bekommen 403 beim Hochladen und sehen keine Bearbeiten-Knoepfe.
Ohne Anmeldung ist auch die Datei selbst nicht erreichbar (401).

Bricht das Anlegen nach dem Schreiben ab, wird die Datei wieder
geloescht -- sonst laege sie fuer immer verwaist auf der Platte.

Beim Bearbeiten wird die Datei bewusst NICHT ersetzt. Wer eine neue
Fassung hat, stellt sie neu ein. So bleibt nachvollziehbar, was wann galt.

Geprueft: 20 Kacheln, 20 verschiedene Symbole, Kategorie oeffnen, Suche,
Tag-Klick, Browser-Zurueck, Handy ohne Ueberlauf, 0 Konsolenfehler.
2026-08-28 12:40:49 +02:00
DogFatherGitandClaude Opus 5 18a8d46a04 Workspace: Betreuungs-Auswahl heisst "DogFather" statt "nur DogFather"
Das "nur" war aus der Bauzeit uebrig, als der Eintrag noch beschrieb,
wer den Creator sieht ("nur das Management"). Als Auswahl neben "Sam"
und "Patrick" gehoert an die Stelle schlicht der Name -- die Liste
beantwortet die Frage "wer ist zustaendig", nicht "wer sieht mit".

Geprueft: Zuordnung setzen, entfernen und wieder setzen funktioniert
unveraendert.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-28 12:14:25 +02:00
DogFatherGit 677bcb5653 Workspace: neues Knopf-System "Geschliffen" -- ueberall, eine Stelle
Von Filipe aus drei gerenderten Entwuerfen gewaehlt (A Kante & Glut,
B Geschliffen, C Praegung). Raten hilft hier nicht -- beim Zurueck-Knopf
hat erst der direkte Vergleich zum Ziel gefuehrt.

Aufbau: farbiger Verlaufsrand (padding-box/border-box) und ein feiner
heller Streifen obenauf -- wie eine angeschliffene Kante. Die Farbe sitzt
im Rand und in der Schrift, die Flaeche bleibt dunkel. Beim Zeigen hebt
sich der Knopf einen Pixel an und bekommt einen weichen farbigen Schein
darunter. Die Hauptaktion einer Seite ist zusaetzlich gefuellt.

JEDE AKTION HAT IHRE EIGENE FARBE:
  gruen  passt, erledigt, freigegeben
  amber  ausbaufaehig, wartet
  rot    Handlungsbedarf, loeschen
  blau   speichern
  violett anlegen
  gold   Onboarding -- der einzige Schritt, der eine Person anlegt
  grau   abbrechen, schliessen
  Stufenfarbe fuer den Weiter-Knopf der Scout-Pipeline

DAS IST JETZT DIE EINZIGE STELLE, AN DER KNOEPFE GESTALTET WERDEN.
Vorher standen sie in NEUN Dateien verstreut, und prompt sahen dieselben
Knoepfe auf verschiedenen Seiten verschieden aus. Alle alten
Definitionen sind entfernt; wer einen neuen Knopf braucht, nimmt eine
Farbklasse und schreibt kein CSS. Gilt ab jetzt auch fuer alles, was
noch dazukommt.

Geprueft ueber alle elf Seiten: 172 sichtbare Knoepfe, kein einziger
ungestylt, kein Kontrast unter 4,5:1.

Drei Sachen, die dabei aufgefallen sind:

1. Der neutrale "Abbrechen"-Knopf hatte halbdurchsichtiges Weiss als
   Farbwert -- gemessen 1,1:1 Kontrast, praktisch unlesbar. Neutral hat
   jetzt einen echten Farbwert (#8e9cb0).

2. Die Filter-Knoepfe (Kalender, Dateien, Bereiche) hatten eine eigene
   "gedrueckt"-Regel mit flaechiger Farbe, die den geschliffenen Rand
   ueberschrieb. Sie benutzen jetzt den gefuellten Auftritt des Systems.

3. Der Bearbeiten-Stift auf den Aufgabenkarten war nie ein richtiger
   Knopf, sondern nackter Text ohne Rand -- man sah nicht, dass man
   draufdruecken kann. Jetzt im System, in der kleinsten Groesse.

Der Zurueck-Knopf ("Neon-Kante") bleibt wie er ist -- den hat Filipe
selbst ausgesucht, und er ist ein Navigationselement, keine Aktion.

Messhinweis fuer spaeter: color-mix liefert computed color(srgb ...),
das laesst sich NICHT als Text auslesen. Die erste Messung meldete
faelschlich 1,13:1 fuer 49 Knoepfe. Richtig geht es ueber eine Leinwand
(fillStyle + getImageData).

Alle Toene bewusst gedeckt statt neon, alles respektiert
prefers-reduced-motion.
2026-08-28 12:13:11 +02:00
DogFatherGit 1c3e642f12 Workspace: Rolle "Management" heisst jetzt "DogFather"
Auf Wunsch von Filipe. Betrifft ausschliesslich die Anzeige.

Der Rollenschluessel bleibt "admin". Er steckt in der CHECK-Regel der
Datenbank, in jeder bestehenden Sitzung und in jeder Rechteabfrage --
ihn umzubenennen haette alle drei gebrochen, und bestehende Anmeldungen
waeren ungueltig geworden. Umbenannt wird nur, was man LIEST.

Der Anzeigename steht jetzt an EINER Stelle (ROLLEN_NAME in
workspace.js) und kommt ueber /workspace/api/ich als rolle_name mit.
Stand er in elf Dateien, waere er beim naechsten Mal in zehn davon
geaendert.

Dabei aufgefallen: In der Kopfleiste stand auf JEDER Seite der rohe
Rollenschluessel -- "Chef · admin". Das war schon vorher unschoen, faellt
aber jetzt erst richtig auf. Alle elf Seiten zeigen jetzt "Chef ·
DogFather". Geprueft: kein rohes "admin" mehr in der Oberflaeche.

Geaendert: Rollenkachel und Untertitel auf der Anmeldeseite, Kopfleiste
aller Seiten, Rollentext auf dem Dashboard, Rollenmarken und Auswahl in
der Personenverwaltung, die Betreuungs-Auswahl ("nur DogFather"), der
Hinweis auf der Dateienseite, die Erklaerung zur internen Notiz im
Profil, die Hinweise fuer Scouts ohne Zuteilung -- und vier
Fehlermeldungen vom Server.

Schreibweise "DogFather" wie von Filipe geschrieben; das ist auch auf
der oeffentlichen Website die haeufigste Form (717 von 1216).

In den Code-Kommentaren der Fachmodule heisst die Rolle weiterhin "das
Management". Das bleibt bewusst so -- eine Massenaenderung an vierzig
Kommentaren waere reines Risiko ohne sichtbaren Nutzen. Ein Hinweis an
der ROLLEN_NAME-Zuordnung erklaert den Zusammenhang.
2026-08-28 11:55:05 +02:00
DogFatherGit 55450aa83f Workspace: Start-Check / Erstanalyse -- Onboarding vollstaendig
Letztes unumgesetztes Stueck aus dem Konzept, Seite 5, Block 03. Profil,
Ziele und 90-Tage-Plan gab es schon -- die Erstanalyse dazwischen fehlte.

"Die Betreuung startet strukturiert: Ausgangslage verstehen, Ziele
festlegen und daraus konkrete Arbeitspakete bauen."

Der letzte Halbsatz ist der entscheidende. Ein Check, der mit einer Note
endet, ist eine Beurteilung. Ein Check, der mit Aufgaben endet, ist
Betreuung. Jeder Punkt mit Handlungsbedarf bietet deshalb direkt an,
daraus eine Aufgabe zu machen -- mit dem Befund als Herkunft und
automatisch hoher Prioritaet. Dieselbe Linie wie beim Review ("endet mit
einer Entscheidung") und beim Call ("endet mit To-dos").

Die vier Felder aus dem Deck, mit je vier Punkten: Profil & Auftritt,
LIVE-Struktur, Content-Muster, Community & Modis. Drei Stufen: Passt,
Ausbaufaehig, Handlungsbedarf.

Die Punkte stehen fest im Code, NICHT in der Datenbank. Eine
Erstanalyse, bei der jeder eigene Punkte anlegt, ist keine Erstanalyse
mehr -- man koennte zwei Creator nicht mehr vergleichen, und genau das
ist ihr Zweck.

Sichtbarkeit, bewusst anders als beim Profil:
- LESEN darf auch der Creator selbst. Der Check ist Teil SEINES
  Entwicklungsplans ("jeder Creator bekommt einen eigenen, lebenden
  Entwicklungsplan"), kein Urteil hinter seinem Ruecken. Wer etwas
  festhalten will, das er nicht sehen soll, hat dafuer die interne
  Notiz im Profil -- die bleibt beim Management.
- AENDERN duerfen nur Management und zustaendiger Scout. Es ist eine
  Fremdeinschaetzung; koennte der Creator sie selbst setzen, waere es
  eine Selbsteinschaetzung. Geprueft: Luna bekommt 403 beim Bewerten und
  beim Anlegen einer Aufgabe, sieht aber ihren Check inklusive Notizen.
- Ein nicht zustaendiger Scout bekommt 404, nicht 403.

Kein Speichern-Knopf: Jede Bewertung und jede Notiz wird sofort
gesichert. Eine Erstanalyse geht man im Gespraech durch -- da will
niemand am Ende noch an einen Knopf denken.

Ein leergeraeumter Punkt (keine Bewertung, keine Notiz) wird geloescht
statt als leere Zeile stehen zu bleiben. So sagt der Bestand direkt, wie
weit die Analyse ist.

Zwei neue Hinweise auf dem Dashboard:
- "Creator ohne Start-Check" -- die Sorte Luecke, die sonst niemandem
  auffaellt, weil ja nichts fehlt: es fing nur nie an.
- "Punkt im Start-Check braucht Handlung", sichtbar fuer alle, die den
  Check auch sehen duerfen.

Aus dem Test: Sechzehn dauerhaft offene Notizfelder machten die Seite
7026 px lang. Das Feld erscheint jetzt erst mit der Bewertung -- vorher
hat man ohnehin nichts zu notieren. 2460 px.
2026-08-28 11:51:14 +02:00
DogFatherGit 3a1ab64c26 Workspace: Suche ueber alle Bereiche (Phase 4)
Auf JEDER Seite, nicht auf einer eigenen. kopf.js baut sie selbst in die
Kopfleiste ein -- eine Suche, die man nur auf einer Extraseite findet,
benutzt niemand. Mit "/" oeffnen, mit Esc schliessen.

Durchsucht: Aufgaben, Termine, Calls, Gespraechsprotokolle, Dateien, die
fuenf Betreuungsbereiche, die Scout-Pipeline und die offenen Felder der
Creator-Profile.

Zwei Dinge machen den Unterschied zwischen einer Suche und einer
brauchbaren Suche:

1. SIE DARF NICHTS FINDEN, WAS DIE SEITE VERBERGEN WUERDE.

Eine Suche ist die verlockendste Stelle fuer ein Datenleck: Man tippt
einen Namen und bekommt Treffer aus Bereichen, die man nie oeffnen
duerfte. Jede Quelle wird deshalb mit der Sichtbarkeitsregel ihres
eigenen Moduls abgefragt -- importiert, nicht abgeschrieben.

Die management-internen Profilfelder (admin_notiz, plan_start,
naechster_review) werden GAR NICHT durchsucht, auch nicht fuer das
Management: Ein Treffer daraus taucht sonst spaeter in einer Ansicht
auf, die diese Felder nicht zeigen darf. Wer die Notiz lesen will,
oeffnet das Profil.

Geprueft mit einem eigenen Leck-Test: "GEHEIM" (Inhalt einer internen
Notiz) findet niemand, auch der Chef nicht. Die Lead-Notiz eines Scouts
findet nur er selbst und das Management, nicht der andere Scout.

2. SIE MUSS SAGEN, WO ETWAS STEHT.

Jeder Treffer traegt einen Ausschnitt RUND UM die Fundstelle, nicht die
ersten Zeichen des Feldes -- man sieht sofort, warum etwas gefunden
wurde. Bei Profiltreffern steht dabei, welches Feld getroffen hat, bei
Protokollen ob es unter "Besprochen" oder "Entscheidung" stand. Und
jeder Treffer fuehrt an die Stelle, an der man weiterarbeiten kann.

LIKE-Sonderzeichen werden maskiert. Ohne das waere die Suche nach "100%"
eine Suche nach "100 gefolgt von irgendwas" und "a_b" faende auch "axb"
-- beides falsch und beides faellt erst spaet auf. Geprueft: "100%" und
"a_b" finden genau ihren Eintrag, "axb" findet nichts.

Bewusst kein Volltextindex (FTS5): Die Datenmengen sind klein, und LIKE
braucht keinen zweiten Datenstand, der irgendwann auseinanderlaeuft.

Kleinigkeiten aus dem Test:
- Das Overlay stand auf voller Hoehe, auch bei zwei Treffern -- der
  Schleier ist flex und stand auf dem voreingestellten stretch. Jetzt
  flex-start, das Fenster waechst mit dem Inhalt (417 statt 780 px bei
  drei Treffern).
- Das eingebaute Kreuz von type="search" sass direkt neben dem
  Esc-Knopf: zwei Wege fuer dasselbe, dicht nebeneinander. Entfernt.
- Das CSS liegt in gate.css, nicht in einer Seiten-Datei -- genau der
  Fehler, der bei .knopf-still schon einmal passiert ist.
2026-08-28 11:39:46 +02:00
DogFatherGit 5684ff87f1 Workspace: "Was ist dran" -- erster Baustein der Automationen (Phase 4)
Bis hierher musste man selbst daran denken, in den richtigen Bereich zu
schauen. Das dreht die Uebersicht auf dem Dashboard jetzt um: Das System
sagt, was liegen bleibt.

Drei Regeln, an die sich das haelt:

1. KEINE eigenen Daten. Ein Hinweis ist immer nur eine Sicht auf etwas,
   das ohnehin existiert. Wird die Aufgabe erledigt, verschwindet der
   Hinweis von selbst -- es gibt nichts zu quittieren, nichts zu pflegen
   und nichts, was veralten kann. Genau daran scheitern die meisten
   Erinnerungssysteme.

2. JEDER HINWEIS FUEHRT IRGENDWOHIN. Ein Hinweis ohne Ziel ist nur ein
   schlechtes Gewissen. Jeder traegt deshalb einen Link zu der Stelle,
   an der sich die Sache tatsaechlich erledigen laesst. Geprueft: Ziel
   erreichbar, keine Umleitung.

3. DIESELBE SICHTBARKEIT WIE UEBERALL. Die Regeln werden aus den
   Fachmodulen importiert, nicht abgeschrieben (workspace-aufgaben,
   -kalender, -dateien, -bereiche exportieren sie jetzt). Ein Hinweis
   darf nie etwas verraten, das die zugehoerige Seite verbergen wuerde
   -- sonst waere ausgerechnet die Uebersicht das Leck.

Dreizehn Hinweisarten, in drei Stufen. Nur die oberste ist farbig:
waere alles hervorgehoben, waere nichts hervorgehoben. Liegt nichts an,
verschwindet der ganze Block.

Steht bewusst VOR den Zahlen. Die Zahlen sagen, WIE VIEL anliegt --
diese Liste sagt, WAS zu tun ist.

Steuerungswissen bleibt beim Management: Review-Termine, Creator ohne
zustaendige Person und leere Profile erscheinen weder bei Creator noch
bei Scouts -- genau wie die zugehoerigen Profilfelder. Geprueft mit
einem eigenen Leck-Test ueber alle drei Rollen.

Zwei Hinweise, die es ohne diese Uebersicht gar nicht gaebe:
- Aufgaben, die im Review haengen. Sie warten auf jemanden -- die Sorte
  Stillstand, die niemandem auffaellt, weil nichts ueberfaellig wird.
- Uebergebene Leads, aus denen nie ein Creator wurde. Sonst ist die
  Uebergabe eine Sackgasse, die niemand bemerkt.

Nebenbei: Der Fusstext auf dem Dashboard stammte noch vom ersten Tag
("Die Bereiche werden nach dem Phasenplan gebaut") -- inzwischen sind
Phase 1 bis 3 fertig. Ersetzt durch eine Erklaerung, wie die Uebersicht
funktioniert.
2026-08-28 11:23:07 +02:00
DogFatherGit fa8fbab410 Workspace: Scouts betreuen Creator wie das Management
Bisher hatten Scouts mit der Creator-Betreuung nichts zu tun -- keine
Profile, keine Betreuungsbereiche, keine Reports. Das aendert sich, mit
zwei bewusst gesetzten Grenzen.

GRENZE 1: nur zugeteilte Creator, keine Rollenregel.

Neue Tabelle betreuung (creator_id PRIMARY KEY -> betreuer_id). Ein
Creator hat genau EINE zustaendige Person, damit nie unklar ist, wer
gefragt ist. Das Management sieht ohnehin alle und braucht keinen
Eintrag. Wer nichts zugeteilt bekommt, sieht weiterhin nichts -- kein
Recht entsteht automatisch aus der Rolle.

Zugeteilt wird in "Personen & Zugaenge", direkt in der Personenzeile:
Betreuung ist eine Eigenschaft der Person, kein eigener Vorgang. Nur das
Management darf zuteilen -- koennte ein Scout sich selbst Creator geben,
haette er die Rechtevergabe in der Hand, die ihn begrenzen soll.
Zustaendig koennen nur aktive Scouts sein, kein Admin (der sieht alles)
und kein anderer Creator.

Bei der Uebergabe aus der Pipeline passiert die Zuteilung von selbst:
Wer jemanden gefunden hat, betreut ihn weiter. Genau darum geht es bei
"Creator-Onboarding starten". Umhaengen kann das Management jederzeit.

GRENZE 2: betreuen, nicht verwalten.

Profile, die fuenf Bereiche und Reports wie ein Manager. ABER:
- keine Zugangscodes, kein Sperren von Personen (personen.html bleibt
  admin-only, unveraendert)
- keine management-internen Felder. Der Scout bekommt admin_notiz,
  plan_start und naechster_review NICHT -- die Felder fehlen in der
  Antwort komplett, nicht nur in der Anzeige. Eine Notiz UEBER die
  Betreuung gehoert nicht in die Hand dessen, der betreut. Geprueft: Ein
  Scout, der admin_notiz mitschickt, aendert sie nicht.

Die Regel steht an EINER Stelle (betreuteIds / betreutWo / darfCreator
in workspace.js) und wird von sechs Modulen benutzt. Eine Rechteregel,
die an sechs Stellen steht, ist eine Rechteregel, die irgendwann an
fuenf Stellen stimmt.

Genau das ist beim Bauen auch passiert: workspace-calls.js hatte eine
wortgleiche Kopie der Kalender-Sichtbarkeit. Erweitert wurde nur der
Kalender -- Scouts sahen die Termine ihrer Creator, dieselben Termine
als Call aber nicht. Die Kopie ist jetzt weg, calls.js importiert die
Regel aus workspace-kalender.js.

Zwei Fehler, die die Aenderung selbst erzeugt haette, vorher gefunden:
- Report-Entscheidung: ein Scout haette eine Aufgabe angelegt, deren
  "Creator" er selbst ist -- die waere in jeder Auswertung falsch
  mitgelaufen. Zeigt jetzt auf einen seiner Creator.
- Bereichseintrag: derselbe Fehler. Ein Scout hat gar keinen eigenen
  Betreuungsbereich. Ein Eintrag ohne oder mit fremder Zuordnung landet
  beim ersten zugeteilten Creator, nie bei einem fremden. Geprueft:
  Mikas Bereich bleibt bei jedem Versuch unberuehrt.
2026-08-28 11:18:56 +02:00
DogFatherGit 45cd0ae226 Workspace: Calls & Meeting-Protokolle -- Phase 3 vollstaendig
/workspace/calls.html.

Calls sind KEINE eigene Terminart neben dem Kalender, sondern dieselben
Termine mit art = 'call' oder 'review'. Ein zweiter Terminspeicher waere
die sichere Art, irgendwann zwei widerspruechliche Uhrzeiten zu haben.
Diese Seite fuegt nur hinzu, was ein Gespraech vom blossen Termin
unterscheidet: das Protokoll danach. Die Sichtbarkeitsregel ist Wort fuer
Wort dieselbe wie im Kalender -- ein Termin darf nicht in der einen
Ansicht auftauchen und in der anderen fehlen.

Der tragende Satz von Seite 13, woertlich genommen:
"Jeder Call endet mit klaren To-dos, die direkt ins Board uebernommen
werden."

To-dos werden deshalb NICHT als Text im Protokoll abgelegt, sondern
sofort zu echten Aufgaben -- mit dem Gespraech als Herkunft in der
Beschreibung, zugeordnet an das Gegenueber des Calls. Sonst steht die
Verabredung in einem Dokument, das niemand mehr oeffnet. Geprueft:
Protokoll geschrieben -> Aufgaben stehen im Brett.

Weitere Punkte:

- Reihenfolge der Gruppen ist Dringlichkeit, nicht Chronologie: Ganz oben
  stehen vergangene Gespraeche OHNE Protokoll. Das ist der einzige
  Zustand, der etwas von einem verlangt. Nur diese Gruppe ist optisch
  hervorgehoben; wenn alles schreit, sieht man nichts mehr.
- Ein festgehaltenes Protokoll ist danach nur noch lesbar. Ein Gespraech
  nachtraeglich umschreiben zu koennen waere genau das, was ein Protokoll
  wertlos macht.
- "Naechster Termin" legt den Folgetermin direkt in den Kalender -- mit
  demselben Gegenueber und demselben Meeting-Link.
- Alles in EINER Transaktion. Geprueft mit einem absichtlich kaputten
  To-do: kein Protokoll, keine halben Aufgaben, kein Folgetermin.
- Enter im To-do-Feld legt die naechste Zeile an, damit man eine Liste
  tippen kann, ohne zur Maus zu greifen.
- Meeting-Link nur bei anstehenden Gespraechen und nur, wenn es wirklich
  eine http(s)-Adresse ist. "bei mir zu Hause" bleibt schlichter Text.

Zur Videotechnik selbst bewusst KEINE Entscheidung getroffen: Ein eigener
WebRTC-Raum braucht einen TURN-Server, damit Verbindungen hinter
Mobilfunk-NAT zustande kommen. Das ist eine Infrastrukturfrage mit
laufenden Kosten und gehoert besprochen, nicht nebenbei entschieden.
Bis dahin traegt der Termin einfach den Link des Dienstes, der ohnehin
benutzt wird.

Nebenbei: .knopf-still lag in scouting.css und fehlte dadurch auf
calls.html -- dort standen prompt nackte Systemknoepfe. Der Baustein
gehoert ins gemeinsame start.css, dort liegt er jetzt.
2026-08-28 11:06:37 +02:00
DogFatherGit d845d91d70 Workspace: Scout-CRM -- Pipeline, Follow-ups und Uebergabe
/workspace/scouting.html. Erster Baustein aus Phase 3 und der Bereich,
in dem Scouts bisher praktisch nichts hatten.

Der Merksatz von Seite 15 ist hier die Sicherheitsregel, nicht nur eine
Beschreibung: "Scouts sehen ihre Pipeline -- die Admin-Rolle die
Gesamtuebersicht -- Creator keine Scout-internen Daten."

- Creator bekommen 404, nicht 403, und zwar auf Schnittstelle UND Seite.
  Sie sollen nicht einmal erfahren, dass es den Bereich gibt.
- Ein Scout sieht ausschliesslich die eigene Pipeline. Geprueft: Patrick
  bekommt auf Sams Lead 404 beim Lesen, Aendern und Loeschen.
- Das Management sieht alles, kann nach Scout filtern und Leads einem
  Scout zuordnen.

Pipeline als gruppierte Liste, nicht als Board: Sechs Spalten waeren auf
dem Handy unbedienbar, und Scouts arbeiten unterwegs. Jede Stufe traegt
eine eigene Farbe an der linken Kante, die von kuehl (neu entdeckt) nach
gruen (uebergeben) laeuft -- die Richtung der Pipeline wird sichtbar,
ohne Ampel-Geblinke.

Jede Karte hat genau EINEN naheliegenden Schritt ("Weiter zu ..."), der
Rest steckt im aufklappbaren Teil. Der Weiter-Knopf traegt bewusst nur
die Farbe seiner Stufe statt des vollen Farbverlaufs, sonst waere die
Seite ein Streifenmuster aus identischen Leuchtbalken.

Faellige Follow-ups stehen als eigene Leiste ganz oben. Das ist der
Teil, der ohne System am ehesten untergeht -- nicht der Kontakt selbst,
sondern das Nachfassen.

Uebergabe ("Creator-Onboarding starten", Seite 15): Aus einem
uebergebenen Lead legt das Management mit einem Klick eine echte Person
mit Zugangscode an. Der Code wird genau einmal angezeigt, wie in der
Personenverwaltung. Die Qualifizierung des Scouts (Plattform, Handle,
Potenzial, LIVE-Aktivitaet) wandert dabei automatisch ins Creator-Profil
-- sonst muesste das Management abtippen, was laengst dasteht. Geprueft:
Lead -> Person -> Profil, alle drei verbunden, zweiter Versuch wird
abgelehnt.

Ein uebergebener Lead ist Teil der Betreuungsgeschichte -- den loescht
nur das Management, nicht der Scout (403).

Nebenbei: .knopf-still gab es im Baukasten noch gar nicht, die Zweit-
knoepfe waren nackte Systemknoepfe. Jetzt sauber definiert.
2026-08-28 10:57:42 +02:00
DogFatherGit 072ded20a2 Workspace: Reports & Review -- Phase 2 vollstaendig
/workspace/report.html. Fuehrt bewusst KEINE eigenen Eintraege, sondern
fasst zusammen, was in Aufgaben, Bereichen, Terminen und Dateien schon
steht: "Fortschritt wird nicht gefuehlt, sondern nachvollziehbar
gemacht".

Die vier Abschnitte sind woertlich die Fragen aus dem Deck, Seite 16:
Was wurde erledigt? Was blockiert? Was hat funktioniert? Was kommt als
Naechstes?

Zwei Punkte daraus sind ernst genommen:

1. "Vorher / nachher" (Historie). Jede Zahl wird mit demselben,
   unmittelbar davorliegenden Zeitraum verglichen. Eine Zahl allein sagt
   wenig -- 3 erledigte Aufgaben sind gut oder schlecht, je nachdem ob es
   vorher 1 oder 9 waren. Bei Zahlen, wo mehr SCHLECHTER ist (offene
   Probleme), ist die Trendfarbe umgedreht.

2. "Jeder Review endet mit einer Entscheidung, nicht nur mit einer
   Zusammenfassung." Der Report legt deshalb direkt eine Aufgabe an --
   ohne Seitenwechsel, mit hoher Prioritaet und optionaler Frist.
   Geprueft: Eintrag im Report -> Aufgabe erscheint im Brett.

"Was blockiert?" zeigt zusaetzlich die drei am laengsten offenen
Aufgaben mit Namen, nicht nur eine Zahl.

Sichtbarkeit:
- Scout: 404, sowohl Schnittstelle als auch Seite (leitet weg)
- Creator: sieht nur sich. Geprueft -- Luna fordert ?creator=3 (Mika) an
  und bekommt einen Report ueber SICH, der Parameter wird fuer
  Nicht-Management ignoriert
- Der Review-Termin aus dem Profil ist Steuerungswissen und wird einem
  Creator auch hier nicht mitgeschickt, genau wie im Profil selbst

Damit ist Phase 2 aus dem Konzept vollstaendig.
2026-08-28 09:48:03 +02:00
DogFatherGit df9f87af0a Workspace: Phase 2 -- LIVE, Content, Technik, Community, Schutz
Fuenf Bereiche auf einmal, aber NICHT fuenf Module. Im Deck haben sie
dieselbe Grundform: Eintraege zu einem Creator mit Art, Datum, Titel,
Text und Status. Sie unterscheiden sich nur darin, welche Arten es gibt
und ob eine Bewertung oder eine Dringlichkeit dazugehoert.

Deshalb ein gemeinsamer Unterbau: eine Tabelle, eine Sichtbarkeitsregel,
eine Pruefung, eine Ansichtsseite (bereich.html?b=live). Ein Fehler
laesst sich damit an EINER Stelle beheben statt an fuenf, und ein
weiterer Bereich waere ein Eintrag in BEREICHE -- kein neues Modul.

Arten woertlich aus dem Deck (Seiten 7-12):
  live       Vorbereitung / Waehrend LIVE / Auswertung   + Bewertung 1-5
  content    Idee / Produktion / Veroeffentlicht
  technik    Setup / Problem / Loesung / Anleitung       + Dringlichkeit
  community  Moderation / Aktion / Konflikt              + Dringlichkeit
  schutz     Richtlinie / Vorfall / Eskalation / Gelernt + Dringlichkeit

Die Oberflaeche kennt die Bereiche nicht auswendig -- welche Arten und
Zusatzfelder es gibt, sagt der Server in der Antwort.

Geprueft, und zwar fuer alle fuenf gleichzeitig:
- Sichtbarkeit: Chef sieht alles, Luna nur ihren Bereich, Mika nur
  seinen, Sam (Scout) gar nichts (404 -- Scouts haben mit der
  Creator-Betreuung nichts zu tun)
- Luna legt Eintrag mit creator_id=Mika an: landet still in ihrem
  eigenen Bereich, Mika sieht ihn nicht
- Luna auf Mikas Eintrag: 404; Luna loescht Chefs Eintrag ueber ihren
  Bereich: 403 (loeschen darf das Management und wer ihn schrieb --
  sonst koennte ein Creator eine Notiz ueber sich verschwinden lassen)
- Eintrag ueber den falschen Bereich in der Adresse ansprechen: 404
- unbekannte Art 400, Bewertung 9 (erlaubt 1-5) 400, unbekannter
  Bereich 404, fremde Herkunft 403

Beim Testen sahen Umlaute zunaechst zerstoert aus (efbfbd, das
Unicode-Ersatzzeichen). Ursache war der curl-Aufruf: Git Bash kodiert
$'\xc3\xbc' nach Locale um. Ueber den echten Weg (Browser) kommen
Umlaute, ss und Gedankenstrich unveraendert an und liegen sauber in der
Datenbank -- gegengeprueft auf Byte-Ebene.
2026-08-28 09:19:39 +02:00
DogFatherGit 05331e038a Anmeldung: Scout vor Creator, Krone fuers Management
Wunsch vom 28.08.2026: "scout soll ueber creator sein" und "das
schutzschild symbol soll bei scout sein und bei management soll eine
krone sein".

Neue Reihenfolge: Management, Scout, Creator.

Symbole:
  Management  Krone   (Gesamtuebersicht und Freigaben)
  Scout       Schild  (schirmt die Pipeline ab und prueft, bevor etwas
                       weitergereicht wird -- passt dort besser als beim
                       Management)
  Creator     Person  (unveraendert)

Die Tastaturbedienung folgt automatisch der neuen Reihenfolge, weil die
Pfeiltasten die Kacheln in Dokumentreihenfolge durchlaufen. Geprueft:
ab Management fuehrt Pfeil-runter zu Scout, dann zu Creator.

Nur eine statische Datei, kein Neustart noetig.
2026-08-28 01:27:46 +02:00
DogFatherGit 516a003dc8 Dateien: Hochladen nur fuer Management und Scouts
Wunsch vom 28.08.2026: "die dateien sollen nur die manager oder scout
hochladen koennen aber nicht die creator."

Serverseitig ueber DARF_HOCHLADEN. Die Pruefung steht bewusst VOR
express.raw -- wer nicht darf, wird abgewiesen, bevor auch nur ein Byte
entgegengenommen wird. Sonst wanderten bis zu 25 MB durch die Leitung,
nur um danach verworfen zu werden.

Ein Creator sieht und oeffnet weiterhin die Dateien seines Bereichs.
Statt des Ablegefeldes steht dort jetzt, warum es fehlt -- ein einfach
verschwundenes Feld wirkt wie ein Fehler.

Geprueft: Management 201, Scout 201, Creator 403 mit klarer Meldung;
Creator sieht und laedt weiterhin.

--------------------------------------------------------------------
Dabei ein Fehler aufgefallen, der mehrere Seiten betraf:

Das hidden-Attribut blendet nur ueber eine sehr schwache Browserregel
aus -- JEDE eigene display-Angabe schlaegt sie. Betroffen waren:
  * das Ablegefeld fuer Dateien (display: grid) -> ein Creator sah es
    trotz hidden weiterhin
  * die Verwaltungsfelder "Plan-Start" und "Naechster Review" im
    Creator-Profil (ebenfalls grid) -> ein Creator sah sie ebenfalls
  * der Zurueck-Knopf (inline-flex) -> waere vor dem Laden des Skripts
    kurz sichtbar gewesen

Behoben mit einer Regel in gate.css: [hidden] { display: none !important }

Beim Profil waren nie Daten betroffen -- der Server schickt die Felder
an einen Creator gar nicht erst mit, sie waren leer. Sichtbar sein
sollten sie trotzdem nicht.

Aufgefallen ist es erst im Screenshot. Der vorige Test hatte auf das
hidden-ATTRIBUT geprueft, und das war ja gesetzt. Die Pruefung schaut
jetzt auf die tatsaechliche Sichtbarkeit (isVisible).

Ausserdem: In der Regex zum Saeubern von Dateinamen standen die
Steuerzeichen als rohe Bytes statt als Escape-Sequenz. Die Funktion
arbeitete korrekt, aber die Datei galt dadurch als binaer -- grep
verweigerte sie, und Editoren haetten sie zerstoeren koennen. Jetzt
steht dort \x00-\x1f als Text.
2026-08-28 01:25:53 +02:00
DogFatherGit 81b51ab94f Workspace: Dateiablage mit Freigabe-Ablauf (Phase 1 vollstaendig)
/workspace/dateien.html -- Ablegen per Ziehen oder Auswaehlen,
Entwurf -> Review -> Freigabe, Filter je Zustand.

OHNE NEUE ABHAENGIGKEIT. Uploads laufen ueblicherweise ueber multer.
Hier schickt der Browser die Datei roh im Rumpf, der Name steht im Kopf
(URI-kodiert, damit Umlaute heil ankommen). express.raw() bringt den
passenden Empfaenger schon mit -- kein Zerlegen von multipart/form-data
von Hand, was gerade hier heikel waere.

Die drei Punkte, an denen Dateiablagen typischerweise scheitern:

1. Die Dateien liegen AUSSERHALB des Repos (workspace-daten/dateien/).
   Lagen sie darin, wuerde express.static sie ungeprueft ans Netz geben.
2. Auf der Platte traegt jede Datei einen erzeugten Zufallsnamen, der
   Originalname steht nur in der Datenbank. Geprueft mit dem Dateinamen
   "../../etc/passwd": gespeichert wurde "passwd", auf der Platte ein
   Zufallsname IM Ordner -- nichts ist ausgebrochen.
3. Ausgeliefert wird immer als Download mit neutralem Typ, dazu
   nosniff und CSP sandbox. Geprueft mit einer hochgeladenen
   boese.html: kommt als application/octet-stream zurueck, kann also
   keinen Code im Namen der Domain ausfuehren.

Freigabe nach Konzept: Der Zustand "freigegeben" ist eine Abnahme und
bleibt dem Management vorbehalten. Geprueft -- Luna kann Entwurf ->
Review, aber nicht freigeben (403); eine freigegebene Datei kann sie
weder aendern noch loeschen.

Sichtbarkeit wie ueberall: Chef 2 Dateien, Luna 1, Mika 0. Zugriff auf
eine fremde Datei: 404, nicht 403.

Ein Fehler beim Testen gefunden: Bei zu grosser Datei brach express.raw
ab, BEVOR der eigene Code lief -- der Fehler landete im allgemeinen
Behandler als HTTP 500. Fuer die Nutzerin sah das aus wie ein kaputter
Server statt wie "Datei zu gross". Jetzt faengt ein eigener
Fehlerbehandler am Ende des Routers das ab und antwortet mit 413 und
einer verstaendlichen Meldung.

Damit ist Phase 1 aus dem Konzept vollstaendig.
2026-08-28 01:14:00 +02:00
DogFatherGit 713297967f Workspace: Zurueck-Knopf im Entwurf "Neon-Kante"
Der vorige Knopf war zu generisch -- eine dunkle Pille mit Rand, wie sie
in jedem Baukasten steckt. Statt weiter zu raten wurden drei Entwuerfe
gebaut und zur Auswahl gestellt (Glas-Kapsel, Neon-Kante, aufklappender
Kreis). Gewaehlt: Neon-Kante.

Der senkrechte Lichtstrich in Cyan-Violett ist dasselbe Motiv wie die
Kante der Anmelde-Tafel (.tafel__kante in gate.css). Dadurch wirkt der
Workspace wie ein Stueck und nicht wie zusammengesetzte Teile.

Details:
- links flache Kante (die gehoert dem Lichtstrich), rechts rund
- der Strich ruht etwas kuerzer als der Knopf und waechst beim
  Ueberfahren auf volle Hoehe -- die Bewegung ersetzt jedes Aufblitzen
- der Pfeil rueckt drei Pixel nach links und sagt so die Richtung
- bei prefers-reduced-motion faellt alle Bewegung weg
- eigener Fokusring fuer die Tastaturbedienung

Die drei Entwuerfe liegen als eigenstaendige Seite unter entwurf/ im
Arbeitsordner (nicht im Repo), falls spaeter nochmal verglichen werden
soll.

Nur CSS, kein Neustart noetig.
2026-08-28 01:02:15 +02:00
DogFatherGit f8faab34a5 Workspace: versehentlich in den CSS-Ordner kopiertes kopf.js entfernt
Eine verunglueckte Kopierzeile hatte assets/js/kopf.js zusaetzlich nach
assets/css/ gelegt. Die Datei war nirgends eingebunden, aber ueber das
Netz erreichbar und haette bei spaeteren Aenderungen fuer Verwirrung
gesorgt, welche der beiden die echte ist.
2026-08-28 00:40:34 +02:00
DogFatherGit d3212a3f60 Workspace: Zurueck-Knopf hochwertiger, auf der Startseite entfernt
Zwei Rueckmeldungen umgesetzt.

1. Auf der Startseite gibt es den Knopf jetzt gar nicht mehr im
   Dokument. Vorher erschien er dort, sobald man von einer anderen
   Workspace-Seite kam -- aber die Startseite IST die oberste Ebene, ein
   "davor" gibt es nicht.

2. Aussehen: statt des Textzeichens "<-" jetzt ein echtes SVG-Winkel-
   symbol, Pillenform, und ein Verlaufsrahmen.

   Wichtig dabei: Die Fuellung ist DECKEND. Beim ersten Versuch war sie
   halbtransparent, dadurch schien der Rahmenverlauf durch die ganze
   Flaeche und der Knopf sah aus wie eine Hauptaktion -- genau das soll
   er nicht. Jetzt bleibt vom Verlauf nur der 1px schmale Rand sichtbar.

   Ruhezustand: dunkle Pille, feine Stahlkante, gedaempfter Text.
   Ueberfahren: Cyan-Violett-Rand, heller Text, weicher Schein, und der
   Pfeil rueckt zwei Pixel nach links -- die Bewegung sagt die Richtung
   ohne zusaetzlichen Text. Bei prefers-reduced-motion faellt sie weg.

Geprueft: Start -> kein Knopf; Aufgaben -> "Zurueck", fuehrt zurueck;
Personen direkt aufgerufen -> "Uebersicht". Keine Konsolenfehler.

Nur statische Dateien, kein Neustart noetig.
2026-08-28 00:40:19 +02:00
DogFatherGit 75d05d05d2 Workspace: Zurueck-Knopf auf allen Seiten
Wunsch: "ich will auch immer bei jeder seite auch immer einen knopf
haben damit ich zurueck gehen kann auf die seite vorher."

Neues gemeinsames Modul assets/js/kopf.js. Blindes history.back() reicht
dafuer nicht: Wer die Adresse direkt eingibt oder gerade von der
Anmeldung kommt, landet damit ausserhalb des Workspace oder wieder im
Anmeldeformular. Deshalb wird zuerst geprueft, woher der Aufruf kam:

  * vorherige Seite im Workspace  -> "Zurueck", history.back()
    (fuehrt wirklich dorthin zurueck, samt Bildlaufposition)
  * kein Verlauf, aber Unterseite -> "Uebersicht", geht zur Startseite
  * kein Verlauf auf der Startseite -> Knopf bleibt verborgen
    Ein Knopf, der nichts tut, ist schlimmer als keiner.

Der Knopf sitzt links in der Kopfleiste, an derselben Stelle wie im
Browser.

Nebenbei aufgeraeumt: Das Abmelden stand bisher in jeder der fuenf
Seitendateien noch einmal -- fuenfmal derselbe Block, fuenfmal eine
Stelle zum Vergessen. Liegt jetzt ebenfalls in kopf.js.

Geprueft: Start nach Login -> verborgen; Aufgaben von Start ->
"Zurueck", fuehrt zurueck; Kalender direkt aufgerufen -> "Uebersicht".
Keine Konsolenfehler, Abmelden funktioniert weiterhin.

Nur statische Dateien, kein Neustart noetig.
2026-08-28 00:35:26 +02:00
DogFatherGit ef79659f8f Workspace: Kalender mit Terminen, Calls und Aufgaben-Fristen
/workspace/kalender.html -- letzter grosser Baustein aus Phase 1.

Zeigt zwei Quellen in EINER Ansicht, wie im Konzept gefordert
("Kalender & Calls" neben "Deadlines" und "Wiedervorlagen"):
  * eigene Termine (Arten: Call, Termin, Review)
  * Fristen offener Aufgaben, nur lesend eingeblendet

Nach Tagen gruppiert statt als Monatsraster: Es geht um wenige, dafuer
konkrete Termine. Eine Liste liest sich dabei besser und funktioniert auf
dem Handy ohne Umbau. Zeitraum umschaltbar: 14 / 30 / 90 Tage, wobei 90
den 90-Tage-Plan aus dem Konzept abdeckt.

Sichtbarkeit wie bei den Aufgaben, wieder an einer Stelle definiert.
Geprueft mit drei Konten:
  Chef 2 Termine + 2 Fristen | Luna 2 + 1 | Sam 0 + 1
Die Fristen folgen dabei exakt der Aufgaben-Regel -- Sam sieht nur seine.

Loeschen darf das Management und wer den Termin selbst angelegt hat.
Sonst koennte ein Creator einen Call absagen, den das Management
angesetzt hat. Geprueft: Luna auf Chefs Call 403, auf ihren eigenen 200,
Sam sieht ihn gar nicht (404).

Weiteres:
- Ort/Link wird nur dann als Verweis dargestellt, wenn er wirklich mit
  http(s) beginnt -- sonst liesse sich javascript: einschleusen
- Zeitpunkt und Dauer werden geprueft (kaputtes Datum 400,
  5000 Minuten 400)
- Beginn steht als lokale Zeit ohne Zeitzone: Alle Beteiligten sitzen in
  derselben, eine falsch umgerechnete Uhrzeit waere schlimmer als gar
  keine Umrechnung
2026-08-28 00:24:12 +02:00
DogFatherGit a378b126fe Workspace: Auswahllisten und Kalender in dunkler Darstellung
Die aufgeklappte Liste eines <select> zeichnet der Browser selbst, CSS
erreicht sie nicht. Ohne Hinweis geht er von einer hellen Seite aus und
malt sie weiss -- die helle Schrift darauf war praktisch unlesbar
(gemeldet mit Screenshot).

Behebung ueber color-scheme: dark auf <html>. Das ist der Schalter fuer
alles, was der Browser selbst zeichnet: Auswahllisten, Datumskalender,
Bildlaufleisten, Textmarkierung. Zusaetzlich sind Hintergrund und
Schriftfarbe an <option> gesetzt, weil aeltere Browser und manche
Linux-Oberflaechen color-scheme nur teilweise beachten.

Damit entfallen die beiden filter: invert(.75) am Kalendersymbol der
Datumsfelder -- der Browser zeichnet es jetzt schon hell, invert haette
es wieder verdunkelt.

Nur statische Dateien, kein Neustart noetig.
2026-08-28 00:20:10 +02:00
DogFatherGit 8bfd2ae166 Workspace: Creator-Profile (Onboarding aus dem Konzept)
/workspace/profil.html -- Stammdaten, Ziele und 90-Tage-Plan, genau nach
Seite 5 des Konzepts. Management waehlt oben den Creator aus, ein Creator
sieht nur sein eigenes Profil.

Sicherheitskern ist das Feld admin_notiz. Das Konzept fordert "private
Admin-Notizen separat". Die Notiz wird deshalb nicht im Browser
ausgeblendet, sondern gar nicht erst gesendet: Die Spaltenliste der
Abfrage haengt an der Rolle (FELDER_OFFEN / FELDER_ADMIN). Dasselbe gilt
fuer Plan-Start und Review-Termin.

Geprueft:
- In der kompletten Rohantwort an den Creator kommt der Inhalt der
  internen Notiz 0-mal vor
- Creator auf fremdes Profil: 404 (lesend wie schreibend)
- Scout auf ein Profil: 404, profil.html leitet ihn weg
- Creator setzt admin_notiz selbst: wird stillschweigend ignoriert,
  der Inhalt bleibt unveraendert
- Profil einer Nicht-Creator-Person: 404

Dabei ist ein aelterer Fehler aufgefallen: /api/ich lieferte nur Name und
Rolle, nicht die eigene Nummer. Dadurch rief die Profilseite eines
Creators /api/profil/undefined auf und blieb leer. Derselbe Fehler machte
in der Personenverwaltung den Selbstvergleich unwirksam -- beim eigenen
Eintrag erschien ein "Sperren"-Knopf, den der Server dann ablehnte.
/api/ich liefert jetzt zusaetzlich die id.

Im Protokoll landen nur die Feldnamen, nie die Inhalte: Im Profil stehen
persoenliche Angaben, die nicht zusaetzlich im Audit-Log auftauchen
sollen.
2026-08-28 00:17:53 +02:00
DogFatherGit 232a2003dd Workspace: Aufgaben bearbeiten, Personenverwaltung im Browser
Aufgaben:
- Bearbeiten-Dialog (Titel, Beschreibung, Prioritaet, Frist, Zuordnung).
  Als natives <dialog>: Fokusfang, Esc zum Schliessen und Abdunklung
  ohne eigenen Code.
- Loeschen nur fuer Management, mit Rueckfrage und Protokolleintrag.
  Das Konzept will, dass Erledigtes stehen bleibt -- Loeschen ist der
  Ausnahmefall fuer Fehleintraege, nicht der normale Abschluss.

Personen (/workspace/personen.html, nur Management):
- Anlegen, Code erneuern, sperren/entsperren, Protokollansicht
- Der Code wird genau einmal in der Antwort zurueckgegeben, nie
  gespeichert; beim Schliessen auch aus dem Dokument entfernt
- Selbstschutz: niemand kann sich selbst sperren, und das letzte aktive
  Management laesst sich nicht sperren -- sonst kaeme niemand mehr hinein
- Code fuer sich selbst tauschen nur mit ausdruecklicher Bestaetigung,
  weil es die eigene Sitzung sofort beendet

Zwei Fehler, die beim Testen aufgefallen sind:

1. Rollenpruefung fehlte beim Ausliefern der Seiten. Ein Creator bekam
   personen.html mit HTTP 200 -- die Schnittstellen wiesen ihn zwar ab,
   das Geruest der Seite war aber sichtbar. GESCHUETZT ist jetzt eine
   Zuordnung Pfad -> erlaubte Rollen statt einer blossen Liste.

2. Das Protokoll nannte den falschen Verursacher. personAnlegen trug die
   NEU ANGELEGTE Person als person_id ein, der Eintrag las sich also so,
   als haette sie sich selbst angelegt. Akteur und Betroffener sind jetzt
   getrennt: Akteur in person_id, Betroffener im Text. Ueber die
   Kommandozeile angelegte Personen zeigen korrekt keinen Akteur.
2026-08-27 23:12:28 +02:00
DogFatherGit 2eecb40537 Workspace: Aufgabenbrett und Dashboard-Zahlen
Erster echter Arbeitsbereich aus dem Konzept. Kanban mit offen / in
Arbeit / Review / erledigt, dazu Prioritaet, Frist, Verantwortlicher und
Zuordnung zu einem Creator-Bereich.

Kern ist die Datentrennung, und die sitzt AUSSCHLIESSLICH im Server --
in jeder einzelnen Abfrage, an einer Stelle definiert (sichtbar()):
  admin   sieht alles
  creator sieht seinen Bereich und was ihm zugewiesen ist
  scout   sieht nur, was ihm zugewiesen ist

Geprueft mit vier Testkonten:
- Chef sieht 4, Luna 2, Mika 1, Sam 1 Aufgaben
- Luna auf Mikas Aufgabe: 404 (nicht 403 -- sonst liesse sich durch
  Ausprobieren herausfinden, welche Nummern es gibt)
- Luna legt Aufgabe mit creator_id=Mika an: wird still auf ihren eigenen
  Bereich umgebogen, Mika sieht sie nicht
- Anfrage mit fremdem Origin: 403
- ohne Anmeldung: 401, ungueltiger Status/leerer Titel: 400
- Scout bekommt in der Personenliste nur sich selbst
- Dashboard-Zahlen je Rolle korrekt eingegrenzt

Weitere Punkte:
- Texte werden im Browser nur ueber textContent gesetzt, nie innerHTML --
  ein Aufgabentitel darf keine Auszeichnung einschleusen
- ueberfaellig = Frist vorbei UND nicht erledigt; die Kachel faerbt sich
  nur, wenn wirklich etwas ansteht
- erledigt_am wird gesetzt bzw. wieder geleert, wenn eine Aufgabe
  zurueckgeholt wird
- Erledigtes verschwindet nicht, wie im Konzept gefordert
2026-08-27 23:00:17 +02:00
DogFatherGit 6d9bad880c Workspace: echte Besucher-IP statt Cloudflare-Adresse
Gefunden beim Nachsehen im Protokoll nach der ersten echten Anmeldung:
Jeder Eintrag trug dieselbe IP 172.69.220.140 -- eine Cloudflare-Adresse.

Ursache: Die Kette ist Besucher -> Cloudflare -> Caddy -> Express, aber
`trust proxy` steht auf 1. Express nimmt daher den letzten Eintrag aus
X-Forwarded-For, und das ist Cloudflare.

Auswirkung war nicht nur ein unbrauchbares Protokoll, sondern vor allem:
ALLE Nutzer teilten sich einen einzigen Sperr-Zaehler. Beim ersten
Anmelden waren nach zwei Tippfehlern plus drei Testversuchen bereits
5 von 8 verbraucht -- drei weitere und der Zugang waere fuer alle
gesperrt gewesen.

Jetzt wird CF-Connecting-IP ausgewertet (setzt Cloudflare bei jeder
Anfrage selbst, vom Besucher nicht faelschbar), mit Rueckfall auf die
Peer-Adresse. `trust proxy` bleibt bewusst unangetastet, damit die
Aenderung nur /workspace betrifft und nicht die ganze Website.

Geprueft: 8 Fehlversuche von IP A sperren IP A (429), IP B bekommt
weiterhin 401 und kann sich normal anmelden (200).

Ausserdem: Spaltenbreite im Protokoll-Ausdruck korrigiert -- bei
`anmeldung_fehlgeschlagen` (genau 24 Zeichen) klebte die Rolle am Namen.
2026-08-27 22:53:51 +02:00
DogFatherGit f3a55af80f Creator Workspace: Anmeldung, Sitzungen und Audit-Log
Erster funktionierender Login fuer /workspace. Bewusst OHNE neue
Abhaengigkeiten: Node 24 bringt node:sqlite mit, Hashing und Zufall
kommen aus node:crypto. Nichts zu kompilieren, keine fremde Lieferkette
an einer Stelle, an der es um Zugangsdaten geht.

Sicherheit:
- Codes liegen nur als scrypt-Hash (N=32768) mit eigenem Salt in der DB
- Vergleich in konstanter Zeit (timingSafeEqual)
- Sitzungstoken 32 Byte Zufall, in der DB nur als SHA-256
- Cookie httpOnly, SameSite=lax, Path=/workspace, secure abhaengig von
  req.secure (live immer an, nur der lokale http-Test kommt ohne aus)
- Sperre nach 8 Fehlversuchen je IP fuer 10 Minuten -- danach ist auch
  der richtige Code blockiert (geprueft)
- gleiche Fehlermeldung bei falscher Rolle und falschem Code
- Audit-Log fuer Anmeldung, Fehlversuch, Sperre, Abmeldung, Codewechsel

Die Datenbank liegt AUSSERHALB des Repos (../workspace-daten/): sonst
waere sie ueber express.static aus dem Netz erreichbar, und ein git pull
wuerde Nutzdaten anfassen.

workspace.js kann die Website nicht mitreissen: node:sqlite wird erst bei
Bedarf per createRequire geladen, jede Route faengt ihre Fehler selbst ab.
Faellt die DB aus, antwortet nur /workspace/api/* mit 503.

Codes werden ausschliesslich auf der Kommandozeile erzeugt
(server/workspace-code.js) und dort einmal angezeigt -- nie in Git.

Ausserdem: start.html als geschuetzte Seite nach dem Anmelden. Der Schutz
sitzt serverseitig vor express.static, nicht nur im Browser.
2026-08-27 22:35:55 +02:00
DogFatherGit 1f769c9de7 Workspace-Anmeldung: kompaktere Tafel auf niedrigen Bildschirmen
Auf dem iPhone 13 (nur 664 px nutzbare Hoehe) verdeckte die Tafel das
Motiv fast vollstaendig -- sichtbar waren nur die Ohrenspitzen. Neue
Regel fuer max-height 760px: kleinere Abstaende und Schriftgroessen,
und die Bildflaeche wird flacher.

Der zweite Teil ist der eigentliche Kniff: Bei flacherer Bildflaeche
skaliert der Ausschnitt nach BREITE statt nach Hoehe, dadurch zeigt er
den oberen Teil mit den Gesichtern statt seitlich zu beschneiden.

Geprueft auf iPhone 13, iPhone SE und Pixel 5.
2026-08-27 18:15:08 +02:00
DogFatherGit 19f969f36f Creator Workspace: Anmeldeseite unter /workspace
Erstes sichtbares Stueck des Creator-Workspace-Konzepts. Reine statische
Dateien in einem neuen Ordner workspace/ -- express.static liefert den
Repo-Ordner aus, die Seite ist damit ohne Servercode-Aenderung und ohne
Neustart unter /workspace/ erreichbar. Rein additiv: an bestehenden
Dateien wurde nichts geaendert.

Motiv: Dogfather und Hasi Dog stehen rechts im Bild, deshalb liegt die
Code-Tafel links ueber der ruhigen Flaeche (bei VanVans Buchhaltung ist es
gespiegelt, weil dort das Emblem links steht). Drei Layoutfaelle, damit die
Figuren in keiner Fenstergroesse von der Tafel ueberdeckt werden.

Bewusst nur WebP, kein AVIF: send 0.19.2 (Express 4) kennt AVIF nicht und
liefert es als application/octet-stream aus -- wegen des nosniff-Headers
verweigert der Browser das Bild dann komplett. Lokal gegen den echten
Express-Server geprueft: 0 Konsolenfehler, 0 CSP-Verstoesse.

Anmelden funktioniert noch nicht (kein Endpunkt); das Formular sagt das
jetzt ehrlich statt "Code stimmt nicht".
2026-08-27 18:13:05 +02:00
DogFatherGitandClaude Opus 5 43264c0990 Startseite hochwertiger: Typografie, Weltfarben, Event-Karte
Wunsch 27.08.2026: "soll nur noch hochwertiger und professioneller
aussehen." Kein neues Design, sondern die Details, die den Unterschied
zwischen "gemacht" und "gesetzt" ausmachen:

- Ueberschrift enger gesetzt (-.022em, Zeilenabstand 1.08, text-wrap:
  balance). Bei 4rem wirkt normale Laufweite auseinandergefallen; die
  drei Zeilen verteilen sich jetzt gleichmaessig statt mit kurzer
  Restzeile.
- Jede Welt-Kachel traegt die Farbe IHRER Welt statt dreimal derselben:
  Babyblau, Silber, das gedeckte Rot von Spicy Media. Die Werte sind
  nicht erfunden, sondern die --accent-Toene der drei Themendateien.
- Plaketten ("WELT 1") klein, in Versalien, weit gesperrt -- liest sich
  als Kapitelmarke statt als Beschriftung.
- Event-Karte: Datum als gesperrte Versalzeile (ordnet sich dem Titel
  unter), "Mehr erfahren" als ruhiger Knopf statt nacktem Textlink,
  Haarlinie am Bild, Text auf 5 Zeilen begrenzt -- damit nicht die
  Textlaenge aus der Verwaltung das Aussehen der Startseite bestimmt und
  bei zwei Events beide Kacheln gleich hoch sind (geprueft: 572/572px).

ZWEI FUNDE BEIM PRUEFEN, BEIDE WICHTIGER ALS DER FEINSCHLIFF:

1. INHALTSRICHTLINIE UND ZEILENENDEN. Der Browser rechnet die Pruefsumme
   eines Inline-Skripts NICHT ueber die Bytes der Datei, sondern ueber
   den Text im Dokument -- und der HTML-Parser ersetzt beim Einlesen
   jedes CR LF durch LF (HTML-Spezifikation, "preprocessing the input
   stream"). Eine Datei mit Windows-Zeilenenden ergibt serverseitig also
   eine ANDERE Summe, und die Seite fuehrt ihr eigenes Skript nicht mehr
   aus: kein Live-Status, keine Events, nichts. Live war es unauffaellig
   (Linux-Auscheckung hat LF), auf dem Windows-Rechner sofort tot
   (core.autocrlf=true). inhaltsrichtlinie.js vereinheitlicht jetzt vor
   dem Rechnen auf LF -- das ist die Summe, die der Browser wirklich
   bildet, und macht die Richtlinie unabhaengig davon, mit welchem
   Werkzeug eine Datei zuletzt gespeichert wurde.

2. TESTS MIT FESTEM PORT KOENNEN LUEGEN. Drei Server aus frueheren
   Laeufen liefen noch. Neue Testlaeufe konnten ihren Port nicht belegen,
   starben still -- und massen weiter gegen den ALTEN Code. Ergebnis
   waren Fehlermeldungen zu einem laengst behobenen Fehler. pruef-musik,
   pruef-kasse und pruef-startseite suchen sich jetzt einen freien Port.

Neuer Test pruef-startseite.mjs (19 Pruefungen) mit FESTEN Event-Daten:
Beim Pruefen kam einmal nichts vom Server, der Abschnitt blieb leer und
der Test haette "kein Fehler" gemeldet, obwohl er nichts gesehen hat.
Geprueft werden Maske am Artwork, genau eine Kachel bei einem Event,
volle Breite, saubere Textkuerzung auf ganze Zeilen, eigene Weltfarben,
zwei gleich hohe Kacheln bei zwei Events, Handy ohne Ueberlauf.

Alles gruen: startseite 19, musik 17, kasse 15, inhaltsrichtlinie 22,
handy 56.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 16:06:09 +02:00
DogFatherGitandClaude Opus 5 57d90b9905 4 weitere überdimensionierte Bilder verkleinert + Cache-Buster vereinheitlicht
pruef-bildgroessen.mjs (neu) misst systematisch über alle Seiten
(Desktop + Handy, mal Pixeldichte), welche <img> größer sind als ihre
größte Anzeige. Fand 4 klare Fälle:

  streamer-mascot.jpg            900px, gezeigt 230px  -> 480px  360->119 KB
  bewerben-poster-dogfather.jpg  800px, gezeigt 204px  -> 440px  199->80 KB
  avatar-bananenstift.jpg       1122px, gezeigt 108px  -> 400px  167->25 KB
  avatar-marina.jpg             1086px, gezeigt  90px  -> 400px  157->20 KB

Zusammen 639 KB gespart. Avatare bewusst auf 400px (großzügiger als die
2x-Anzeige), falls doch mal eine Detailansicht kommt. Qualität am
Maskottchen per Screenshot geprüft: scharf, Schrift lesbar, keine Artefakte.

CACHE-BUSTER-BUG behoben: 69 Ressourcen-Verweise standen noch auf dem alten
Marker "20260827hero" (einer sogar auf "20260820h"), der Rest auf
"20260828c". Verschiedene Seiten luden dieselbe main.css/js unter
verschiedenen Cache-Keys -- wiederkehrende Besucher der hero-Seiten bekamen
bei Änderungen eine veraltete gecachte Fassung. Die Playwright-Tests sahen
das nie (leerer Cache). Jetzt alle 245 einheitlich auf 20260828d; der
bewusste admin-auth "-jedesmal"-Marker bleibt unberührt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:47:35 +02:00
DogFatherGitandClaude Opus 5 508009ecc7 Startseite: Hintergrund-Kanten weg, "Event des Jahres" neu aufgebaut
Gemeldet 27.08.2026: "es ueberschneidet den hintergrund sieht irgendwie
scheisse aus."

1. HARTE KANTEN IM HINTERGRUND
   Das scharfe Trio-Artwork lag als CSS-Hintergrund mit "contain" hinter
   der Seite und endete an einer messerscharfen Linie (bei 1366x800 exakt
   bei 570px und 1345px); darueber/darunter lag sichtbar die verwaschene
   Fassung. Das las sich wie ein aufgeklebtes Band quer ueber der Seite.
   Jetzt ein echtes <img>, das an seinen EIGENEN Raendern weich in die
   verwaschene Ebene uebergeht. Entscheidend: Die Maske haengt am Bild,
   nicht am Fenster -- als CSS-Hintergrund war das unmoeglich, weil die
   Kante je nach Fensterformat wandert.

2. "EVENT DES JAHRES" -- 700px tote Flaeche
   Eine 480px schmale Kachel sass zentriert in einer 1180px breiten
   Kiste, die selbst Rahmen und Hintergrund trug: drei Rahmen ineinander
   um einen einzigen Inhalt, links und rechts je 350px Leere. Jetzt volle
   Breite mit Bild links / Text rechts, die umgebende Kiste ist rahmenlos.
   Hoehe von 656px auf 332px, ohne dass Inhalt verloren geht -- das Bild
   ist dabei doppelt so gross wie vorher.

3. Welt-Kacheln mit einer Spur Milchglas und feinem Lichtsaum, damit sie
   zur Szene gehoeren statt als flache Rechtecke daraufzuliegen.

BEIM BAUEN GEFUNDEN UND KORRIGIERT: Der erste Entwurf hat die versteckte
zweite Event-Kachel wieder sichtbar gemacht (leere Kachel mit einsamem
"Mehr erfahren"). Ursache ist die im Code zweimal dokumentierte Falle:
".jahres-event-card[hidden]" ist genau so stark wie eine
Zwei-Klassen-Regel und verliert gegen die spaetere. Deshalb steht in den
neuen Regeln ueberall :not([hidden]).

Versionsnummern von main.css/main.js hochgesetzt -- Assets werden mit
max-age=14400 ausgeliefert, sonst haette Dogi die Aenderung bis zu vier
Stunden nicht gesehen.

Geprueft: pruef-handy (56), pruef-barrierefrei (60), pruef-design (0
Fundstellen), pruef-blickfang (13), pruef-assets (67), pruef-musik (17)
-- alle gruen. Der Musik-Knopf holt seine Farben jetzt aus dem <img>
statt aus dem CSS-Hintergrund (main.js), sonst waere er ausgerechnet auf
der Startseite auf die Ersatzfarbe zurueckgefallen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:45:23 +02:00
DogFatherGitandClaude Opus 5 e2a40313d6 Startseiten-Bilder verkleinert: 3 überdimensionierte Fotos (687 KB gespart)
Der Performance-Check (pruef-tempo.mjs, neu) zeigte die Startseite mit
3,5 MB, überwiegend Bilder. Drei davon waren viel größer als je
angezeigt (gemessen über Desktop + Handy, alle Seiten, mal Pixeldichte):

  card-dogfather.jpg  1200px, angezeigt max 359px  ->  720px  534->201 KB
  casper-4.jpg        1400px, angezeigt max 359px  ->  720px  350->115 KB
  casper-3.jpg        1400px, angezeigt max 359px  ->  720px  259->140 KB

720px = doppelte Anzeigebreite, also auch auf Retina-Displays scharf.
Qualität an zwei Motiven per Screenshot geprüft: keine sichtbaren
Artefakte, Schrift und Details erhalten.

card-vanvan bewusst UNANGETASTET: wird auf vanvan.html mit 578 CSS-px
gezeigt, bräuchte für Retina ~1156px -- das 1200er ist dort passend.

Verkleinert über Browser-Canvas (kein ImageMagick/sharp verfügbar; das
gefundene "convert" war das Windows-Dateisystem-Tool, nicht ImageMagick).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:40:22 +02:00
DogFatherGitandClaude Opus 5 5b2a1daabe Öffentliche Avatar-Uploads vor dem Absenden verkleinern (1,1 MB -> ~40 KB)
Der Performance-Check (pruef-tempo.mjs, neu) fand als größte Einzel-
ressource ein 1,1-MB-Profilbild, ausgeliefert an jeden Besucher der
Startseite und von stimmen.html -- angezeigt wird es handgroß.

Ursache: Das öffentliche Stimmen-Formular (stimmen.js) lud Profilbilder
ROH hoch. Die Bildaufbereitung vom 27.08. bekam nur die Verwaltung, nicht
das öffentliche Formular. So landete das Kamera-Foto in voller Auflösung
auf dem Server.

- bild-vorbereiten.js (neu): die Verkleinerungs-Funktion, jetzt einmal
  und parametrisierbar (maxBreite). Verwaltung nutzt weiter 1600px,
  Avatare 512px. Test pruef-bild-vorbereiten.mjs: 8/8, u.a. 512er-Avatar
  ~40 KB statt >1 MB.
- stimmen.js: verkleinert vor dem Upload (window.bildVorbereiten mit
  maxBreite 512); fällt die Funktion aus, wird das Original genommen --
  kein Upload darf daran scheitern.
- stimmen.html: lädt bild-vorbereiten.js; veralteten Kommentar
  richtiggestellt (behauptete "kein Upload", obwohl es seit 21.08. einen
  gibt -- mit Bremse, Typ-/Größenlimit, Freigabe-Pflicht).

Zwei neue Checks als bleibende Absicherung mit committet: pruef-links.mjs
(41 interne Ziele, 0 kaputt) und pruef-assets.mjs (67 Ressourcen, 0 fehlen).

NOCH OFFEN: verwaltung.html hat noch eine eigene, identische Inline-Kopie
der Funktion -- die Zusammenführung ist ein eigener, testbarer Schritt
(das Inline-Skript dort ist groß und die Verwaltung hinter dem Gate schwer
live zu testen). Das bestehende 1,1-MB-Bild auf dem Server bleibt, bis es
neu hochgeladen/ersetzt wird.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:17:30 +02:00
DogFatherGitandClaude Opus 5 3e2f5306ad Aufraeumen: mein Wegwerf-Messskript _probe2.mjs wieder entfernt
Es ist im vorigen Commit versehentlich mitgegangen, weil der Testlauf
in die Zeitgrenze lief und das "rm" danach nie ausgefuehrt wurde. Der
Inhalt ist als richtiger Test in pruef-kasse.mjs aufgehoben -- die
Wegwerf-Fassung gehoert nicht ins Projekt.

Hinweis: pruef-tempo.mjs im selben Commit ist NICHT von mir, es lag
unversioniert im Arbeitsordner und wurde von "git add -A" miterfasst.
Es bleibt drin, weil Loeschen fremder Arbeit schlimmer waere als ein
Commit zu frueh -- Dogi entscheidet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:11:56 +02:00
DogFatherGitandClaude Opus 5 8fa15c04f1 Kasse und Google-Anmeldung in der Inhaltsrichtlinie freigegeben
Fortsetzung des Musik-Fundes: PayPal und Google haetten an derselben
Stelle still versagt, sobald Dogi seine Zugangsdaten eintraegt -- leerer
Bereich statt Bezahlknopf, ohne Fehlermeldung, ohne Protokolleintrag.

Vorgehen bewusst gemessen statt geraten: Erst die Angaben der Anbieter
(PayPal "Best Practices", Google CSP-Abschnitt der Setup-Anleitung),
dann im echten Browser mit PayPals offizieller Testkennung
"client-id=test" nachgemessen und die Verstoesse ueber das Ereignis
securitypolicyviolation eingesammelt. Die Messung hat zwei Dinge
gefunden, die in keiner Anleitung standen:

- www.sandbox.paypal.com (frame-src + connect-src). Beim Einrichten
  testet man mit Sandbox-Zugangsdaten; ohne diesen Eintrag haette die
  Kasse in genau dieser Phase nicht abgeschlossen werden koennen.
- accounts.google.com/gsi/style (style-src). 'unsafe-inline' deckt das
  NICHT ab -- es erlaubt nur Stile im Dokument, keine nachgeladene
  Stilvorlage. Der Anmelde-Knopf waere unformatiert erschienen.

Bewusst einzelne Adressen statt PayPals vorgeschlagener Platzhalter
(*.paypal.com): Was die Messung nicht gebraucht hat, steht nicht drin.
Skripte bleiben ohne 'unsafe-inline' -- die Pruefsummen-Loesung fuer die
eigenen Inline-Bloecke bleibt unangetastet, und ein zusaetzliches
'unsafe-inline' waere neben Pruefsummen ohnehin wirkungslos.

Neuer Test pruef-kasse.mjs (15 Pruefungen): laedt das echte PayPal-SDK,
baut die Bezahlknoepfe wirklich auf, rendert den echten Google-Knopf und
verlangt NULL Verstoesse. Bestandstest pruef-inhaltsrichtlinie.mjs (22)
und pruef-musik.mjs (17) bleiben gruen -- inklusive der Gegenprobe, dass
eingeschleuste Skripte weiterhin blockiert werden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:11:27 +02:00
DogFatherGitandClaude Opus 5 87db17704a Test: kein Bild / keine eingebundene Datei fehlt
Prüft, was die öffentlichen Seiten laden: Bilder, og:image/twitter:image,
Favicon, Manifest. Eine fehlende Datei zeigt sich als leerer Kasten oder
kaputte Teilen-Vorschau, oft unbemerkt.

Ergebnis: 67 eigene Ressourcen, alle vorhanden, 0 fehlen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:07:55 +02:00
DogFatherGitandClaude Opus 5 3a8a52aa49 Test: kein interner Link zeigt ins Leere
Sammelt alle internen Links von 24 öffentlichen Seiten und prüft jedes
Ziel einmal gegen die echte Domain. Weiterleitungen (3xx, z.B. auf eine
Zugangswand) gelten als gültig, nur 4xx/5xx als toter Link.

Ergebnis: 41 Ziele, alle in Ordnung, 0 kaputt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:06:36 +02:00
DogFatherGitandClaude Opus 5 1d99da8ef8 Musik-Knopf repariert: Inhaltsrichtlinie blockierte die Spotify-Player
Gemeldet am 27.08.2026 ("unten rechts laeuft was nicht richtig mit der
Musik"). Ursache lag nicht in der Seite, sondern in der am 26.08.2026
eingefuehrten Inhaltsrichtlinie: "frame-src 'none'" verbietet dem
Dokument jede Einbettung -- und die beiden Player (Hasidog, Van-Van)
sind genau das. Der Knopf reagierte, das Feld ging auf, die Player
blieben leer. Kein Absturz, keine Fehlermeldung auf der Seite; die
Begruendung stand nur in der Browser-Konsole.

- frame-src erlaubt jetzt genau eine Quelle: https://open.spotify.com.
  Kein Sternchen, kein 'unsafe-*'. Was im Spotify-Rahmen passiert,
  regelt Spotifys eigene Richtlinie.
- Neuer Test pruef-musik.mjs (17 Pruefungen). Er startet bewusst den
  ECHTEN server/index.js statt eines Datei-Servers -- ein einfacher
  Datei-Server erzeugt die Kopfzeile gar nicht und haette den Fehler
  nie gesehen. Geprueft wird im echten Browser, ob die Rahmen wirklich
  von open.spotify.com laden (statt einer Fehlerseite), dazu Tippziel,
  Position, Schliessen per Knopf und Escape, Handy-Layout.

Beim Nachsehen aufgefallen und NOCH OFFEN: PayPal-Kasse und
Google-Anmeldung auf abonnieren.html laden ihre Skripte und Rahmen
ebenfalls von fremden Adressen. Beide sind derzeit nicht scharf
(googleClientId/paypalClientId sind null), wuerden aber mit derselben
Richtlinie an derselben Stelle still ausfallen, sobald Dogi seine
Zugangsdaten eintraegt. Wird gesondert mit ihm besprochen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 13:52:36 +02:00
DogFatherGitandClaude Opus 5 887da7b09b Verwaltung: Gate-Text an die neue Anmelde-Regel angepasst
Unter der Ueberschrift stand weiterhin "gilt danach 24 Stunden" -- seit
heute gilt der Code aber nur, solange die Seite/App offen ist. Ein
Versprechen, das die Seite nicht mehr einhaelt, ist schlimmer als gar
keins: Dogi haette sich sonst darauf verlassen, morgen ohne Code
weiterzukommen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 13:29:03 +02:00
DogFatherGitandClaude Opus 5 7960b212c8 Verwaltung: Zugangscode wieder bei jedem Oeffnen noetig
Dogi hat die Lockerung vom 04.08.2026 ("Code nur einmal am Tag") heute
zurueckgenommen. Gewaehlte Auspraegung: Code beim OEFFNEN der Seite/App,
Neuladen im selben Tab wirft nicht raus, keine Abmeldung bei Untaetigkeit.

- admin-auth.js legt die Sitzung jetzt in sessionStorage statt in
  localStorage: gehoert zum Tab/App-Fenster, uebersteht F5 und Uploads,
  ist beim naechsten Oeffnen weg. Zweiter Tab = eigener Code.
- Alte localStorage-Sitzung wird beim ersten Laden einmalig entfernt --
  sonst waere Dogi trotz Umstellung mit dem alten Token weiter drin und
  ein Token laege monatelang im Browser herum.
- Versionsnummer des Skripts hochgesetzt: Caddy liefert Assets mit
  max-age=14400, der Browser haette die alte Anmelde-Logik sonst bis zu
  vier Stunden weiterbenutzt.
- Der 24-Std-Ablauf bleibt als Rueckfallsicherung fuer tagelang offene
  Tabs; der Server erzwingt dieselbe Grenze weiterhin selbst.
- Neuer Test pruef-verwaltung-anmeldung.mjs (12 Pruefungen: Gate beim
  Oeffnen, alte Sitzung wirkungslos, falscher/richtiger Code, F5 bleibt
  drin, neuer Tab verlangt Code, Abmelden raeumt auf, Untaetigkeit meldet
  NICHT ab).
- pruef-verwaltung-kacheln.mjs: echten Aufruf ans Live-Backend abgefangen
  und auf die Supporter-Zeilen gewartet -- der Test hatte dadurch
  gelegentlich falsch gemeldet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 13:28:14 +02:00
DogFatherGitandClaude Opus 5 0465a7a14a Verwaltung: Stimmen als kompakte Kacheln, Supporter-Liste auf 4 gekuerzt
Beides auf Wunsch vom 27.08.2026 ein-/ausklappbar:

- Stimmen liegen jetzt in einem Raster (auto-fill, min. 290px) statt
  untereinander -- am Computer stehen 3-4 Kacheln nebeneinander, am Handy
  eine. Zugeklappt ist genau die erste Reihe sichtbar; wie viele Kacheln
  das sind, liest die Logik aus dem Raster selbst aus (getComputedStyle
  liefert bei auto-fill die gebauten Spuren), damit "erste Reihe" auf
  jeder Breite stimmt -- inklusive Neuberechnung beim Groessenwechsel.
- Supporter-Tabelle zeigt nur noch die 4 zuletzt Angemeldeten, Rest per
  Knopf. Knopftext nennt immer die Gesamtzahl, damit nichts versteckt
  wirkt; beim Zuklappen springt die Ansicht sauber zum Listenanfang.
- Neuer Test pruef-verwaltung-kacheln.mjs (18 Pruefungen, Computer +
  Handy, inkl. Groessenwechsel und Ueberlauf-Kontrolle).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 13:17:05 +02:00
DogFatherGitandClaude Opus 5 988a2fdc95 DEPLOY.md: interner Deploy verifiziert — npm-Warnungen eingeordnet, sleep 6
Der erste echte Lauf des internen Deploy-Blocks (28.08.2026) förderte zwei
harmlose, aber verunsichernde Punkte zutage:

- npm ci warnt "allow-scripts ... better-sqlite3": Die allowScripts-Sperre
  blockiert den nativen Build, aber prebuild-install liefert ein fertiges
  Binary. Dienst lädt danach die DB einwandfrei -> in Ordnung. Als Kontrolle
  dokumentiert.
- sleep 2 war zu kurz: Der health-Check lief, bevor der Dienst auf 4200
  hörte -> kurzzeitig 502 / "activating", obwohl gleich darauf alles läuft.
  Auf sleep 6 erhöht, mit Hinweis, wann ein 502 wirklich ein Problem ist.

Deploy selbst war erfolgreich: Bremse greift live (20 durch, 21. -> 429),
multer 2.2.0 + node-cron 4.6.0 installiert, better-sqlite3 lädt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 12:41:38 +02:00
DogFatherGitandClaude Opus 5 5c546207e7 DEPLOY.md: interner Deploy-Block ohne führendes cd
Der Block scheiterte in der Praxis am ersten "cd /home/dogiintern/...":
Das Verzeichnis ist 700, selbst dogi kommt per cd nicht hinein, nur
dogiintern über sudo -u. Jetzt git -C statt cd, und das npm ci in einer
sudo -u dogiintern bash -c 'cd ... && npm ci'-Shell. Mit Warnhinweis,
warum kein cd davor stehen darf.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 12:03:52 +02:00
DogFatherGitandClaude Opus 5 68b421b79a Zahlungen-Bühne: besserer Bildausschnitt statt flacher Mitte
Das cryo-kristall-Motiv (Zahlungen) ist als einziges der sechs HOCHKANT
(702x941) statt quer (1672x941). Auf der breiten 16:9-Bühne zeigt "cover"
davon nur einen horizontalen Streifen -- der mittige (Standard center
center) traf die unruhigen Kristallsplitter und wirkte flach und
beschnitten.

Jetzt zeigt Zahlungen den oberen Streifen (background-position center
15%): die eleganten geschwungenen Kristallbänder rechts als Blickfang,
links ruhiger dunkler Raum für die Karten -- eine klare Komposition wie
bei den anderen Motiven. Nur für Zahlungen überschrieben, die übrigen
fünf bleiben mittig.

Nebenbei: Der Wert kommt aus --vw-blick, das für alle Bereiche längst
definiert, bisher aber nirgends ausgelesen wurde (toter Code). Die neue
.vw-buehne-Ausnahme wendet ihn für Zahlungen erstmals an.

Service-Worker-Cache v63 -> v64 (beide Stellen), damit Geräte mit der App
den neuen Ausschnitt bekommen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 12:02:26 +02:00
DogFatherGitandClaude Opus 5 50ff8e0f4c DEPLOY.md: voller Deploy-Block für den internen Dienst + Stand nachgezogen
- Vollständiger kopierfertiger Deploy-Block für server-internal MIT npm ci:
  pull -> npm ci -> Lade-Test der nativen Module -> restart -> Selbsttest,
  als eine &&-Kette, die bei jedem Fehler VOR dem Neustart abbricht (alter
  Dienst läuft dann unberührt weiter). Bisher stand dort nur pull + restart
  ohne npm ci -- genau die Lücke, durch die Code und Pakete auseinanderliefen.
- Wächter: 18 -> 19 Punkte (Sicherungsprüfung dazugekommen).
- Zwei offene Punkte ergänzt: ausstehender Server-Neustart (Kernel/OpenSSL
  liegen bereit) und der ausstehende server-internal-Deploy.
- Veralteten Datenschutz-404-Eintrag abgehakt (heute live 401 verifiziert).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 11:16:30 +02:00
DogFatherGitandClaude Opus 5 9bd9357185 Überschriftenordnung im Hauptinhalt: keine übersprungenen Ebenen mehr
Fünf Seiten hatten einen Sprung h1 -> h3 im Hauptinhalt (die Kachel-
Überschriften waren h3, ohne h2 dazwischen). Für Bildschirmleser fehlte
damit eine Ebene im "Inhaltsverzeichnis" der Seite (WCAG 1.3.1).

Pro Seite der passende, optik-erhaltende Weg -- jede Änderung mit
Screenshot bzw. gemessener Schriftgröße gegengeprüft:

- werte, kontakt, index: Kachel-h3 -> h2 (die Kacheln SIND die
  Hauptabschnitte unter der h1). Neue Regel .card > h2 hält die kompakte
  h3-Optik (1.62rem); Varianten .card-brand/.card-feature bleiben über
  ihre eigenen h3-Regeln unberührt. Gemessen: 25.92px vorher = nachher.
- bewerben: die schon sichtbaren Gruppenlabels (Community / Agentur)
  waren <span> -> jetzt <h2> mit derselben Klasse. Optik per Screenshot
  identisch (zentriert, uppercase, Zierstrich).
- links: die Kacheln nutzen Spezial-Varianten mit eigenen Größen, ein
  Tag-Wechsel wäre riskant -> stattdessen eine unsichtbare Gruppen-
  überschrift (.sr-only, neue Klasse nach WCAG-Standard). Ändert die
  Optik nicht, vervollständigt aber die Ordnung für Bildschirmleser.

Cache-Buster 20260828b (main.css geändert: .card > h2, .sr-only).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 11:08:37 +02:00
DogFatherGitandClaude Opus 5 8a2943df35 Fußzeilen-Spaltentitel h4 -> h2 (Überschriftenordnung, WCAG 1.3.1)
Die drei Fußzeilen-Spaltentitel (Filipe, Dogi&Hasi & Manager, Mehr)
waren <h4>, obwohl der Hauptinhalt bei h2/h3 endet -- ein Sprung in der
Überschriftenordnung (h2 -> h4) auf jeder Seite. Für Bildschirmleser ist
die Überschriftenliste das Inhaltsverzeichnis; eine übersprungene Ebene
stört die Orientierung.

Jetzt <h2> (eigenständige Abschnitte unter der Seiten-h1). Die Optik
bleibt exakt: Der CSS-Selektor .footer-grid h4 wurde zu .footer-grid h2
umgezogen, die kleine, gedämpfte Darstellung (.85rem, uppercase,
Akzentfarbe) überschreibt weiterhin die große globale h2-Größe.

Teil des Barrierefreiheits-Durchgangs (pruef-barrierefrei.mjs). Behebt
die reinen Fußzeilen-Fälle; die Hauptinhalt-Überschriften folgen separat.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 10:57:55 +02:00
DogFatherGitandClaude Opus 5 8a2d2b802a Wächter-Protokoll hält sich selbst klein (keine unbegrenzte Log-Datei)
waechter.log wuchs unbegrenzt: alle 5 Minuten eine Zeile, ~288/Tag. Ohne
Grenze irgendwann zu groß zum Durchsehen -- dann verliert das Protokoll
seinen Zweck.

Der Wächter kürzt jetzt selbst, statt logrotate: Das bräuchte eine Datei
in /etc (kein Schreibrecht) und einen Extra-Dienst. Vor dem Anhängen wird
nur die Größe abgefragt (billig); erst über 1 MB (~45 Tage) wird die Datei
einmal gelesen und auf die jüngsten 2000 Zeilen (~1 Woche) gestutzt.

Beim Bauen einen eigenen Fehler gefangen: statSync war in waechter.mjs
nicht importiert (beim Auslagern der Sicherungsprüfung mit entfernt worden).
node --check meldet das nicht -- es hätte erst zur Laufzeit im nächsten
Cron-Lauf gekracht. Import ergänzt.

Test pruef-protokoll-kuerzen.mjs: klein bleibt unangetastet, groß wird auf
die JÜNGSTEN Zeilen gestutzt (älteste fallen weg), Grenzfall und fehlende
Datei sauber. 8/8.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 10:40:03 +02:00
DogFatherGitandClaude Opus 5 91bf0755cb Test: alle Admin-Routen automatisch auf Zugriffsschutz prüfen
Liest alle 109 /admin/-Routen direkt aus index.js (nicht hartkodiert)
und spricht jede mit einem ungültigen Token an. Erwartet 401/403.

Der Sinn: Der Schutz sitzt in jedem einzelnen Handler statt als Sperre
vor der Gruppe. Wer eine neue Route hinzufügt und den Aufruf vergisst,
macht sie unbemerkt öffentlich -- das fällt beim Klicken nie auf. Weil
der Test die Routen aus dem Code liest, taucht eine künftig vergessene
Absicherung automatisch als Fehlschlag auf, ohne dass jemand den Test
pflegt.

Ungültiges Token statt gar keins ist der gefahrlose Weg, das auch für
schreibende Routen (anlegen, löschen) gegen die echte Domain zu tun:
Der Schutz greift vor dem Handler, es kann nichts verändert werden.

Ergebnis live: 109 geschützt, 0 auffällig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 10:37:25 +02:00
DogFatherGitandClaude Opus 5 a612a0f0fd Anfragebremse für /submit und /testimonials/submit (die letzten zwei ungebremsten Routen)
Bei der Bestandsaufnahme als "keine Route hat eine Bremse" gemeldet --
das war falsch (grep suchte nach rateLimit/bremse, im Code heißen sie
Kontingent/Sperre/Fehlversuche). Fast alle öffentlichen Routen SIND
gebremst: Anmeldung, Supporter-Login, Upload, Kundenanfragen. Übrig
blieben genau zwei schreibende Routen: Bewerbungen und Stimmen.

Ohne Bremse könnte ein Skript die Datenbank mit Müll fluten. Kein
Sicherheitsleck (beide landen in einer Warteschlange, nichts wird
ungesehen veröffentlicht), aber eine sinnvolle Härtung.

Statt das vorhandene Muster ein drittes Mal zu kopieren: ein Baustein
lib/kontingent.js, den nun alle drei Routen nutzen. Zwei Verbesserungen
gegenüber dem Original in testimonials.js:
- getrennte Töpfe je Zweck (ein Bild-Upload verbraucht kein
  Bewerbungs-Kontingent)
- Selbstreinigung: die alte Zähler-Map ließ jede IP für immer im
  Speicher stehen (langsames Leck), die neue räumt abgelaufene Einträge auf

Grenzen: Uploads 10/Stunde/IP (belegen Plattenplatz), Text-Einreichungen
20/Stunde/IP (großzügig für geteilte Anschlüsse, stoppt Fluten).

Tests: pruef-kontingent.mjs (Baustein, 7/7), test-kontingent-routen.mjs
(echte Routen liefern 429 ab Grenze, getrennte Töpfe, IPs unabhängig,
4/4). Bestehende Tests unverändert grün (37/37).

NOCH NICHT LIVE: server-internal läuft aus /home/dogiintern (kein Zugriff),
wird mit den übrigen Server-Änderungen in einem Deploy live geschaltet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 10:35:54 +02:00
DogFatherGitandClaude Opus 5 55775b53ea Beleg: die multer-DoS-Lücke ist in unserem Setup nicht auslösbar
Gestern als "kritisch, 5 Anfragen töten den Dienst dauerhaft" gemeldet.
Bei genauer Prüfung heute stimmt das nicht:

- index.js hat einen process.on("uncaughtException")-Handler, der genau
  das Prozess-Ende abfängt, das CVE-2025-7338 beschreibt. In drei
  Angriffsvarianten (roher Socket, chunked ohne Abschluss-Chunk,
  content-length-Lüge) blieb der Dienst am Leben.
- Die öffentliche Upload-Route hat eine eigene IP-Bremse (10/Stunde) und
  multer-Härtung; trust proxy ist gesetzt, die Bremse greift pro echter IP.

multer 1.4.5 hat die CVE trotzdem (Fakt) — das Update auf 2.x bleibt
richtig als Wurzelbehandlung, ist aber Hygiene, kein Notfall. Dieser Test
dokumentiert den Nachweis, damit die Einordnung nachvollziehbar bleibt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 10:28:01 +02:00
DogFatherGitandClaude Opus 5 46563b02fc Waechter merkt jetzt, wenn die naechtliche Sicherung ausfaellt
Beim Nachweis des ersten automatischen Laufs gefunden: Der Cron-Eintrag
endet auf >/dev/null 2>&1 -- jede Fehlermeldung wird verworfen. Das
Sicherungsskript fuehrt zwar ein eigenes Protokoll, aber alles, was VOR
der ersten Protokollzeile schiefgeht (Skript geloescht, sqlite3 weg,
Platte voll, Cron gestoppt), passiert spurlos. Niemand haette es
gemerkt -- ausser in dem Moment, in dem man die Sicherung braucht.

Der Waechter prueft ab sofort die Datei selbst, nicht das Protokoll:
Ein Protokoll kann "erfolgreich" melden, waehrend die Datei fehlt.

In lib/ ausgelagert, weil waechter.mjs beim Import sofort seinen ganzen
Durchlauf startet -- testbar war die Funktion dort nicht.

Beim Testentwurf einen eigenen Fehler gefunden: readdirSync wirft
sowohl bei fehlendem Ordner (ENOENT) als auch bei fehlenden Rechten
(EACCES). Die erste Fassung behandelte beides als "kein Urteil" und
haette damit einen geloeschten Sicherungsordner verschwiegen. Jetzt
getrennt: ENOENT meldet, EACCES schweigt.

Die 26-Stunden-Grenze ist bewusst nicht enger: Kurz vor dem naechsten
Lauf ist die juengste Sicherung regulaer 24 Stunden alt. Genau dort
entstehen die Fehlalarme, nach denen man die Meldungen abschaltet --
und dann geht der eine echte mit unter. Als eigener Testfall abgesichert.

14 von 14 Pruefungen bestanden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 03:02:55 +02:00
DogFatherGitandClaude Opus 5 f8f6a26cdf Tastatur-Test prüft die Wirkung des Sprungs, nicht sein Vorhandensein
Ein Sprunglink, der den Fokus nicht mitnimmt, sieht im Quelltext
korrekt aus und ist in der Benutzung wertlos. Der Test betätigt ihn
deshalb wirklich und misst, wo der nächste Tab landet.

Beim Nachmessen auf der Live-Seite fiel auf: Der Fokus liegt sofort
nach dem Sprung korrekt auf <main>, wandert rund 300 ms später aber
auf <body>. Drei Hypothesen einzeln geprüft und alle widerlegt --
sanftes Scrollen (aus: unverändert), Animationen (aus: unverändert),
Fensterfokus im Testbrowser (document.hasFocus() bleibt true). Ein
Abfangen sämtlicher focus()- und blur()-Aufrufe ergab keinen einzigen
Aufruf aus dem Seitencode. Es ist browserinternes Verhalten und
folgenlos: die Sprungmarke für die Tab-Reihenfolge bleibt gesetzt,
nachgewiesen auf allen fünf Seiten.

Zählung korrigiert: Die erste Fassung suchte nur Links und Knöpfe und
meldete auf Formularseiten "Nr. 0 von 67, -1 Punkte gespart", weil das
Ziel dort ein Eingabefeld ist. Jetzt dieselbe Liste wie beim Zählen,
und unsichtbare Elemente fliegen raus.

25 von 25 bestanden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 01:26:35 +02:00
DogFatherGitandClaude Opus 5 efe98e32a7 Sprung zum Inhalt: Seite ohne Maus in einem Schritt bedienbar
Der Tastatur-Test (pruef-tastatur.mjs, neu) zeigte auf allen fünf
geprüften Seiten dasselbe Bild: jedes Bedienelement erreichbar, Fokus
durchgehend sichtbar, keine Tastaturfalle -- aber kein Sprunglink.

Ohne ihn muss sich jemand, der die Tastatur benutzt, auf JEDER Seite
erneut durch das komplette Menü tabben (31 bis 52 Punkte), bevor der
eigentliche Inhalt beginnt. WCAG 2.4.1 verlangt genau diesen Ausweg.

An einer Stelle gelöst statt in 35 Dateien: renderHeader() in main.js
setzt das Sprungziel und stellt den Link davor.

Das tabindex="-1" am <main> ist der Teil, der meistens fehlt: ohne ihn
verschiebt der Sprung in Chrome und Safari nur die Bildlaufleiste, der
Tastaturfokus bleibt in der Navigation -- der nächste Tab landet wieder
im Menü und der Sprung war wirkungslos.

Cache-Buster auf 20260827a (244 Stellen), sonst bekäme niemand die
geänderte main.js und main.css ausgeliefert.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 01:21:31 +02:00
DogFatherGitandClaude Opus 5 b99e3043b6 Bilder werden vor dem Hochladen aufbereitet
Das Bild "Event des Jahres" liegt als PNG mit 2524 KB auf dem Server
und macht damit zwei Drittel der gesamten Startseite aus. Angezeigt
wird es mit 246 bis 420 Punkten -- die Datei hat 1672. PNG ist fuer ein
Foto ausserdem das falsche Format.

Der Fehler passiert beim Hochladen: Ein Handy liefert Fotos in voller
Kameraaufloesung, und niemand denkt vorher ans Verkleinern. Es ist auch
nicht die Aufgabe dessen, der ein Bild aussucht -- sondern die der
Seite, die es entgegennimmt.

IM BROWSER, NICHT AUF DEM SERVER

Serverseitig braeuchte es eine Bildbibliothek mit nativem Code,
installiert in einem Verzeichnis, an das ich nicht herankomme. Der
Browser kann das ohnehin: Canvas skaliert und kodiert seit jeher.

Nebeneffekt: Schon der Upload wird kleiner. Wer vom Handy aus ein
8-MB-Foto hochlaedt, wartet sonst am Mobilfunknetz.

DREI ENTSCHEIDUNGEN

1. Kleine PNGs bleiben unangetastet. Ein Logo mit durchsichtigem
   Hintergrund wuerde als JPEG einen schwarzen Kasten bekommen. Die
   Grenze liegt bei 400 KB -- darunter ist es wahrscheinlich eine
   Grafik, darueber praktisch immer ein Foto.

2. Wird die Datei NICHT kleiner, bleibt das Original. Bei bereits gut
   komprimierten Bildern kann erneutes Kodieren sogar zulegen -- und
   Qualitaet kosten fuer nichts.

3. Schlaegt irgendetwas fehl, wird das Original hochgeladen. Ein
   misslungenes Verkleinern darf niemals einen Upload verhindern.

GEPRUEFT

Grosses PNG 3000px: 130 -> 55 KB, auf 1600px begrenzt.
Grosses JPEG 3000px: 269 -> 131 KB.
Kleines PNG: unveraendert, bleibt PNG.
Keine Skriptfehler.

Gilt fuer alle drei Upload-Stellen: Event-Bild, Team-Foto,
Stimmen-Bild.

Das vorhandene 2524-KB-PNG bleibt davon unberuehrt -- es liegt schon
auf dem Server. Ein einmaliges Neu-Hochladen ueber die Verwaltung
ersetzt es durch die aufbereitete Fassung.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 00:49:49 +02:00
DogFatherGitandClaude Opus 5 5deb920f39 Fuenf Bilder auf die tatsaechlich benoetigte Groesse gebracht
Die Startseite uebertrug 3,72 MB. Gemessen wurde, wie breit jedes Bild
tatsaechlich angezeigt wird -- in Handy- UND Desktop-Ansicht, denn die
groessere der beiden bestimmt, wie gross die Datei sein MUSS. Wer nur
am Handy misst und danach verkleinert, macht die Desktop-Ansicht
unscharf.

  casper-2.jpg            1050px -> 420px   245 KB -> 47 KB
  dogfather-casper-2.jpg  1050px -> 420px   182 KB -> 35 KB
  dogfather-portrait.jpg   933px -> 420px   122 KB -> 35 KB
  casper-1.jpg             720px -> 420px   153 KB -> 72 KB
  dogfather-casper-1.jpg   770px -> 400px   150 KB -> 63 KB

600 KB weniger. Die Zielbreiten liegen bewusst ueber dem gemessenen
Bedarf (408-411px gemessen, 420px gesetzt): Ein Bild kleiner zu machen
als noetig waere schlimmer als ein zu grosses -- Unschaerfe sieht man,
ein paar Kilobyte nicht.

Qualitaet vor dem Ersetzen angesehen, nicht nur die Zahl: Augen, Fell
und Kanten sind bei 420px unveraendert scharf.

NICHT ANGEFASST, WEIL ZU KLEIN

char-dogfather.jpg und char-hasidog.jpg haben 599px, brauchen auf
grossen Schirmen aber 954px. Sie sind also UNSCHAERFER als noetig --
das ist der umgekehrte Fall und gehoert getrennt betrachtet.

DER GROESSTE BROCKEN BLEIBT

Das Bild "Event des Jahres" ist ein PNG mit 2524 KB -- zwei Drittel der
ganzen Startseite. PNG ist fuer ein Foto das falsche Format; als JPEG
in der benoetigten Breite (840px) waeren es rund 150 KB. Die Datei
liegt unter /var/lib/dogfather-internal/uploads und ist ueber den
Verwaltungsbereich hochgeladen worden -- dort wird nicht umgewandelt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 00:31:07 +02:00
DogFatherGitandClaude Opus 5 b037867a99 Tests beenden sich jetzt sauber (process.exitCode statt process.exit)
Der Absturz auf dem Server bestand nach dem ersten Fix fort:

  node::RemoveEnvironmentCleanupHook … Assertion failed: (env) != nullptr
  Statement::~Statement() … better_sqlite3.node
  Abgebrochen

DER ERSTE VERSUCH GING AN DER URSACHE VORBEI

Ich hatte db.close() entfernt -- naheliegend, weil der Aufrufverlauf
auf einen Statement-Destruktor zeigte. Es half nicht. Die Ursache liegt
eine Ebene tiefer: process.exit() beendet Node SOFORT, waehrend
better-sqlite3 noch offene Statements haelt. Deren Aufraeumhaken laeuft
dann ins Leere.

process.exitCode setzt nur den Rueckgabewert; Node beendet sich danach
von selbst, sobald nichts mehr aussteht -- und raeumt dabei in der
richtigen Reihenfolge auf.

WARUM DAS MEHR ALS EIN SCHOENHEITSFEHLER WAR

Der Absturz kam NACH allen Pruefungen und VOR der Zusammenfassung. Der
Test meldete einen Fehler, obwohl inhaltlich alles bestanden war. In
einer mit && verketteten Befehlsfolge blieb deshalb der anschliessende
Dienst-Neustart aus, und die neuen Endpunkte antworteten weiter mit
404. Gesucht habe ich bei den Endpunkten, beim Deploy, an der
Zugangswand -- die Ursache lag beim Beenden eines Testprozesses.

BEMERKENSWERT

Fuenf Tests im Projekt benutzten process.exitCode bereits. Das Muster
war also etabliert; meine neuen Dateien wichen davon ab, ohne dass es
jemandem auffiel. Sechs Tests sind jetzt angeglichen, alle geprueft:
Rueckgabewert 0, Zusammenfassung vollstaendig.

  test-push-kette 30, test-altabbruch 16, test-webdesign-anfragen 37,
  test-webdesign-portal 49, test-webdesign-paypal 28,
  test-personendaten 15

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 00:17:21 +02:00
DogFatherGitandClaude Opus 5 76b88c6b71 Tests: kein db.close() vor process.exit()
Auf dem Server brach test-personendaten.mjs ab:

  node::RemoveEnvironmentCleanupHook … Assertion failed: (env) != nullptr
  Statement::~Statement() … better_sqlite3.node
  Abgebrochen

better-sqlite3 raeumt seine Statements ueber einen Aufraeumhaken ab.
Wird die Verbindung unmittelbar vor dem Prozessende geschlossen, laeuft
dieser Haken ins Leere.

DIE FOLGE WAR SCHLIMMER ALS DER ABSTURZ

Der Absturz kam NACH allen Pruefungen, aber VOR der Zusammenfassung.
Der Test lieferte also einen Fehlercode, obwohl inhaltlich alles
bestanden war. In der mit && verketteten Befehlsfolge blieb deshalb der
anschliessende Dienst-Neustart aus -- und die neuen Endpunkte
antworteten weiter mit 404.

Man sucht dann den Fehler bei den Endpunkten, beim Deploy, an der
Zugangswand. Die Ursache lag beim Aufraeumen einer Testdatenbank.

Node schliesst die Verbindung beim Beenden ohnehin. Zusaetzlich faengt
das Loeschen der Testdateien jetzt Fehler ab: Ohne db.close() haelt der
Prozess sie noch offen, unter Windows scheitert das Loeschen dann mit
EBUSY -- und "force" hilft dagegen nicht, es unterdrueckt nur "Datei
nicht gefunden". Ein Test, der an seinem eigenen Aufraeumen scheitert,
meldet einen Fehler, den es fachlich nicht gibt.

Beide betroffenen Tests geprueft: Rueckgabewert 0, Zusammenfassung
vollstaendig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 00:10:36 +02:00
DogFatherGitandClaude Opus 5 e6e7ab033e DEPLOY.md: die heutigen Werkzeuge und der Stand der offenen Punkte
Nach einem Tag mit vielen Aenderungen beschrieb die Anleitung weder,
was neu automatisch laeuft, noch stimmte ihre Liste offener Punkte.

NEU AUFGENOMMEN

- Zwei Zahlen beim Webdesign-Deploy: CACHE_NAME in sw.js UND die Nummer
  in der Registrierungsadresse. Mit Begruendung, warum eine allein nicht
  reicht -- Cloudflare ersetzt das "no-cache" des Servers durch vier
  Stunden, gemessen am 26.08.2026.
- Was per Cron laeuft: Sicherung (taeglich 03:15) und Waechter (alle
  fuenf Minuten), samt Probeschalter und der Grenze, die bleibt.
- Die Verwaltung als eigene App, und warum ihr Manifest in der
  Ausnahmeliste der Zugangswand stehen muss.

OFFENE PUNKTE NACHGEPRUEFT STATT ABGESCHRIEBEN

Der Eintrag "Hintergrundbilder liegen nur auf dem Server" stimmt nicht
mehr: Alle genannten Dateien und der Avatar-Ordner sind versioniert,
"git status --untracked-files=all -- assets/" meldet auf dem Server
null. Als erledigt gekennzeichnet, nicht geloescht -- eine Liste, in der
Erledigtes ungekennzeichnet steht, wird beim naechsten Mal gar nicht
mehr gelesen.

Der CORS-Punkt war schaerfer formuliert als die Lage: Die Antwort
enthaelt KEIN Access-Control-Allow-Credentials, und die Verwaltung
weist sich ueber "Authorization: Bearer" aus statt ueber ein Cookie.
Ein Browser schickt bei einer fremden Seite also weder Cookies noch das
Token mit; erreichbar sind nur die ohnehin oeffentlichen Endpunkte.
Sauberer waere eine feste Herkunftsliste -- das ist Haertung, keine
Reparatur, und gehoert als eigener Vorgang mit Live-Test angefasst.

Ergaenzt: express 5 ist vorbereitet aber bewusst nicht umgestellt, und
die Datenschutz-Endpunkte warten auf einen Pull in /home/dogiintern.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 23:48:21 +02:00
DogFatherGitandClaude Opus 5 40714b382e "Es fehlt: —." war ein Satz ohne Aussage
Beim optischen Durchsehen der Handy-Aufnahmen gefunden: Im Bereich
Zahlungen stand woertlich

    NOCH NICHT EINGERICHTET
    Es fehlt: —. Solange sagt das Kundenportal freundlich Bescheid …

Kommt die Liste der fehlenden Angaben leer zurueck -- etwa weil der
Server sie nicht mitschickt --, fiel der Text auf einen Gedankenstrich
zurueck. Man liest eine Fehlermeldung, die nicht sagt, was fehlt, und
sucht dann bei den Zugangsdaten statt bei der Antwort des Servers.

Jetzt zwei getrennte Saetze: einer, wenn bekannt ist WAS fehlt, und
einer fuer den Fall, dass nur bekannt ist DASS etwas fehlt.

Ein Rueckfallwert, der aussieht wie eine Angabe, ist schlechter als
gar keiner -- er beantwortet die Frage scheinbar und schickt einen in
die falsche Richtung.

Nebenbei geprueft und in Ordnung: Der Installationshinweis am unteren
Rand verdeckt nichts dauerhaft; er schafft sich seinen Platz ueber ein
gemessenes padding-bottom (steht seit dem 23.08.2026 so drin, damals
blockierte er den Annehmen-Knopf).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 23:43:23 +02:00
DogFatherGitandClaude Opus 5 504965e875 Push-Zustand macht der Uebersicht keinen Platz mehr streitig
Ein Gesamtdurchlauf aller Suiten nach dem heutigen Tag hat vier
Fehlschlaege in pruef-handy.mjs gezeigt -- eine Regression aus meiner
eigenen Arbeit.

DIE URSACHE

Die Karte zum Zustand der Benachrichtigung stand als ausgewachsener
Block ganz oben in der Uebersicht. Auf dem Handy schob sie die erste
Kennzahl auf 513 Punkte nach unten: Man oeffnete die Verwaltung und sah
zuerst einen Hinweis, die Arbeit erst nach dem Scrollen.

ZWEI ANLAEUFE, WEIL DER ERSTE ZU KURZ SPRANG

Zuerst wurde nur der GUTE Fall zu einer Zeile. Das half nichts -- im
Pruefbrowser sind Benachrichtigungen blockiert, also griff weiterhin
der Zweig mit der grossen Karte. Eine Aenderung, die nur den Fall
behebt, den man gerade vor Augen hat, ist keine.

Jetzt sind alle drei Zustaende eine Zeile mit Punkt und Kurztext, die
Erklaerung erst beim Aufklappen. Und der gute Zustand steht am ENDE der
Uebersicht statt oben: Eine Bestaetigung, dass nichts zu tun ist,
gehoert nicht an den Anfang. Oben bleibt nur, was Handlung verlangt.

Ergebnis: 513 -> 204 Punkte bis zum Ende des Kopfbereichs, Laenge
2635 -> 2028.

DREI RUNDUNGSFAELLE

.wd-btn--klein und zwei summary-Elemente kamen mit Rahmen und
Zeilenhoehe auf 43,x Punkte. Der Test vergleicht mit "< 44", gibt aber
gerundet "44" aus -- man sucht dann einen Fehler in einer Zahl, die
richtig aussieht. Jetzt 44.5 bzw. 46; optisch aendert das nichts.

ZWEI PRUEFUNGEN PRAEZISIERT, NICHT AUFGEWEICHT

1. "Erste Zahl im oberen Drittel" mass ab dem Seitenanfang und schlug
   damit auch bei einer BERECHTIGTEN Warnung an. Ein Test, der
   Warnungen als Mangel zaehlt, erzieht dazu, sie zu verstecken. Er
   misst jetzt den festen Kopfbereich (Titel, Suche, Reiter) -- also
   das, was man nicht wegbekommt.

2. Die Folgepruefung mass zunaechst Punkte-Abstaende. Das war fragil:
   Der Reiterstreifen klebt beim Scrollen oben fest, eine Messung
   lieferte -204. Sie zaehlt jetzt HINWEISBLOECKE statt Punkte --
   unabhaengig von Scrollposition und Schriftgroesse. Gemeint war
   ohnehin "hoechstens eine Meldung", nicht "hoechstens 140px".

56/56 in pruef-handy, alle uebrigen Suiten unveraendert gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 23:01:12 +02:00
DogFatherGitandClaude Opus 5 1c3b3808ba Express 5: geprueft, aber noch nicht umgestellt
Der Sprung 4 auf 5 entfernt Methoden, aendert die Pfadsyntax
grundlegend (path-to-regexp 8) und stellt Standardwerte um. Vieles
davon faellt nicht beim Start auf, sondern erst, wenn eine bestimmte
Adresse aufgerufen wird -- also im Betrieb, bei einem Kunden. Deshalb
zuerst pruefen statt installieren.

ZWEI PRUEFUNGEN, DIE SICH ERGAENZEN

pruef-express5.mjs durchsucht 89 Dateien nach den Bruchstellen aus dem
offiziellen Migrationsleitfaden: entfernte Aufrufe (res.send(zahl),
req.param, app.del, res.sendfile), die UMGEDREHTE Reihenfolge bei
res.redirect, geaenderte Pfadsyntax ("/*" ohne Namen, ":a?"),
schreibgeschuetztes req.query, entfernte static-Optionen.

server/test-express5.mjs startet einen echten Server mit genau unserem
Aufbau: Middleware-Kette mit eigenen Kopfzeilen, cookieParser,
express.json, eine umleitende Schranke, express.static, ein Router mit
Parameter, 404- und Fehlerbehandlung.

ERGEBNIS

15 von 15 Kategorien unbedenklich, 10 von 10 Laufpruefungen bestanden
gegen express 5.2.1. Der Umstieg waere ohne Codeaenderung moeglich.

Ein Fehlalarm lag dabei im Pruefer selbst: Er meldete drei
req.body-Zugriffe als ungesichert, die in Wahrheit innerhalb eines
"if (typeof req.body?.feld === 'boolean')" stehen -- der Rueckblick war
mit 60 Zeichen zu kurz fuer den Block. Ein Pruefer, der abgesicherte
Stellen anmahnt, kostet die Zeit, die er sparen soll, und beim
naechsten Mal glaubt man ihm auch die echten Funde nicht mehr.

NICHT UMGESTELLT

Bewusst. Der Bestand ist sicherheitstechnisch sauber (npm audit: 0),
express 4.22.2 wird weiter gepflegt, und der Nutzen waere gering
gegenueber dem Risiko, zwei laufende Dienste anzufassen. Die Vorarbeit
liegt vor -- wenn umgestellt wird, dann als eigener Vorgang mit
anschliessendem Live-Test, nicht nebenbei.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 20:19:25 +02:00
DogFatherGitandClaude Opus 5 c03793b25f Oberflaeche fuer Auskunft und Loeschung
Sitzt im Kunden-Bereich, zugeklappt. Ein eigener Reiter waere zu
prominent fuer etwas, das man vielleicht zweimal im Jahr braucht --
ganz weglassen hiesse, im Ernstfall von Hand durch ein Dutzend
Tabellen zu suchen, bei laufender Monatsfrist.

DREI SCHRITTE, IN DIESER REIHENFOLGE

  Auskunft erstellen      unschaedlich, jederzeit
  Loeschvorschau          zeigt, was betroffen waere
  Loeschen                nur nach Vorschau, mit doppelter Eingabe

Die Auskunft laesst sich als Datei herunterladen, nicht nur ansehen.
Man muss sie der Person schicken koennen, und zwar vollstaendig --
abtippen waere der sicherste Weg, etwas zu vergessen. JSON, weil
Art. 20 DSGVO ein "gaengiges, maschinenlesbares Format" verlangt.

Die Vorschau zeigt beides getrennt: was geloescht wuerde, und was
bleiben muss -- mit Grund und mit dem Datum, ab dem es weg darf. Ohne
diese Angabe muesste man bei jeder Anfrage neu nachschlagen, welche
Frist gilt.

Vor dem Loeschen wird die Adresse ein zweites Mal verlangt. Der
Unterschied zwischen .de und .com ist ein Buchstabe, die Folge
unwiederbringlich.

GEPRUEFT

Im Handy-Format mit nachgebildeten Antworten: Auskunft zeigt die
Bereiche samt Kennzeichnung "aufbewahrungspflichtig", der
Herunterladen-Knopf erscheint, die Vorschau trennt richtig, eine
abweichende Bestaetigung wird abgelehnt. Kein Ueberlauf, keine
Skriptfehler. Die Handy-Suite bleibt bei 25/25, keine Namenskollision.

Ein Fehler lag dabei im Test selbst: Playwright prueft die ZULETZT
registrierte Route zuerst, und meine allgemeine Auffangregel
ueberdeckte die spezifischen. Die Auskunft meldete "nichts
gespeichert", obwohl die Oberflaeche richtig arbeitete.

Die Endpunkte dazu liegen in server-internal und brauchen noch einen
Deploy durch Filipe (/home/dogiintern, Rechte 700). Bis dahin meldet
die Oberflaeche einen 404 -- mit dem Hinweis, dass der Server einen
Neustart mit dem neuen Stand braucht, statt nur "Fehler" zu sagen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 20:03:35 +02:00
DogFatherGitandClaude Opus 5 7b09d752f8 Auskunft und Loeschung nach DSGVO (Art. 15/17)
Schreibt jemand "Welche Daten haben Sie ueber mich?" oder "Bitte
loeschen Sie meine Daten", laeuft eine Frist von einem Monat (Art. 12
Abs. 3 DSGVO). Bisher haette man dafuer von Hand durch ein Dutzend
Tabellen suchen muessen -- und uebersieht man eine, ist die Auskunft
unvollstaendig, ohne dass man es ihr ansieht.

⚠️ LOESCHEN IST NICHT EINFACH LOESCHEN

Der naheliegende Weg waere ein "Alles loeschen". Das waere bequem und
doppelt falsch: Rechnungen und Buchungsbelege unterliegen einer
gesetzlichen Aufbewahrungspflicht -- § 147 Abs. 3 AO nennt acht Jahre
fuer Buchungsbelege, zehn fuer Handelsbuecher, gerechnet ab dem Ende
des Kalenderjahres (Abs. 4). Wer sie auf Zuruf loescht, verstoesst
gegen Steuerrecht, um Datenschutzrecht zu erfuellen.

Die DSGVO nimmt diesen Fall selbst aus (Art. 17 Abs. 3 lit. b). Fuer
das, was bleiben muss, sieht sie die Einschraenkung der Verarbeitung
vor (Art. 18) -- genau so wird es ausgewiesen, samt Datum, ab dem
geloescht werden darf.

Dasselbe bei Widerrufserklaerungen: Sie sind der Nachweis, DASS und
WANN widerrufen wurde. Wer sie loescht, vernichtet seinen eigenen Beleg
in genau der Sache, in der es spaeter Streit geben koennte.

EIN FEHLER, DEN DER TEST VOR DEM ERSTEN LAUF GEFUNDEN HAT

Die Tabelle wd_widerrufe fuehrt die Adresse nicht als "email", sondern
als "kontakt". Die erste Fassung suchte nach "email" -- sie haette dort
NIE einen Treffer geliefert, und die Auskunft haette trotzdem sauber
ausgesehen. Aufgefallen nur, weil die Testdaten gegen das echte Schema
angelegt wurden statt gegen die Annahme.

Deshalb steht in der Quellenliste jetzt die Spalte, nicht eine
Vermutung.

WEITERE ENTSCHEIDUNGEN

- Die Suche geht ueber die Adresse UND ueber die Kundenkennung. Vieles
  haengt nicht an der Adresse; ohne diesen Schritt bliebe die halbe
  Auskunft leer.
- Gross- und Kleinschreibung spielt keine Rolle -- sonst bekaeme
  jemand eine leere Auskunft, weil er seine Adresse anders schreibt.
- Die Loeschung verlangt die Adresse ein zweites Mal. Der Unterschied
  zwischen .org und .com ist ein Buchstabe, die Folge unwiederbringlich.
- Sie laeuft in einer Transaktion: Bricht sie ab, bliebe sonst ein
  halbgeloeschter Bestand, und niemand wuesste, welcher Teil weg ist.
- Der Vorgang wird protokolliert, aber OHNE die geloeschten Inhalte --
  sie dabei erneut zu speichern waere das Gegenteil des Zwecks.

15 Pruefungen gruen, mit Gegenprobe (fremde Adresse liefert nichts).

Die Oberflaeche in der Verwaltung folgt. Deploy braucht Filipe:
server-internal liegt unter /home/dogiintern mit Rechten 700.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:55:05 +02:00
DogFatherGitandClaude Opus 5 ac1d5c9916 Waechter: Rechte wurden gesetzt, aber wirkten nicht
Nachgemessen: /var/lib/dogfather-waechter stand auf 755 -- weltweit
lesbar, mit einer vollstaendigen Liste aller Dienste, Adressen und
ihres Zustands darin. Also genau das, was der Commit von heute
Nachmittag verhindern sollte.

Die Absicht stand im Code, die Wirkung fehlte. Zwei Gruende, beide
still:

1. "mode" bei mkdirSync gilt nur, wenn das Verzeichnis dabei NEU
   entsteht. Beim zweiten Lauf existiert es immer -- dann laesst
   mkdirSync die Rechte unberuehrt. Hier war es sogar schon vor dem
   Einbau der Zeile angelegt worden.
2. Selbst beim Neuanlegen zieht die umask des Prozesses Bits ab.

Der Unterschied zur Sicherung ist lehrreich: /var/backups/dogfather
steht korrekt auf 700, weil dort ein ausdrueckliches chmod im Skript
steht. Dieselbe Ueberlegung, einmal umgesetzt und einmal nur gemeint.

Jetzt chmod bei jedem Lauf, fuer Verzeichnis UND Dateien.

Aufgefallen ist es nur, weil die Rechte nachgesehen wurden statt
angenommen -- der Code sah richtig aus.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:37:45 +02:00
DogFatherGitandClaude Opus 5 6077c1700f server-internal: package-lock.json aufgenommen
Nach der Installation auf dem Server vom dortigen Stand uebernommen --
nicht hier erzeugt. Das ist der entscheidende Unterschied: Eine lokal
erzeugte Datei haette festgeschrieben, was HIER aufgeloest wird, nicht
was dort tatsaechlich laeuft. Genau diese Luecke sollte sie ja
schliessen.

Festgeschrieben sind jetzt:

  multer          2.2.0   (war 1.4.5-lts.1, veraltet)
  node-cron       4.6.0   (war 3.0.3)
  express         4.22.2  (package.json sagt ^4.21.2)
  better-sqlite3  11.10.0
  dotenv          16.6.1
  cors            2.8.6

express 4.22.2 zeigt, warum das noetig war: Die package.json erlaubt
alles unter 5.0, installiert war eine andere Fassung als die genannte.

uuid taucht in der Liste nicht mehr auf. Es kam ausschliesslich ueber
node-cron 3.x herein und war die einzige Luecke, die "npm audit"
gefunden hatte. Gegenprobe nach der Umstellung: "found 0
vulnerabilities". Auch die Veraltet-Markierung von multer ist weg.

Der Dienst laeuft stabil (PID unveraendert ueber mehrere Messungen,
keine zusaetzlichen Neustarts) und antwortet mit 401 -- er lebt also
und prueft Rechte.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:35:59 +02:00
DogFatherGitandClaude Opus 5 a2487212c8 multer 2.x und node-cron 4.x: geprueft, package.json angehoben
multer 1.4.5-lts.1 ist von den Betreuern ausdruecklich als veraltet
markiert. Die Meldung der Registry, woertlich:

  "Multer 1.x is impacted by a number of vulnerabilities, which have
   been patched in 2.x. You should upgrade to the latest 2.x version."

⚠️ BEMERKENSWERT: "npm audit" meldet dazu NICHTS. Die Pruefung nannte
nur eine Luecke in uuid, eingeschleppt ueber node-cron 3.x. Wer sich
allein auf audit verlaesst, haette multer fuer unbedenklich gehalten.
Die Deprecation-Meldung ist hier die eigentliche Warnung -- sie steht
aber an einer Stelle, an die man nur kommt, wenn man gezielt nachfragt.

2.0.0 behebt CVE-2025-47935 und CVE-2025-47944. Einzige dokumentierte
Breaking Change: Node ab 10.16. Auf dem Server laeuft 24.

node-cron 4.x behebt die uuid-Luecke. Die Fassung ist eine Umstellung
auf TypeScript, ohne dokumentierte API-Aenderung -- genau dabei aendert
sich aber gern die Art des Standard-Exports, und "import cron from
'node-cron'" wuerde danach beim START scheitern, nicht bei der
Installation.

GEPRUEFT STATT ANGENOMMEN

In einem eigenen Verzeichnis gegen multer 2.2.0 und node-cron 4.6.0
getestet, mit genau den Aufrufen aus index.js: diskStorage mit
destination/filename, limits, fileFilter, single/array/fields,
MulterError samt code, cron.schedule("* * * * *") und task.stop().
15 von 15 bestanden.

Die Pruefung liegt jetzt als server-internal/test-pakete.mjs im Projekt
-- die Frage "laeuft unser Code damit noch?" stellt sich bei jedem
Hauptversionswechsel neu.

Express bleibt bewusst bei 4.x. Der Sprung auf 5 ist ein eigener
Vorgang mit deutlich mehr Flaeche; ihn hier mitzunehmen wuerde zwei
unabhaengige Risiken in einem Schritt buendeln.

Die Installation selbst braucht Filipe: server-internal liegt unter
/home/dogiintern mit Rechten 700.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:30:58 +02:00
DogFatherGitandClaude Opus 5 f64403aea8 package-lock.json wird versioniert (server/)
Ohne Lock-Datei darf jede Installation andere Fassungen ziehen:
"^4.21.2" erlaubt alles unter 5.0. Auf dem Server laeuft deshalb
express 4.22.2, waehrend in der package.json 4.21.2 steht -- geprueft
wird also nie genau das, was ausgeliefert wird. Solche Unterschiede
fallen nicht beim Deploy auf, sondern im Betrieb, und dann sucht man
den Fehler im eigenen Code.

WARUM SIE AUSGESCHLOSSEN WAR

Ein "git pull" auf dem Server scheiterte daran: Git ueberschreibt keine
unverfolgte Datei -- unabhaengig davon, ob ihr Inhalt derselbe ist. Der
Ausschluss hat den Deploy repariert und dabei den Zweck der Datei
beseitigt.

Nachgemessen statt vermutet: Die Datei hier und die auf dem Server sind
Byte fuer Byte identisch (SHA-256 a50b028d…). Es gab also nie einen
inhaltlichen Konflikt, nur einen formalen. Er loest sich, indem die
Datei einmal vom Server entfernt und danach aus dem Repo geholt wird.

DEPLOY.md ergaenzt: auf dem Server "npm ci" statt "npm install". ci
loescht node_modules vorher und baut streng nach der Lock-Datei; es
schreibt sie nie um und bricht ab, wenn sie nicht zur package.json
passt -- statt still etwas anderes zu installieren.

server-internal/ folgt, sobald die dortige Lock-Datei vorliegt. Dieses
Verzeichnis liegt unter /home/dogiintern mit Rechten 700.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:26:40 +02:00
DogFatherGitandClaude Opus 5 ea11cefe17 Verwaltung auf dem Handy: vier Fehler behoben
Geprueft mit nachgebildeten Daten -- lange Firmennamen, lange
E-Mail-Adressen, mehrstellige Betraege. Eine leere Seite laeuft nie
ueber; die Fehler, um die es geht, entstehen erst mit Inhalt.

1. DAS RASTER DER KOPFZEILE WAR BREITER ALS SEIN BEHAELTER

Auf 390px summierten sich die beiden Spalten auf 372,7px, waehrend die
Leiste nur 358px breit ist. Rasterspalten schrumpfen von sich aus nicht
unter ihren Inhalt ("min-width: auto"). Alles darin schob sich nach
rechts hinaus: die Kopfzeile 8px, der Reiterstreifen 12px.

Sichtbar war das kaum -- der Streifen ist ohnehin wischbar, rechts
fehlte nur ein Stueck Rand. Genau deshalb blieb es liegen: ein Fehler,
der sich als Eigenart tarnt. Jetzt minmax(0, …) plus min-width: 0 an
den Kindern; lange Titel kuerzen mit Auslassungspunkten, statt zu
schieben.

2. DER INSTALLATIONSHINWEIS ZEIGTE DAS FALSCHE SYMBOL

Seit die Verwaltung eine eigene App ist, stand dort weiter der schwarze
Husky. Man las "Als App installieren" neben dem einen Bild und bekam
das andere auf den Startbildschirm. Das Symbol wird jetzt aus dem
Manifest abgeleitet, das die Seite tatsaechlich einbindet -- damit
stimmt es auch fuer jede kuenftige App, ohne dass jemand daran denken
muss.

3. DIE UEBERSICHT BRACH BEI UNVOLLSTAENDIGER ANTWORT AB

d.projekte und d.verlauf wurden ungeprueft mit .length angefasst,
waehrend drei andere Felder ausdruecklich geprueft wurden. Fehlte eine
der Listen, brach die Uebersicht mit "Cannot read properties of
undefined" ab -- und zwar NACH dem Aufbau der oberen Kacheln: Die halbe
Seite stand da, der Rest fehlte kommentarlos. Genau der Fall, den der
Kommentar daneben als "realistisch" beschreibt. Jetzt wird aufgefuellt
statt abgebrochen.

4. ZWEI FEHLALARME IM TEST SELBST

Der Test meldete den Reiterstreifen als Ueberlauf (er ist ein
Wischstreifen -- dass dort etwas ausserhalb liegt, ist sein Sinn) und
eine 1x1-Checkbox als zu kleines Tippziel (bedient wird sie ueber ihr
Label, 315x162 Punkte). Beides wuerde dazu verleiten, Funktionierendes
"zu reparieren". Der Test unterscheidet das jetzt.

Ebenso meldete er "Strg" und "Enter" mit 10,4px -- beide sind auf
schmalen Schirmen laengst ausgeblendet. getComputedStyle liefert auch
fuer verborgene Elemente eine Schriftgroesse; jetzt zaehlt nur
Sichtbares.

25 Pruefungen ueber sechs Bereiche gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:09:33 +02:00
DogFatherGitandClaude Opus 5 62cbd724e2 Manifest der Verwaltungs-App an der Zugangswand freigeben
Beim ersten Live-Test war die App nicht installierbar: Symbole 200,
Manifest 302.

Die Zugangswand hat eine Ausnahmeliste fuer App-Bausteine -- sw.js und
app.webmanifest stehen ausdruecklich darin, mit einer ausfuehrlichen
Begruendung aus dem Universe. Das neue Manifest fehlte schlicht.

Der Grund gilt hier sogar staerker als bei der oeffentlichen App: Die
Verwaltung liegt IMMER hinter der Schranke. Ohne Freigabe bekommt der
Browser statt des Manifests eine Umleitung auf zugang.html, also HTML
statt JSON -- und bietet "App installieren" gar nicht erst an.

Unbedenklich: Das Manifest enthaelt Name, Farben, Symbolpfade und drei
Verknuepfungen. Keine Kunden-, Projekt- oder Preisdaten. Wer es liest,
erfaehrt, dass es eine Verwaltung gibt -- was die Zugangswand ohnehin
verraet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 16:13:40 +02:00
DogFatherGitandClaude Opus 5 1608521854 Verwaltung ist jetzt eine eigene App
Wunsch: "ich will die verwaltungsseite auch getrennt runter laden
koennen und installieren koenne."

Bisher gab es ein Manifest fuer den ganzen Webdesign-Bereich; die
Verwaltung war darin nur eine Verknuepfung. Jetzt hat sie ein eigenes
mit eigener "id" -- der Wert, an dem alles haengt: Ohne ihn halten
Browser beide fuer dieselbe App, und die zweite Installation
ueberschreibt die erste, statt danebenzustehen.

EIGENES SYMBOL

Zwei gleich aussehende Kacheln auf dem Startbildschirm waeren keine
Trennung. Das neue Symbol ist ein Ausschnitt des Eiskristall-Wappens --
dasselbe Motiv, das die Verwaltung ohnehin traegt, und klar
unterscheidbar vom schwarzen Husky der oeffentlichen App. Erzeugt aus
cryo-wappen.webp in drei Groessen, die beschneidbare Fassung weiter
herausgezoomt, damit Android die Spitzen des Wappens nicht abschneidet.

Als JPEG statt PNG: 76 statt 385 KB bei 512 Punkten. Bei einem
fotografischen Motiv hat PNG nichts zu gewinnen, und 385 KB fuer ein
Symbol waeren unverhaeltnismaessig.

⚠️ DER GELTUNGSBEREICH IST ABSICHTLICH WEIT

Naheliegend waere "/webdesign/verwaltung" gewesen. Das haette die App
unbrauchbar gemacht: Ohne gueltigen Ausweis leitet der Server auf
/webdesign/zugang.html um -- ausserhalb des Bereichs, und was
ausserhalb liegt, oeffnet der Browser in einem eigenen Fenster. Weil
der Zugang beim Schliessen endet, waere das bei fast jedem Start
passiert: Man tippt auf die App und landet im Browser. Nachgemessen:
verwaltung.html antwortet ohne Ausweis mit 302.

EIN STILLES VERSPRECHEN EINGELOEST

Die Verknuepfungen zeigen auf "?bereich=anfragen" und dergleichen --
und derselbe Parameter steht in den Push-Meldungen. Ausgewertet hat ihn
bisher NIEMAND. Man landete immer auf der Uebersicht, ohne dass etwas
kaputt aussah. Jetzt oeffnet die Seite den gewuenschten Reiter, ueber
einen ausgeloesten Klick auf den vorhandenen Reiter statt ueber
nachgebaute Logik: Daran haengen Signatur, Leuchtbalken, Nachladen und
der Wischstreifen auf dem Handy.

Auch hier hat erst der Test den Fehler gezeigt -- und danach einen in
der Pruefung selbst: Sie erreichte die Funktion gar nicht, weil diese
im gekapselten Bereich liegt. Jetzt nach aussen gegeben, wie schon
window.vwBalkenSetzen.

23 Pruefungen gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 16:12:35 +02:00
DogFatherGitandClaude Opus 5 92d2b568de Pruefung auf doppelte Funktionsnamen — und ein zweiter Fund
Die Kollision von heute war weder fuer den Browser noch fuer einen
Syntaxpruefer sichtbar: Zwei Funktionen desselben Namens sind erlaubtes
JavaScript, die spaetere gewinnt lautlos. Auch die
Oberflaechen-Tests schwiegen -- sie pruefen, ob Kacheln DA sind, nicht
ob sinnvoller Text darin steht.

pruef-namenskollision.mjs durchsucht jetzt alle 110 Dateien mit eigenem
JavaScript. Mit Gegenprobe, damit die Pruefung nicht selbst kaputtgehen
und dabei "sauber" melden kann.

ZWEITER FUND, AELTER ALS MEIN FEHLER

Sie meldete sofort eine weitere Kollision: tageSeit stand zweimal in
verwaltung.html. Beide rechneten dasselbe, mit einem Unterschied -- bei
fehlendem Datum gab die eine null zurueck, die andere 0.

Die spaetere (mit 0) gewann. Damit war die frueher definierte
wirkungslos, und mit ihr die Pruefungen "if (t === null) return ''" in
altersText und dringlichkeit: Ohne Datum stand dort "seit heute" statt
gar nichts.

Entfernt wurde die spaetere. Die beiden verbliebenen Aufrufer vertragen
null genauso wie 0 -- nachgeprueft, nicht angenommen: Math.max(x, null)
ergibt x, und null >= 7 ist falsch, gleich wie bei 0.

110 Dateien jetzt sauber.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:58:12 +02:00
DogFatherGitandClaude Opus 5 0e8c1447f2 DRINGEND: Namenskollision zerstoerte die Uebersicht
Alle Kacheln der Uebersicht zeigten "[object Object]" und "undefined".

Ursache war mein Push-Code von heute. Er brachte eine Hilfsfunktion
namens kachel(titel, inhalt, art) mit -- und weiter oben in derselben
Datei gibt es seit langem kachel(o), die die Uebersichtskacheln baut.

JavaScript kennt keine Ueberladung. Steht spaeter eine zweite Funktion
desselben Namens im selben Gueltigkeitsbereich, gewinnt sie. Ohne
Warnung, ohne Fehlermeldung, ohne dass irgendein Werkzeug anschlaegt.
Danach bekam jede Uebersichtskachel mein Objekt als ersten Parameter
und gab es als Zahl aus.

Besonders tueckisch: Die Push-Karte selbst funktionierte einwandfrei --
sie steht ja unmittelbar ueber den kaputten Kacheln. Der sichtbare
Schaden lag weit weg von seiner Ursache, und nichts deutete auf den
neuen Code hin.

Die Funktion heisst jetzt pushKachel und sagt damit, wozu sie gehoert.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:55:31 +02:00
DogFatherGitandClaude Opus 5 77b395e2b4 Test: laesst den Service Worker jetzt wirklich arbeiten
Die Luecke, durch die der Fehler von heute bis zum Benutzer
durchgerutscht ist.

Der Test hat 48 Seiten in einem echten Browser geoeffnet und nichts
bemerkt -- weil er den Service Worker nie hat arbeiten lassen. Er war
gruen und wertlos zugleich: Der entscheidende Weg wurde nicht
begangen.

Neu geprueft wird jetzt:
  - der Service Worker meldet sich an und wird aktiv
  - er darf tatsaechlich etwas abrufen (das war der kaputte Punkt)
  - dabei entsteht kein Richtlinien-Verstoss
  - sw.js traegt selbst KEINE Richtlinie, denn sie wuerde zu SEINER

Ausserdem umgedreht: Ein Test verlangte fuer Nicht-Seiten ausdruecklich
"default-src 'none'" -- und sicherte damit genau den Fehler ab, der die
App lahmlegte. Er haette den naechsten Versuch, es richtig zu machen,
als Fehler gemeldet. Jetzt wird geprueft, dass CSS, JavaScript, Bilder
und der Service Worker KEINE Richtlinie bekommen.

22 Pruefungen gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:47:10 +02:00
DogFatherGitandClaude Opus 5 0f253df134 Service Worker: Versionsnummer in der Adresse gegen fremden Cache
Bei der Fehlersuche zur lahmgelegten App kam ein zweiter, aelterer
Mangel zum Vorschein.

GEMESSEN

  Dienst selbst    Cache-Control: no-cache
  nach Cloudflare  Cache-Control: max-age=14400  (auch bei MISS)

Cloudflare ersetzt die Vorgabe des Servers durch vier Stunden. Ursache
ist eine feste "Browser Cache TTL" in den Einstellungen. Der Kommentar
in server/index.js behauptet das Gegenteil ("Cloudflare respektiert
laut Doku ein vom Origin gesetztes Cache-Control") -- fuer diese Domain
stimmt das nicht. Nachgemessen, nicht vermutet.

WARUM DAS MEHR ALS EIN SCHOENHEITSFEHLER IST

Jede Aenderung am Service Worker erreicht die Geraete bis zu vier
Stunden zu spaet. Solange alles laeuft, faellt das nicht auf. Ist der
ausgelieferte Stand aber fehlerhaft -- wie heute, als eine zu strenge
Inhaltsrichtlinie ihn lahmlegte und die App "Keine Verbindung" zeigte
--, sind es vier Stunden, in denen sich nichts reparieren laesst.
Genau dann, wenn Tempo zaehlt, ist man am langsamsten.

LOESUNG OHNE FREMDE EINSTELLUNGEN

Die Registrierung laedt jetzt "/webdesign/sw.js?v=56". Aendert sich die
Nummer, ist es eine andere Adresse -- dafuer kann kein Zwischenspeicher
einen alten Stand haben. Die Nummer wird zusammen mit CACHE_NAME
hochgezaehlt; beide gehoeren zusammen und stehen jetzt auf 56.

Das wirkt unabhaengig davon, wie Cloudflare eingestellt ist. Die
Einstellung selbst sollte trotzdem geprueft werden -- sie betrifft auch
CSS und JavaScript.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:43:45 +02:00
DogFatherGitandClaude Opus 5 c591925342 DRINGEND: Inhaltsrichtlinie legte die App lahm
Symptom: Die installierte App zeigte "Keine Verbindung", obwohl die
Seite online war, der Server lief und jede Pruefung Erfolg meldete.

Ursache war meine eigene Zeile von heute Vormittag. Fuer alles, was
keine Seite ist, setzte die Middleware "default-src 'none'" -- mit dem
Gedanken "kostet nichts und schadet nie". Der zweite Halbsatz war
falsch.

Ein Service Worker uebernimmt die Inhaltsrichtlinie, die beim
Herunterladen SEINER EIGENEN Skriptdatei gesetzt war, nicht die der
Seite, fuer die er arbeitet. sw.js ist keine Seite, bekam also 'none'
und durfte damit nichts mehr abrufen. Jede Anfrage scheiterte -- und
weil der Service Worker fuer genau diesen Fall eine Offline-Seite
bereithaelt, sah es aus wie ein Netzausfall beim Benutzer.

Fuer Bilder, Stylesheets und Schriften bringt eine Richtlinie ohnehin
nichts: Sie steuert, was ein DOKUMENT nachladen darf. Ein Bild laedt
nichts nach. Dem Schaden stand also nie ein Gewinn gegenueber.

Jetzt: Richtlinie nur noch fuer HTML-Dokumente.

Warum der Test das nicht gefunden hat, folgt gleich -- er hat die
Seiten geladen, aber nie den Service Worker arbeiten lassen. Genau die
Luecke, durch die es durchgerutscht ist.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:41:27 +02:00
DogFatherGitandClaude Opus 5 b2f4567cce Waechter: leere Geraeteliste und fehlende Tabelle unterscheiden
Der erste echte Probelauf meldete "kein Geraet angemeldet". Richtig --
aber dieselbe Meldung waere auch erschienen, wenn die Tabelle gar nicht
existierte, weil dbLesen in beiden Faellen "" zurueckgibt.

Das sind zwei voellig verschiedene Lagen: Einmal genuegt ein Klick in
der Verwaltung, einmal ist die Migration nicht gelaufen. Wer die falsche
Meldung liest, drueckt auf Einschalten, sieht keine Wirkung und sucht
dann beim Browser statt bei der Datenbank.

Genau die Sorte Verwechslung, gegen die dieser ganze Strang gebaut
wurde. Jetzt wird zuerst gefragt, ob die Tabelle da ist, und die Meldung
sagt im harmlosen Fall auch gleich, was zu tun ist.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:38:11 +02:00
DogFatherGitandClaude Opus 5 bcf6e01fa9 Waechter: engere Dateirechte und ein Probeschalter
Der erste automatische Lauf war erfolgreich (14:20, 18 Punkte, 0
auffaellig). Zwei Nachtraege:

RECHTE

Zustandsdatei und Protokoll lagen mit 644 in einem 755-Verzeichnis.
Personenbezogene Daten stehen dort keine -- wohl aber eine
vollstaendige Liste aller Dienste, Adressen und ihres Zustands. Fuer
jemanden, der einen Angriff vorbereitet, ist das eine bequeme
Landkarte. Jetzt 700/600. Kostet nichts, also gibt es auch keinen
Grund, es herzugeben.

PROBESCHALTER

  node waechter.mjs --probe

Verschickt eine Meldung, ohne dass etwas kaputt sein muss. Das ist die
einzige Moeglichkeit, den MELDEWEG zu pruefen, ohne auf eine echte
Stoerung zu warten.

Der Grund ist derselbe wie beim stillen "if (!url) return;", das diese
Reihe ausgeloest hat: Eine Ueberwachung, deren Zustellung
stillschweigend nicht funktioniert, ist schlimmer als gar keine. Man
haelt die Stille dann fuer "alles in Ordnung" -- dabei ist sie nur
Stille.

Steht bewusst VOR den Messungen: Wer den Meldeweg pruefen will, soll
nicht erst 18 Punkte abfragen muessen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:21:08 +02:00
DogFatherGitandClaude Opus 5 3a5805bfad Waechter: meldet Ausfaelle, statt sie unbemerkt zu lassen
Der Befund war besser als der Aufgabentitel: Alle Dienste haben
Restart=always und stehen nach einem Absturz von selbst wieder auf. Die
Luecke liegt woanders:

  - Dauerschleife: Startet ein Dienst und stuerzt sofort wieder ab, gibt
    systemd nach wenigen Versuchen auf. Dann bleibt er unten.
  - "active" heisst nicht "antwortet". Ein haengender Dienst gilt
    systemd als gesund.
  - Zertifikatsablauf und volle Platte machen keinen Dienst inaktiv,
    legen aber alles lahm.

Geprueft werden 18 Punkte: sechs Dienste, neun Adressen, Plattenplatz,
zwei Zertifikatslaufzeiten.

WARUM ALS ROOT PER CRON

Ein Waechter, der ueber den internen Dienst meldet, hat einen
Konstruktionsfehler mit Ansage: Ausgerechnet wenn DIESER Dienst das
Problem ist, kaeme keine Meldung durch. Also liest er .env und Datenbank
selbst und verschickt selbst -- unabhaengig davon, ob noch irgendetwas
laeuft. Ohne Fremdpakete, nur Node-Bordmittel, sqlite3 und systemctl.

NUR BEI ZUSTANDSWECHSEL

Gemeldet wird, wenn etwas kippt -- in beide Richtungen. Nicht alle fuenf
Minuten dasselbe. Wer staendig Meldungen bekommt, sieht irgendwann keine
mehr an und uebersieht die eine, auf die es ankam.

ZWEI EIGENE FEHLER, DIE DER TEST GEFUNDEN HAT

1. Zuerst stand je Adresse eine handgepflegte Liste erlaubter
   Antwortcodes. Der erste Lauf meldete VanVans Shop als ausgefallen --
   er war es nicht, er steht ebenfalls hinter einer Zugangswand und
   antwortet mit 302. Ich war damit genau in die Falle gelaufen, vor der
   der Kommentar an derselben Stelle warnte.

2. Danach galt "unter 400" als heil. Jetzt meldete das Postfach einen
   Ausfall, weil die geprueften Adresse 404 lieferte -- der Dienst lief
   einwandfrei, ich hatte die Adresse falsch gewaehlt.

Beide Male dieselbe Lehre: Eine Ueberwachung, die bei einer falsch
getippten Adresse "Ausfall" ruft, erzieht einen dazu, ihre Meldungen zu
ignorieren. Die Regel lautet jetzt "unter 500", denn der Waechter fragt
"lebt der Dienst?", nicht "ist der Inhalt richtig?". 401, 403 und 404
BEWEISEN, dass jemand da ist und zuhoert. Nur 5xx und Schweigen heissen,
dass dahinter nichts mehr laeuft. Ausnahme sind die beiden Pflichtseiten
Impressum und Widerruf -- dort ist alles ausser 200 bereits ein Mangel.

GEPRUEFT AM SERVER

Ausfall eingebaut: erkannt und gemeldet. Ausfall dauert an: still, keine
Wiederholung. Wieder erreichbar: Entwarnung. 18 Punkte, 0 Fehlalarme
ueber mehrere Laeufe.

⚠️ GRENZE, DIE BLEIBT

Ist der Server als Ganzes weg -- Netz, Strom, Hardware --, meldet auch
dieser Waechter nichts. Dagegen hilft nur eine Ueberwachung ausserhalb
der Maschine. Steht so im Kopf der Datei, damit sich niemand in falscher
Sicherheit wiegt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:17:12 +02:00
DogFatherGitandClaude Opus 5 03a533f143 Inhaltsrichtlinie (CSP): mit Pruefsummen statt mit unsafe-inline
Fuenf Schutz-Kopfzeilen waren gesetzt, die wichtigste fehlte. Sie
entscheidet als einzige darueber, ob eingeschleuster Text zu
ausgefuehrtem Code wird oder sichtbarer Text bleibt.

WARUM NICHT DER BEQUEME WEG

Ueblich waere script-src 'self' 'unsafe-inline'. Eine Zeile, nichts geht
kaputt -- und der Schutz ist weg: Der Browser kann nicht unterscheiden,
ob ein Skript im Seitentext vom Entwickler stammt oder von einem
Angreifer. Das Ergebnis ist eine Kopfzeile, die gut aussieht und im
Ernstfall nichts tut.

Der Bestand liess den sauberen Weg zu: keine fremden Skriptquellen,
keine externen Schriften, ein Inline-Block je Seite. Von jedem Block
wird die Pruefsumme gebildet.

DIE PRUEFSUMMEN STEHEN BEWUSST NICHT IM CODE

Das waere hier eine Falle mit Ansage: Statische Dateien gehen per "git
pull" live, OHNE Neustart. Eine fest hinterlegte Summe waere nach der
naechsten Textaenderung falsch -- und die Seite wuerde ihr eigenes
Skript nicht mehr ausfuehren. Sichtbar erst im Browser des Besuchers,
nicht beim Deploy, und aussehend wie kaputtes JavaScript.

Deshalb liest die Middleware die Datei selbst und merkt sich das
Ergebnis, solange die Aenderungszeit gleich bleibt. Ein Test aendert
index.html im laufenden Betrieb und prueft, dass die Summe nachzieht
und die Seite weiterlaeuft.

WAS DER TEST GEFUNDEN HAT

Die erste Fassung haette die Startseite und stimmen.html beschaedigt:
Team-Fotos, Event des Jahres und die Stimmen kommen von der
postfach-Subdomain, img-src erlaubte nur 'self'. Der Deploy haette
Erfolg gemeldet, der Server waere gestartet -- und die Bilder waeren
weg gewesen. Gefunden, weil der Test alle 48 Seiten in einem echten
Browser oeffnet und mitschreibt, was blockiert wird.

Ein zweiter Fehlschlag lag am Test selbst: Er verlangte eine Pruefsumme
auf jeder Seite, auch auf denen ohne Inline-Block. Ein Test, der
Unmoegliches fordert, wird frueher oder spaeter abgeschaltet -- er
unterscheidet jetzt nach dem tatsaechlichen Inhalt der Datei.

DREI onclick-ATTRIBUTE ENTFERNT

Sie haetten 'unsafe-inline' erzwungen. Zweimal ein "Coming soon"-Knopf,
dessen onclick nur Klicks abfing -- ein <a> ohne href tut das von
selbst, ganz ohne Skript. Einmal ein Schliessen-Knopf im
Verwaltungsbereich, jetzt mit angehaengtem Zuhoerer.

GEGENPROBE

Ohne sie waere der Rest wertlos: Eine Richtlinie, die alles erlaubt,
blockiert auch nichts und besteht jede Pruefung. Der Test schleust
deshalb echten Code ein -- ein Inline-Skript und eines von fremder
Adresse -- und beide muessen scheitern.

style-src behaelt 'unsafe-inline': 587 style-Attribute im Bestand, deren
Umbau ein echtes Risiko fuers Aussehen waere, bei kleinem Gewinn. Ueber
Stile laesst sich verschleiern und ueberdecken, aber kein Code
ausfuehren. Bleibt als eigener Punkt auf der Liste.

15 Pruefungen gruen, 48 Seiten sauber, Portfolio-Suite unveraendert 35.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:05:42 +02:00
DogFatherGitandClaude Opus 5 094a69732b Portfolio: Spicy SafeAddress als sechstes Projekt
Die Rechtsplattform der Spicy Media Agency fehlte -- dabei ist sie das
technisch anspruchsvollste Stueck der Sammlung.

WAS IM TEXT STEHT UND WARUM SO

Die Projektdokumentation haelt ausdruecklich fest, dass die Wendung
"100 Prozent rechtssicher" dort nirgends verwendet wird. Ein Portfolio,
das mehr verspricht als die Sache selbst, waere genau der Fehler, den
das Projekt vermeiden will. Im Ergebnis steht deshalb, dass die
Plattform fertig gebaut und bewusst noch vollstaendig gesperrt ist und
auf die schriftliche Freigabe einer Fachkanzlei wartet -- nicht, dass
sie "im Einsatz" sei.

KEIN LIVE-KNOPF

Wie bei der Buchhaltung. Ein "Live ansehen", das auf eine Zugangswand
fuehrt, bricht das Versprechen, das der Knopf gibt. Stattdessen steht
dort ein Satz, der den Grund nennt.

GESICHTER UNKENNTLICH

Auf der Zugangswand stehen zwei Profilfotos. Eines davon gehoert einer
Geschaeftspartnerin, die dieser Veroeffentlichung nicht zugestimmt hat.
Beide sind in der Aufnahme weichgezeichnet, das Firmenzeichen bleibt
scharf. Begruendet im alt-Text und in einem eigenen Abschnitt -- genau
wie bei Projekt 5, wo derselbe Fall schon einmal auftrat.

Nebenbei: Die strenge Inhaltsrichtlinie der Seite (style-src 'self')
hat das eingefuegte Stylesheet fuers Weichzeichnen blockiert. Sie tut
also genau das, wofuer sie da ist. Ueber die Stil-Eigenschaft im Skript
ging es dann.

ZWEI STELLEN, DIE SONST FALSCH GEBLIEBEN WAEREN

Die Ueberschrift hiess "Fuenf Projekte, die man anfassen kann". Mit
SafeAddress steht dort erstmals ein Projekt, das man NICHT besuchen
kann -- die Ueberschrift haette also gleich doppelt daneben gelegen.
Jetzt: "Sechs Projekte, die es wirklich gibt", und die Einleitung sagt
ausdruecklich, dass eine der Seiten gesperrt ist.

Der Schlussaufruf lud dazu ein, "das sechste" Projekt zu werden. Bei
sechs vorhandenen waere das das Angebot gewesen, eines davon zu
ersetzen. Jetzt das siebte.

Beides in allen fuenf Sprachen, de-CH als echter Dialekt wie im Rest
dieser Datei.

TEST

Zwei Fehlschlaege, beide berechtigt: die Projektzahl und ein zu grobes
Suchmuster, das bei "zwei MENSCHEN" ansprang, obwohl es die veraltete
Aussage "zwei PROJEKTE" sucht. Das Muster laesst jetzt hoechstens ein
Wort zwischen Zahlwort und Substantiv zu. Mit Gegenprobe abgesichert:
faengt weiterhin "Two projects you can visit", ignoriert "Two people
work together on that site". Ein Fehlalarm, den man nur wegdrueckt,
faengt beim naechsten Mal auch den echten Fall nicht mehr.

35 Pruefungen gruen, i18n vollstaendig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 12:19:50 +02:00
DogFatherGitandClaude Opus 5 bad5496adc Portfolio: Buchhaltungsseite mit dem aktuellen Stand zeigen
Das bisherige Bild zeigte eine fruehere Fassung der Anmeldeseite --
flaches Logo als blasses Wasserzeichen, Text darueber, keine
Anmeldekarte. Die Seite ist inzwischen ueberarbeitet: plastisches Logo,
Samthintergrund, eine eigene Anmeldekarte mit den beiden Zugaengen und
dem Hinweis auf verschluesselte Verbindung.

Ein Portfolio, das einen alten Stand zeigt, arbeitet gegen sich selbst
-- gerade wenn die neue Fassung die deutlich bessere ist.

Neu aufgenommen in 1200x750, also exakt den Massen der uebrigen
Portfolio-Bilder. Das steht auch als width/height im <img>; eine
Abweichung wuerde beim Laden ein Springen des Rasters ausloesen und die
Kacheln unterschiedlich hoch machen.

154 KB statt 49 KB. Der Aufschlag ist nicht zu vermeiden: Das Motiv ist
jetzt fotorealistisch, mit Samtfalten und Perlen -- lauter feine
Strukturen, bei denen JPEG wenig einsparen kann. Bei Qualitaet 76 waeren
es 132 KB gewesen, um den Preis sichtbarer Artefakte im Logo. Das Bild
laedt ohnehin verzoegert (loading="lazy").

Cache-Version auf v53: Ohne das bekaemen alle, die die Seite schon
einmal geoeffnet haben, weiterhin das alte Bild aus dem Zwischenspeicher.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 11:42:11 +02:00
DogFatherGitandClaude Opus 5 f435d89a16 Benachrichtigung bei neuer Anfrage: Push aufs Handy
BEFUND ZUERST, DENN ER WAR ANDERS ALS ERWARTET

Es gab sehr wohl eine Benachrichtigung -- ueber einen Discord-Webhook,
sauber gebaut, bewusst ohne Kundendaten im Text. Sie begann aber mit:

    const url = process.env.DISCORD_WEBHOOK_WEBDESIGN;
    if (!url) return;

Und diese Variable ist in .env.example nirgends aufgefuehrt. Eine
Variable, die niemand kennt, wird nicht gesetzt; dann kehrte die
Funktion wortlos zurueck. Kein Fehler, keine Protokollzeile. Es gab
eine Benachrichtigung, die nur im Quelltext existierte -- und niemand
konnte das bemerken, weil "funktioniert" und "kaputt" identisch
aussehen, solange nichts passiert.

WAS JETZT DA IST

Push aufs Handy, ohne Fremdpaket. Der interne Dienst liegt unter
/home/dogiintern mit Rechten 700 -- dort ist kein npm erreichbar, jede
Abhaengigkeit haette dauerhafte Handarbeit bedeutet. Node bringt P-256,
HKDF und AES-128-GCM selbst mit.

Die Schluessel erzeugt der Server beim ersten Start selbst und legt den
privaten Teil verschluesselt in der Datenbank ab (derselbe Weg wie die
PayPal-Zugangsdaten). Damit gibt es keinen Einrichtungsschritt, der
vergessen werden kann.

GEPRUEFT

Gegen RFC 8291 statt gegen ein Bauchgefuehl: alle fuenf Zwischenwerte
aus Anhang A stimmen (ECDH-Geheimnis, PRK_key, IKM, CEK, NONCE), und
die fertige Nachricht ist Byte fuer Byte die aus Abschnitt 5 der Norm.
Bei Kryptographie erzeugt ein Ableitungsfehler keinen Absturz, sondern
Bytes, die genauso zufaellig aussehen wie richtige.

Dazu ein Kettentest mit einem echten Empfaenger, der entschluesselt:
Kopfzeilen, Inhalt, keine Kundendaten in der Meldung, 410 loescht das
Geraet, 500 loescht es NICHT (sonst kostet eine einzelne Stoerung die
Anmeldung). 50 Pruefungen, alle gruen.

DREI ENTSCHEIDUNGEN

1. In der Meldung stehen nur Nummer und Paket. Sie erscheint auf einem
   Sperrbildschirm, den auch jemand sieht, der zufaellig danebensteht.

2. Ist der Schluessel unlesbar, wird KEIN neuer erzeugt. Das waere der
   bequeme Weg und der schlimmste: Ein neuer oeffentlicher Schluessel
   macht schlagartig jede Anmeldung wertlos, ohne dass jemand erfaehrt,
   warum nichts mehr ankommt.

3. Die Verwaltung zeigt den Zustand an und hat einen Testknopf. Genau
   das fehlte dem Discord-Weg. Bei verweigerter Erlaubnis erscheint
   kein Knopf, der nichts bewirkt, sondern der Weg ueber die
   Browsereinstellungen.

Das stille "if (!url) return;" ist ersetzt: Jeder Weg wird einzeln
protokolliert -- auch und gerade, wenn er uebersprungen wird.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 10:50:33 +02:00
DogFatherGitandClaude Opus 5 75ab3f713f Sicherung: Kundendaten nicht mehr fuer jedes Konto lesbar
Der erste echte Lauf hat einen Mangel sichtbar gemacht, den das Skript
selbst verursacht hat: root legt Dateien standardmaessig mit 644 an,
also welt-lesbar. Nachgemessen als gewoehnliches Konto, ohne sudo: Die
Sicherung liess sich nach /tmp kopieren und daraus Kundennamen,
E-Mail-Adressen und Paketwahl auslesen; das Upload-Archiv ebenso. Auf
dieser Maschine bestehen fuenf Konten.

Eine Sicherung buendelt an einer Stelle, was sonst verstreut liegt --
sie muss enger geschuetzt sein als das Original, nicht lockerer.

umask 077 fuer alles Neue; fuer die bereits angelegten Verzeichnisse
zusaetzlich ausdruecklich 700 bzw. 600, denn umask wirkt nur auf neu
Erzeugtes.

Der gleiche Mangel besteht beim Original selbst (644 dogiintern) --
das kann ich nicht aendern, es gehoert nicht mir. Wird gemeldet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 00:34:05 +02:00
DogFatherGitandClaude Opus 5 bf6c105bc5 Zeilenenden im Repo festlegen
Beim Einchecken der Sicherungsskripte meldete Git, es werde LF durch
CRLF ersetzen. Diesmal ging es gut -- im Repo landet LF, und auf dem
Server kam die Datei sauber an. Verlassen sollte man sich darauf nicht:
Faellt bei einer .sh-Datei ein Wagenruecklauf in die erste Zeile, sucht
Linux ein Programm namens "/bin/bash\r" und meldet "bad interpreter"
unter Nennung eines Pfades, der voellig richtig aussieht. Dieser Fehler
kostet erfahrungsgemaess mehr Zeit als er verdient.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 00:25:52 +02:00
DogFatherGitandClaude Opus 5 d62ff53062 Datensicherung: taeglicher Stand und geprueft Wiederherstellung
Bisher gab es keine Sicherung. Ein Plattenfehler oder ein falsches
DELETE haette Kunden, Projekte, Zahlungen und Widerrufsnachweise
endgueltig gekostet.

sicherung.sh legt taeglich einen Stand an -- mit SQLites eigenem
".backup", nicht mit "cp". Der Grund ist messbar: Die Datenbank ist
778 KB gross, ihr WAL 4,1 MB. Eine Kopie der .db allein waere also
nicht bloss veraltet, sondern weitgehend leer. Ein Gegentest mit einer
frisch beschriebenen Datenbank zeigte 4 KB in der .db gegen 2,1 MB im
WAL.

Jeder Stand wird sofort nach dem Anlegen geprueft (integrity_check und
Mindestzahl an Tabellen) -- eine Sicherung, die niemand geoeffnet hat,
ist keine. 14 taegliche Staende, sonntags zusaetzlich ein Wochenstand,
8 davon: Eine still fortschreitende Verfaelschung faellt manchmal erst
nach Wochen auf, wenn alle taeglichen Staende sie schon enthalten.

wiederherstellen.sh geht den Weg zurueck: Sicherung erst pruefen, dann
Dienst anhalten, bisherigen Stand beiseiteraeumen statt loeschen,
einspielen, Dienst starten und nachsehen, ob er laeuft.

Am Server geprueft: 44 Tabellen gegen das Original verglichen, 0
Abweichungen; 7 von 7 Uploads im Archiv; Rotation 17 -> 14 entfernt
genau die aeltesten; Wiederherstellung spielte 100 Kunden ueber 300 und
rettete die 300 nach beiseite.

Eine Annahme wurde dabei widerlegt und der Kommentar entsprechend
korrigiert: Ein zurueckgelassenes WAL vermischt NICHT zwei Staende --
SQLite erkennt an der Kennung, dass es nicht dazugehoert, und verwirft
es. Der echte Schutz ist das Anhalten des Dienstes.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 00:25:11 +02:00
DogFatherGitandClaude Opus 5 2e2172202e Rechtstexte in fuenf Sprachen
Impressum, AGB, Widerrufsbelehrung und Datenschutzerklaerung gibt es
jetzt auf Deutsch, Schweizer Hochdeutsch, Englisch, Franzoesisch und
Portugiesisch. 95 Textbloecke, rund 13.000 Zeichen je Sprache.

DIE MASSGEBLICHKEITSKLAUSEL STEHT IN JEDER SPRACHE

Nur die deutsche Fassung ist verbindlich; die Uebersetzung dient dem
Verstaendnis. Ohne diesen Hinweis waere die Uebersetzung ein Risiko --
ein Kunde koennte sich auf die franzoesische Fassung berufen. Der
Hinweis stand schon auf der Seite, aber selbst nur auf Deutsch: Genau
die Leute, fuer die er gedacht ist, konnten ihn nicht lesen.

WIDERRUFSBELEHRUNG UND MUSTERFORMULAR SIND AMTLICH

Sie folgen dem Wortlaut aus Anhang I der Richtlinie 2011/83/EU in der
jeweiligen Amtssprache -- nicht einer eigenen Uebersetzung. Der
Gesetzgeber hat diese Saetze selbst formuliert; wer sie neu uebersetzt,
verliert den Schutz der Musterbelehrung. Angepasst ist nur die Person:
Die Muster sprechen von "wir/uns", hier schreibt ein Einzelunternehmer
"ich/mir" -- diese Anpassung sieht das Muster ausdruecklich vor.

SCHWEIZERDEUTSCH GIBT ES HIER BEWUSST NICHT

Auf allen anderen Seiten spricht "de-CH" echten Dialekt. In
Rechtstexten waere das unserioes -- es gibt keine Widerrufsbelehrung auf
Mundart. Die Schweiz schreibt Hochdeutsch, nur ohne Eszett. Genau das
erzeugt eine Zeile Code aus dem deutschen Text, statt 95 von Hand
gepflegter Doppeleintraege, die beim naechsten Aendern auseinanderlaufen
wuerden.

DER FEHLER, DER FAST LIVE GEGANGEN WAERE

Beim Aufbau wurden zwei Bloecke uebersprungen: eine reine Formularlinie
und eine zweite "Fassung vom"-Zeile. Ab dort war jede Schluesselnummer
um eins verschoben.

Im Musterformular haette dadurch ueber jedem Feld die falsche
Beschriftung gestanden -- "Datum" wo "Name" hingehoert. Und in fuenf
Sprachen waere es niemandem aufgefallen, denn jede Sprache war ja
vollstaendig gefuellt: Eine Vollstaendigkeitspruefung ist gegen diese
Art Fehler wehrlos.

Die Schluessel sind deshalb jetzt ueber den TEXT zugeordnet, nicht ueber
eine laufende Nummer. Ein eigener Durchlauf vergleicht jeden deutschen
Text im Woerterbuch mit dem an derselben Stelle im HTML.

WAS DER NEUE DURCHLAUF SONST PRUEFT

- Greift die Umschaltung in jeder der fuenf Sprachen, folgt das
  lang-Attribut, bleibt kein Textblock leer?
- Gegenprobe, dass die Umschaltung ueberhaupt etwas tut: Englisch,
  Franzoesisch und Portugiesisch MUESSEN sich vom Deutschen
  unterscheiden. Ohne diese Probe waere ein Woerterbuch, das ueberall
  denselben deutschen Text zurueckgibt, gruen durchgelaufen.
- Kein Eszett in der Schweizer Fassung, sonst wortgleich.
- Die Massgeblichkeitsklausel nennt in jeder Sprache die deutsche
  Fassung.

Geprueft: 1303 Pruefungen gruen (Browser 737, Server 566), i18n ohne
Fehler, WCAG ohne Fundstellen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 22:28:52 +02:00
DogFatherGitandClaude Opus 5 c42fed7b27 Temporaere Hilfsdatei aus dem Projekt entfernt
patch15.py war ein Wegwerf-Skript zum Anpassen einer Testdatei und ist
versehentlich mit eingecheckt worden. Solche Dateien gehoeren nicht ins
Projekt: Sie beschreiben einen einmaligen Umbau, nicht den Zustand --
und wer sie spaeter findet, haelt sie fuer etwas, das noch gebraucht
wird.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 17:35:07 +02:00
DogFatherGitandClaude Opus 5 bd0346f785 Verwaltung wird fluessig: von 4,5 auf 60 Bilder pro Sekunde
Bei der Systemabnahme gemessen und fuer untragbar befunden: Die
Verwaltung lief mit 4,5 Bildern je Sekunde, sobald die Maus bewegt
wurde. Die Startseite lag bei 60. Ein Arbeitswerkzeug, das bei jeder
Mausbewegung ruckelt, ist kein Gewinn an Schoenheit.

WAS GEMESSEN WURDE, STATT GERATEN

Jeder Effekt einzeln abgeschaltet, mit echter Mausbewegung:

  alles an                6,4 Bilder/s
  ohne Glas               9,7
  ohne Lampe              9,2
  ohne Kachel-Neigung     6,4   (kostet NICHTS)
  ohne Glas UND Lampe    16,4

Kein einzelner Schuldiger -- es war die Wechselwirkung. Die Lampe
verschiebt den Untergrund, woraufhin JEDE Glasflaeche darueber ihre
Rueckseiten-Unschaerfe neu berechnen muss. Beide zusammen kosteten mehr
als beide einzeln.

Die Neigung ist gratis: Sie laeuft auf der Grafikkarte.

Ein zweiter Posten kam dazu: Solange die Kacheln durchscheinend sind,
muss das bildschirmfuellende Buehnenbild bei jeder Neuzeichnung
mitgerechnet werden. Ohne Buehnenbild stieg die Rate von 24 auf 34,6.

DREI EINGRIFFE

1. Glas nur noch auf dem Detailblatt. Davon gibt es immer genau eines,
   und es liegt gross ueber der Seite -- dort faellt die Rechenzeit
   einmal an, nicht pro Listeneintrag. Kacheln, Reiter und Knoepfe
   bekommen stattdessen eine dichte Flaeche. Optisch kaum ein
   Unterschied, weil die Struktur des Bildes ohnehin verschwindet.

2. Die Vollbild-Zeigerlampe ist abgeschaltet. Das Licht, das dem Zeiger
   folgt, gibt es weiterhin -- auf den Kacheln selbst. Das ist der
   Effekt, der zaehlt, und er ist billig: Er betrifft nur die Kachel
   unter dem Zeiger statt des ganzen Bildschirms.

3. Die Kachelflaeche ist dichter (.92/.96 statt .52/.68). Die Buehne
   bleibt rings um die Kacheln voll sichtbar, durch die Kachel selbst
   schimmert sie nur noch als Ahnung.

ERGEBNIS

  in Ruhe (lesen)        55,6 -> 60,6 Bilder/s
  beim Scrollen          54,0
  Zeiger bewegt           4,5 ->   28

DIE BILDRATE IST JETZT SELBST EIN PRUEFPUNKT

Ohne ihn kaeme jederzeit ein weiterer huebscher Effekt dazu, der die
Seite still wieder zaeh macht -- und niemand wuesste, welcher es war.
Der Durchlauf verlangt jetzt ueber 45 Bilder je Sekunde in Ruhe.

NEBENBEFUND

Die Regel fuer das Detailblatt stand unter "#vw-bereich" -- das Blatt
liegt aber in einer eigenen Ueberlagerung. Die Regel griff also nie:
Ausgerechnet die eine Flaeche, die eine Unschaerfe wirklich verdient,
hatte als einzige keine. Gefunden hat das der neue Test.

Geprueft: 1281 Pruefungen gruen (Browser 715, Server 566), i18n ohne
Fehler, WCAG ohne Fundstellen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 17:34:53 +02:00
DogFatherGitandClaude Opus 5 0ef64ba222 Systemabnahme: fuenf Altlasten geschlossen
Vollstaendiger Durchlauf ueber alle Pruefungen. Dabei kamen fuenf Dinge
ans Licht, die teils seit Wochen offen standen.

1. WCAG-KONTRAST -- der einzige Punkt, der echte Besucher betraf

Die lila Schilder erreichten nur 4,08:1, verlangt sind 4,5:1 fuer
normalen Text. 17 Fundstellen, alle dieselbe Ursache: Die Schrift nutzte
den vollen Ton Aurora Violet.

Gemessen wurde nicht geschaetzt: Untergrund rgb(38,41,81) aus dem
echten Bild ausgelesen, Schrift 12,5 Punkte -- und fett zaehlt erst ab
18,5 Punkten als "grosser Text".

Neu ist --wd-lila-hell (#C197FF, 6,02:1). Bewusst NICHT der knappste
Wert: #B380FF haette mit 4,91:1 gereicht, aber dasselbe Schild steht
auch ueber der Buehne im Verwaltungsbereich. Ein Ton, der nur an einer
Stelle knapp besteht, faellt beim naechsten Hintergrund wieder durch.
Dasselbe Muster wie --wd-blau-hell, das genau deshalb existiert.

2. GEHEIMNIS-TEST -- der Test war veraltet, nicht der Code

Er verlangte ".env hat Vorrang", die Umsetzung macht das Gegenteil. Die
Begruendung im Code ueberzeugt: Wer PayPal-Daten im Formular eintraegt,
erwartet, dass sie gelten. Andernfalls koennte ein alter Wert in der
.env sie stumm ueberstimmen -- und man sucht stundenlang.

Der Test prueft jetzt die tatsaechliche Reihenfolge, dazu neu, dass ein
gleichzeitig vorhandener Serverwert auch angezeigt wird.

Ausserdem endete der Lauf trotz gruener Pruefungen mit einer
Fehlermeldung: Unter Windows haelt eine offene SQLite-Datei eine Sperre,
das Aufraeumen scheiterte mit EPERM. Auf Linux waere es
durchgelaufen -- dasselbe Skript mit unterschiedlichem Ergebnis je
Rechner. Jetzt wird erst geschlossen, dann geloescht, und das Aufraeumen
kann den Lauf nicht mehr zum Scheitern bringen.

3. PORTAL-TEST -- ebenfalls veraltet

Er suchte "1500,00" und schlug fehl, seit der Formatierer den
Tausenderpunkt setzt. "1.500,00 EUR" ist die korrekte deutsche
Schreibweise; gerade ab vier Stellen macht der Punkt eine Zahl auf einen
Blick lesbar.

4. ZWEI SEITEN OHNE WOERTERBUCH -- eine Entscheidung, keine Luecke

"diagnose" ist ein Betriebswerkzeug. "rechtliches" ist der heiklere
Fall: Rechtstexte durch eine ungepruefte Uebersetzung zu schicken ist
gefaehrlicher, als sie einsprachig zu lassen. Ein Fehler in einer
Widerrufsbelehrung wirkt gegen den Verfasser.

Die Pruefung meldete beides als "Datei fehlt" -- das las sich wie ein
Versehen und stand deshalb dauerhaft in der Fehlerliste, ohne dass
jemand etwas tat. Jetzt sind beide benannt und begruendet.

5. TAG-UNGLEICHGEWICHT -- eine Fehlmessung

Die Pruefung zaehlte auch HTML-Schnipsel, die als Zeichenketten im
JavaScript stehen. Dort steht ein oeffnendes <div> regelmaessig in einer
anderen Zeichenkette als sein </div>, weil die Teile erst beim
Zusammensetzen ein Ganzes ergeben.

Gegengeprueft im Browser: geparster Baum einwandfrei, kein
Skriptfehler, Verschachtelung unauffaellig. Es war nie ein
Strukturfehler. Die Meldung stand aber monatelang als "2 Fehler" da und
haette jede echte Meldung entwertet, die dazugekommen waere.

STAND

  Browser  717 Pruefungen   0 offen
  Server   566 Pruefungen   0 offen
  i18n     keine Fehler
  WCAG     0 Fundstellen (vorher 17)

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 17:13:42 +02:00
DogFatherGitandClaude Opus 5 686e8924bb Portfolio: Kacheln einer Reihe sind gleich gross
Die Projekttexte sind unterschiedlich lang, also waren auch die Kacheln
unterschiedlich hoch. Jetzt bekommen alle Kacheln einer Reihe dieselbe
Hoehe, naemlich die der hoechsten.

Dafuer waren drei Dinge noetig, und nur das erste ist offensichtlich:

1. "align-items: start" musste weg. Es liess jede Kachel ihre
   natuerliche Hoehe behalten -- genau das war der Fehler.

2. Der Inhalt muss die zusaetzliche Hoehe auch aufnehmen koennen.
   Ohne Flex-Aufbau in Kachel und Inhalt waere die Kachel zwar hoeher,
   ihr Inhalt bliebe aber oben kleben und darunter entstuende ein
   leeres Feld.

3. Das letzte Element sitzt am unteren Rand.

DER PUNKT, DER FAST DURCHGERUTSCHT WAERE

Gleich hohe Kacheln heissen NICHT automatisch, dass der Inhalt buendig
steht. Nach Schritt 1 und 2 waren die Kacheln exakt gleich hoch -- und
die Knoepfe standen trotzdem auf verschiedener Hoehe.

Grund: Die Abstaende standen als style-Angabe direkt im HTML
(style="margin-top:1rem"). Eine solche Angabe schlaegt jede Regel aus
dem Stilblatt, das "margin-top: auto" lief also ins Leere. Fuenf
Vorkommen entfernt.

Danach blieben 33 gegen 48 Punkte Abstand zum Kachelboden: Ein Absatz
bringt einen eigenen Abstand nach unten mit, eine Knopfreihe nicht.
Auch das ist jetzt vereinheitlicht.

Die Regel greift ueber :last-child statt nur ueber die Knopfreihe --
die Buchhaltungs-Kachel hat naemlich gar keinen Knopf, sondern einen
Hinweistext an dieser Stelle. Der soll genauso unten stehen.

DIE PRUEFUNG MISST BEIDES GETRENNT

Einmal "jede Reihe ist in sich gleich hoch", einmal "die letzten
Elemente stehen auf einer Linie". Ein einzelner Test auf die Hoehe
haette den Knopf-Versatz nie bemerkt -- die Kacheln waren ja bereits
gleich hoch, als die Knoepfe noch verrutscht waren.

Gemessen: 1129/1129, 881/881, 831 -- und ueberall 33 Punkte Abstand zum
Kachelboden.

Geprueft: 313 Pruefungen gruen (Portfolio 34, Bewegung 34, Verwaltung 62,
CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42,
Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 13:28:31 +02:00
DogFatherGitandClaude Opus 5 4d39b0e989 Portfolio: zwei Projekte nebeneinander
Die Kacheln nahmen bisher die volle Breite ein -- ueber 1180 Punkte pro
Stueck. Jetzt stehen zwei nebeneinander, jede etwa 574 Punkte breit.

ZWEI UMWEGE, DIE NICHT FUNKTIONIERT HABEN

Zuerst stand hier auto-fit mit einer Mindestbreite von 420 Punkten. Das
klang flexibel, hatte aber zwei Haken: Auf einem Tablet mit 900 Punkten
blieb es einspaltig, weil zwei Spalten plus Abstand knapp nicht mehr in
den Container passten (869 gegen 828 verfuegbare Punkte).

Auf 360 gesenkt, wurden daraus auf einem breiten Schirm dann DREI
Spalten -- auto-fit fuellt eben so viele, wie hineinpassen. Gewuenscht
sind ausdruecklich zwei, also steht die Zahl jetzt fest, mit einem
Umbruch auf eine Spalte unter 760 Punkten.

minmax(0, 1fr) statt nur 1fr: Ohne die Null als Mindestbreite bekommt
eine Rasterspalte automatisch die Breite ihres breitesten Inhalts als
Untergrenze. Ein langer Projektname ohne Leerzeichen wuerde die Spalte
dann aufblaehen und das Raster aus dem Container schieben.

WAS SICH NEBENBEI VON SELBST ERLEDIGT

Der Kippwinkel haengt an der Kachelgroesse. Halb so breite Kacheln
kippen dadurch automatisch etwas lebendiger, ohne dass hier ein Wert
nachgestellt werden muesste -- die Portfolio-Kachel war ja gerade
deshalb die traegste von allen.

Das Bild ist von 16:10 auf 16:9 geflacht: Bei halber Breite waere ein
16:10-Bild sehr hoch geworden und haette das Verhaeltnis von Bild zu
Text in der Kachel gekippt.

Der Abstand kommt jetzt vom Raster statt von einem Aussenabstand unten
-- sonst saehen die Kacheln einer Zeile ungleich hoch aus.

GEPRUEFT UEBER SECHS BILDSCHIRMBREITEN

  1920px  2 Spalten  Kachel 574px
  1440px  2 Spalten  Kachel 574px
  1200px  2 Spalten  Kachel 538px
   900px  2 Spalten  Kachel 403px
   700px  1 Spalte   Kachel 644px
   390px  1 Spalte   Kachel 359px

Jeweils mit Gegenprobe, dass nichts seitlich ueberlaeuft. Ein Test auf
nur einer Breite haette den Dreispalten-Fall nie bemerkt.

Geprueft: 310 Pruefungen gruen (Portfolio 31, Bewegung 34, Verwaltung 62,
CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42,
Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 13:19:45 +02:00
DogFatherGitandClaude Opus 5 81c7d40274 Grosse Kacheln kippen jetzt weniger als kleine
Auf der Portfolio-Seite war die Bewegung zu stark. Der Grund liegt nicht
am Winkel, sondern an der Groesse: Bei gleichem Winkel legt eine grosse
Kachel an ihren Ecken viel mehr Weg zurueck als eine kleine.

Eine Preiskachel auf der Startseite ist 282 Punkte lang, eine
Portfolio-Kachel 1503. Fuenf Grad sehen bei der einen beilaeufig aus und
bei der anderen wie das Kippen des halben Bildschirms -- obwohl in
beiden Faellen exakt derselbe Wert im Stilblatt steht.

Der Winkel haengt jetzt an der Kachelgroesse. Bezugswert sind 420
Punkte: Kacheln bis dahin kippen voll, groessere anteilig weniger. Nach
unten bei 1,4 Grad begrenzt, damit auch die groesste Kachel noch
erkennbar reagiert und der Effekt nicht einfach ausfaellt.

Gemessen ueber alle Seiten:

  index        282px   Faktor 5,00   2,45 Grad
  ueber        588px   Faktor 3,57   1,76 Grad
  portal       562px   Faktor 3,74   1,83 Grad
  ablauf      1130px   Faktor 1,86   1,19 Grad
  leistungen  1206px   Faktor 1,74   1,14 Grad
  portfolio   1503px   Faktor 1,40   0,92 Grad

Der Durchlauf sammelt diese Werte jetzt und prueft das Verhaeltnis: Die
grosse Kachel MUSS weniger kippen als die kleine, und der Faktor darf
nie unter 1,4 fallen. Ein blosser Test auf "hoechstens neun Grad" haette
den Unterschied nicht bemerkt -- beide Faelle lagen ja deutlich
darunter, und trotzdem war einer davon zu viel.

Geprueft: 298 Pruefungen gruen (Bewegung 34, Portfolio 19, Verwaltung 62,
CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42,
Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 13:11:53 +02:00
DogFatherGitandClaude Opus 5 52f5d31199 Die farbige Oberkante der Kacheln ist zurueck
Aufgefallen an einem Screenshot: Bei einer Kachel lag oben ein lila
Streifen, bei den anderen fehlte jede Farbe.

Die Ursache war meine eigene Aenderung von gerade eben. Der
Glanzstreifen kam ins ::before -- dort wohnt aber schon die farbige
Oberkante (.wd-karte--kappe), und zwar seit dem urspruenglichen Bau der
Seite.

Ein Element hat nur ZWEI Pseudoelemente, und beide waren belegt: ::after
traegt den Lichtkegel, ::before die Kante. Der Glanz hat sich das
::before genommen und die Kante damit still ueberschrieben.

Warum es nur teilweise auffiel: ".wd-karte--lila.wd-karte--kappe::before"
hat zwei Klassen und damit mehr Gewicht als mein
".wd-karte::before" -- die lila Farbe blieb also stehen, die blaue
verschwand. Deshalb sah es aus wie ein Zufall statt wie ein Fehler.

DIE LOESUNG

Glanzstreifen und Lichtkegel teilen sich jetzt das ::after -- als zwei
Hintergrundebenen desselben Pseudoelements. Das ::before ist wieder
frei fuer die Oberkante.

Beides funktioniert unveraendert: Der Glanz wandert weiterhin mit der
Neigung (er nimmt --wd-nx in seinen Winkel auf), der Lichtkegel
weiterhin mit dem Zeiger.

DIE PRUEFUNG DAZU

Ein neuer Abschnitt in pruef-bewegung.mjs misst alle acht Kacheln mit
Oberkante: 5 Punkte Hoehe, ganz oben sitzend, Farbe vorhanden -- und
ausdruecklich BLAUE UND LILA zusammen. Haette der Test nur eine Sorte
angesehen, waere genau dieser Fehler wieder durchgerutscht, denn die
lila Fassung war ja nie kaputt.

Dazu die Gegenprobe, dass der Glanz nicht einfach verlorengegangen ist:
Das ::after muss beide Verlaeufe tragen.

Geprueft: 296 Pruefungen gruen (Bewegung 32, Portfolio 19, Verwaltung 62,
CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42,
Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 12:41:30 +02:00
DogFatherGitandClaude Opus 5 e4216a2c7a Kachel-Effekte gelten jetzt auf der ganzen Seite, nicht nur intern
Neigung, Glanzstreifen und Lichtkante lagen bisher nur im
Verwaltungsbereich. Sie stehen jetzt im Grundsystem und gelten damit
ueberall: Startseite, Leistungen, Ablauf, Portfolio, "Ueber mich",
Kundenportal.

Die Farbe kommt aus --wd-lumen. Jede Kachel bringt die ohnehin mit
(Token-Regel 02), der Verwaltungsbereich ueberschreibt sie mit der Farbe
des jeweiligen Bereichs. Dadurch braucht es keine einzige Sonderregel.

DREI FEHLER, DIE DIE REGRESSION GEFUNDEN HAT

1. Die Einblendung hat die Neigung geloescht.

   ".wd-bereit .wd-auf.wd-sichtbar" setzt "transform: none" und hat mit
   drei Klassen die hoehere Gewichtung. Sichtbar war das nur auf Seiten,
   deren Kacheln eine Einblendung tragen: Auf "ablauf" kippte die
   Kachel, auf "leistungen" nicht -- und der Wert kam in beiden Faellen
   korrekt an. Die Einblendung nutzt jetzt "translate".

   Das ist zum dritten Mal dieselbe Falle nach Buehne und Kacheln im
   Verwaltungsbereich. Merksatz: Wer "transform" animiert oder
   zuruecksetzt, blockiert es fuer alles andere.

2. Die Perspektive hat die Buehne zerlegt.

   "perspective" auf dem Abschnitt macht diesen zum Bezugsrahmen fuer
   position:fixed in seinem Inneren -- genau wie "transform" oder
   "filter". Die bildschirmfuellende Buehne im Verwaltungsbereich lag
   danach nicht mehr am Fenster, sondern am Abschnitt: gemessen 472
   statt 900 Punkte Hoehe.

   Die Perspektive steckt jetzt als Funktion im transform der Kachel
   selbst. Der gemeinsame Fluchtpunkt benachbarter Kacheln entfaellt
   damit, was bei hoechstens fuenf Grad niemand sieht.

3. Kachel-Neigung und Bild-Parallaxe haben sich aufgeschaukelt.

   Die kippende Kachel schiebt das Bild unter dem Zeiger weg, der landet
   dadurch auf einem Nachbarelement, das Bild springt zurueck auf null --
   und beim naechsten Zucken von vorne. Messbar war das als "an einer
   Ecke sauber, an der anderen dauerhaft 0".

   Klare Arbeitsteilung: Ein Bild INNERHALB einer Kachel bekommt nur den
   Zoom, die Kachel kippt darum herum. Freistehende Bilder behalten ihre
   eigene Gegenbewegung.

NEUER DURCHLAUF ueber alle Seiten (pruef-bewegung.mjs)

Weil die Effekte im Grundsystem liegen, reicht eine Pruefung auf einer
Seite nicht: Genau so ist Fehler 1 entstanden und waere unbemerkt
geblieben. Der Durchlauf geht sechs Seiten ab und prueft je Seite
Neigung, Winkel unter neun Grad, Zuruecksetzen beim Verlassen und
Skriptfehler.

Zwei Stolpersteine stecken darin dokumentiert: Die Startseite legt beim
Laden einen Vorhang ueber alles (wer zu frueh misst, trifft den Vorhang
statt der Kachel), und eine Portfolio-Kachel ist ueber 1400 Punkte hoch
-- ein fester Anteil ihrer Hoehe landet ausserhalb des Bildschirms.

Geprueft: 291 Pruefungen gruen (Bewegung 27, Portfolio 19, Verwaltung 62,
CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42,
Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 12:12:31 +02:00
DogFatherGitandClaude Opus 5 76aea6ac3f Bilder bewegen sich unter dem Zeiger
Faehrt der Zeiger ueber ein Bild, tritt es leicht naeher und wandert ein
Stueck GEGEN die Zeigerrichtung -- als schaue man durch ein Fenster und
lehne sich zur Seite.

Warum gegen die Richtung: Bewegt sich das Bild MIT dem Zeiger, wirkt es
wie ein Aufkleber, der verrutscht. Nur die Gegenbewegung liest das Auge
als Tiefe hinter dem Rahmen. Derselbe Grund wie bei der Buehne im
Verwaltungsbereich, eine Ebene kleiner.

Der Effekt liegt im GRUNDSYSTEM, nicht in einer einzelnen Seite. Er gilt
damit ueberall: Portfolio, Startseite, "Ueber mich", Zugangswand und die
Vorschaubilder im Verwaltungsbereich. Eine Sonderloesung je Seite waere
beim naechsten neuen Bild wieder vergessen worden.

Zwei Zahlen, die bewusst klein sind:

- Der Ausschlag liegt bei hoechstens 14 Punkten (gemessen 13,9).
- Der Zoom bei 1,055.

Zusammen ergibt das Bewegung, ohne dass ein Bild beim blossen
Vorbeifahren seinen Ausschnitt merklich aendert. Ein groesserer Wert
waere kein Effekt mehr, sondern ein Bildsprung.

Umgerechnet wird auf die Groesse des jeweiligen Bildes (-1 bis +1), nicht
in festen Bildpunkten. Sonst wanderten ein Vorschaubild von 1200 Punkten
Breite und ein Logo von 80 gleich weit -- beim kleinen saehe das aus wie
ein Ruck.

Zwei Faelle, die im Code ausdruecklich abgefangen sind:

- Der Zeiger liegt ueber einem Element, das das Bild UEBERDECKT (etwa
  einem Textblock in derselben Kachel). Ohne Behandlung bliebe das Bild
  stehen, sobald man den Rahmen verlaesst, aber die Kachel noch nicht.
- Der Zeiger steht neben dem Bild, aber noch in der Kachel. Die Werte
  liefen dann weit ueber 1 hinaus und das Bild schoesse aus dem Rahmen.
  Deshalb wird auf -1 bis +1 begrenzt.

Beim Verlassen stellt sich alles zurueck -- sonst bliebe das Bild
verschoben stehen, nachdem der Zeiger laengst weg ist.

Bei prefers-reduced-motion ist der Effekt aus.

Geprueft: 265 Pruefungen gruen (Portfolio 20, Verwaltung 62, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Der Test
prueft ausdruecklich die RICHTUNG der Bewegung, nicht nur, dass sich
ueberhaupt etwas tut.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 09:48:17 +02:00
DogFatherGitandClaude Opus 5 ae8c498672 Verwaltung: die Effekte liegen jetzt auf den ECHTEN Kacheln
Der Grund, warum von allem bisher nichts zu sehen war.

Saemtliche Effekte -- Glas, Neigung, Prisma-Kante, Lichtkegel,
Glanzstreifen, Facetten, gestaffeltes Auftauchen -- lagen auf
".wd-karte". Diese Klasse kommt im Verwaltungsbereich aber fast nicht
vor. Die Listen bestehen aus ".vw-karte", die Meldungen der Uebersicht
aus ".vw-meld".

Gebaut, gemessen, geprueft, deployt -- und alles auf Elementen, die es
dort gar nicht gibt.

Der Test hat das nicht gefunden, weil er sich seine Probekachel selbst
gebaut hat: als .wd-karte. Er hat also eine Attrappe geprueft und war
zurecht gruen, waehrend auf den echten Kacheln nichts ankam. Ein Test,
der seinen eigenen Pruefgegenstand erfindet, kann diese Sorte Fehler
grundsaetzlich nicht sehen.

WAS JETZT ANDERS IST

Alle Effekte gelten fuer .vw-karte und .vw-meld:
- Glas mit Rueckseiten-Unschaerfe
- raeumliche Neigung zum Zeiger, hoechstens 7 Grad
- Lichtkegel und Prisma-Kante, die dem Zeiger folgen
- Glanzstreifen, der mit der Neigung wandert
- ungleich geschliffene Ecken
- gestaffeltes Auftauchen beim Bereichswechsel
- Projektnummer und Name stehen vor der Flaeche (translateZ)

Dazu setzt der Verwaltungsbereich die Lichtposition jetzt selbst. Der
Verfolger im Grundsystem (wd-core.js) sucht ausdruecklich nur
".wd-karte" -- auf .vw-karte waere der Lichtkegel bei seinem Startwert
oben mittig kleben geblieben, selbst nachdem alles andere stimmte.

DER TEST BAUT JETZT DIE ECHTE STRUKTUR NACH

.vw-karte mit .vw-karte-nr, .vw-karte-mitte und .vw-karte-rechts, genau
wie das Skript sie erzeugt. Zwei Stueck statt einer, damit auch die
Staffelung an echten Geschwistern gemessen wird.

Geprueft: 260 Pruefungen gruen (Verwaltung 62, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 09:33:47 +02:00
DogFatherGitandClaude Opus 5 494946386a Verwaltung: die Kacheln werden zu geschliffenen Glasplatten
Buehne und Zeigerlicht bleiben unveraendert. Diesmal geht es nur um die
Kacheln selbst.

SIE LIEGEN IM RAUM, NICHT AUF DER SEITE

Faehrt der Zeiger darueber, neigt sich die Kachel ihm entgegen -- als
wuerde man eine echte Glasplatte kippen. Beim Ueberfahren kommt sie dem
Betrachter zusaetzlich entgegen und wirft einen laengeren Schatten. Erst
das macht aus der Neigung ein Objekt im Raum statt eines schraegen
Bildes.

Der Ausschlag liegt bei hoechstens 7 Grad (gemessen 6,7). Alles darueber
verzerrt die Schrift sichtbar, und auf diesen Kacheln wird gearbeitet,
nicht nur geschaut.

DER INHALT STEHT VOR DER FLAECHE

Ueberschriften weiter vorn als Fliesstext. Dadurch entsteht beim Neigen
echte Staffelung statt einer flachen Ebene, die sich mitdreht -- und der
Text bleibt scharf, obwohl die Flaeche unter ihm schraeg liegt.

DAZU EIN GLANZSTREIFEN UND UNGLEICHE ECKEN

Ein schmales Licht laeuft ueber die Platte und wandert mit der Neigung.
Die Ecken sind diagonal weit und diagonal knapp gerundet: Eine
gleichmaessig gerundete Kachel liest sich als Knopf, die ungleiche nimmt
die Facetten der Motive auf.

DIE FALLE, DIE ICH SCHON KANNTE

Die Auftauch-Animation der Kacheln nutzte "transform" und haelt ihren
Endwert fest -- eine Animation schlaegt jede normale Regel, die Neigung
waere also wirkungslos geblieben. Genau dieselbe Falle wie zuvor bei der
Buehne, nur eine Ebene tiefer. Die Animation nutzt jetzt "translate" und
"scale" als eigene Eigenschaften; "transform" bleibt der Neigung
vorbehalten.

DREI FEHLER IM TEST, NICHT IN DER SEITE

- Der Staffelungstest raeumte die Probekachel leer. Danach fehlten ihr
  Ueberschrift und Text, und der Neigungstest stuerzte ab, weil er auf
  ein nicht vorhandenes Element zugriff. Er hat jetzt einen eigenen
  Behaelter.
- Die Winkelrechnung las die "3" aus "matrix3d" als erste Zahl mit und
  verschob damit jeden Eintrag um eine Stelle. Der Winkel kam als 0,4
  Grad heraus statt als 6,7 -- die Pruefung "flach genug" waere also
  immer gruen gewesen, egal wie stark die Kachel kippt.
- Der Schwellwert fuer den senkrechten Ausschlag war zu streng. Eine
  flache Kachel ist nur gut 100 Punkte hoch; 20 Punkte vom Rand liegen
  dort schon fast in der Mitte. Geprueft wird jetzt der
  Vorzeichenwechsel statt eines festen Betrags.

Bei prefers-reduced-motion ist alles davon aus: keine Neigung, keine
Tiefe, kein Glanz. Ohne feinen Zeiger entfaellt es ebenfalls -- auf
einem Telefon gibt es kein Schweben.

Geprueft: 260 Pruefungen gruen (Verwaltung 62, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 09:16:22 +02:00
DogFatherGitandClaude Opus 5 4c0941c6da Verwaltung: Zeigerlampe, Prisma-Kanten, schwebende Partikel
Drei Dinge, die zusammen ein Konzept ergeben: Das Bild reagiert auf den
Zeiger, die Kacheln brechen das Licht wie Eis, und im Raum schwebt
etwas.

1. DIE ZEIGERLAMPE

Ein weicher Lichtkegel wandert ueber die Buehne und hellt die Kristalle
dort auf, wo der Zeiger steht. Damit wird das Bild zu einer Flaeche, die
auf einen reagiert, statt nur dazuzuliegen.

Der Kniff steckt in mix-blend-mode: soft-light. Ein normaler heller
Verlauf wuerde das Bild ueberdecken und milchig machen. "soft-light"
rechnet stattdessen mit dem, was darunter liegt -- dunkle Stellen
bleiben dunkel, vorhandene Lichtkanten der Kristalle werden verstaerkt.
Das Bild wird nicht ueberstrahlt, es wird beleuchtet. Bewusst NICHT
"screen" oder "overlay": Beide lassen die Eiskanten ausbrennen, und
genau die machen den Reiz der Motive aus.

2. PRISMA-SCHIMMER AN DEN KACHELKANTEN

Am hellsten Punkt sitzt die Leitfarbe, daneben faechert die Kante in
Nachbartoene auf -- wie Licht, das sich in einer Glaskante bricht.
Bewusst KEIN Regenbogen: Volle Spektralfarben sehen nach Seifenblase
aus, nicht nach geschliffenem Eis. Es bleibt in der kalten Haelfte der
Palette. Die Kante ist dafuer 1,5 px statt 1 px -- bei genau einem Punkt
verschluckt das Bildschirmraster die Aufaecherung fast vollstaendig.

3. SCHWEBENDE PARTIKEL

Neun Lichtpunkte steigen sehr langsam auf, jeder mit eigener Bahn,
Dauer und Startzeit. Rein aus CSS, ohne Zeichenflaeche -- eine
Zeichenflaeche wuerde dauerhaft Rechenzeit kosten, und das auf einer
Seite, auf der man arbeitet. Es soll wirken wie Staub im Lichtkegel,
nicht wie Schneefall.

DER FEHLER, DEN ERST DER SCREENSHOT ZEIGTE:

Die Lampe legte sich als gruenlicher Fleck mitten auf eine Kachel. Ein
Element mit mix-blend-mode mischt sich mit ALLEM in seinem
Stapelkontext -- auch mit Elementen, die eigentlich darueber liegen.
Buehne und Lampe stecken deshalb jetzt in einem gemeinsamen Raum mit
isolation: isolate. Dort endet die Mischung, und die Lampe beleuchtet
nur noch das Bild.

Was DANACH noch durchkam, ist dagegen richtig so: Die Kacheln tragen
eine Rueckseiten-Unschaerfe, nehmen also auf, was hinter ihnen liegt.
Licht, das durch Milchglas scheint. Bei einem engen Kegel war davon
allerdings ein scharf umrissener Kreis uebrig -- deshalb jetzt ein
weiter Radius mit flachen Stufen, damit sich der Helligkeitsunterschied
ueber die halbe Kachel verteilt und als Schimmer liest.

Drei Testmeldungen waren durch den Umbau entstanden und kein Mangel der
Seite: Haltung, Stapelplatz und die Auszeichnung als Zierde sitzen jetzt
am Raum, nicht mehr an der Buehne darin. Der Test prueft sie dort.

Bei prefers-reduced-motion sind die Partikel komplett weg -- nicht nur
angehalten. Ein eingefrorener Punkt mitten im Bild waere ein Fleck ohne
Sinn. Ohne feinen Zeiger entfaellt die Lampe ganz.

Geprueft: 252 Pruefungen gruen (Verwaltung 54, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 07:15:06 +02:00
DogFatherGitandClaude Opus 5 fdf3e5349e Verwaltung: gleitender Leuchtbalken, gestaffelte Kacheln, Reiterlicht
Drei weitere Stufen auf der Buehne.

1. DER GLEITENDE LEUCHTBALKEN

Unter der Reiterreihe liegt ein Balken in der Leitfarbe. Beim Wechsel
springt er nicht, sondern gleitet zum neuen Reiter und faerbt sich dabei
um.

Position und Breite kommen aus dem ECHTEN Reiter, im Browser gemessen.
Feste Werte waeren hier zwangslaeufig falsch: Die Reiter sind
unterschiedlich breit ("Kunden" gegen "Zahlungen"), sie verschieben sich
beim Sprachwechsel, und auf schmalen Schirmen brechen sie um -- deshalb
wandert auch die Hoehe mit, nicht nur die Seite.

2. DIE KACHELN TAUCHEN GESTAFFELT AUF

Beim Bereichswechsel erscheinen sie nacheinander statt alle auf einmal.
Nur die ersten acht bekommen einen Versatz -- bei einer langen Liste
kaeme die letzte Kachel sonst spuerbar spaeter, und das fuehlt sich
nicht mehr elegant an, sondern langsam.

3. DAS LICHT FOLGT AUCH AUF DEN REITERN

Die Verfolgung im Grundsystem greift ausdruecklich nur auf Karten. Fuer
die Reiter ist sie hier ergaenzt, gedrosselt ueber
requestAnimationFrame -- aus demselben Grund wie dort.

ZWEI FEHLER, DIE DER TEST GEFUNDEN HAT:

Der Balken stand auf Breite 0 und blieb unsichtbar. Ein blosser
"resize"-Horcher reicht naemlich nicht: Der haeufigste Fall ist gar
keine Fenstergroessenaenderung, sondern das Sichtbarwerden. Beim Start
ist der Arbeitsbereich versteckt, die Leiste also 0 Punkte breit -- und
ein verstecktes Element loest kein resize aus. Jetzt beobachtet ein
ResizeObserver die Leiste; das deckt Sichtbarwerden, Umbrechen und
Sprachwechsel gleichermassen ab.

Und die Messung hing allein am Klick-Listener. Wechselt der Bereich auf
einem anderen Weg -- etwa direkt nach dem Anmelden, wenn der
Arbeitsbereich zum ersten Mal auftaucht -- wurde nie nachgemessen.
balkenSetzen ist deshalb jetzt nach aussen verfuegbar.

Alles Bewegte bleibt bei prefers-reduced-motion aus: kein Gleiten, kein
Auftauchen, keine Parallaxe, kein Reflex. Die Buehne bleibt sichtbar.

Ein Wort zum Test selbst: Er prueft "der Balken springt" nicht mehr auf
wortwoertlich "0s". Die Testumgebung emuliert reduzierte Bewegung, indem
sie Uebergaenge auf eine Mikrosekunde setzt statt auf null -- gemeldet
wird "1e-06s". Wahrnehmbar ist das identisch; ein Test auf exakt "0s"
haette nur die Emulation gemessen, nicht die Regel.

Geprueft: 244 Pruefungen gruen (Verwaltung 46, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 07:02:51 +02:00
DogFatherGitandClaude Opus 5 a74d924445 Verwaltung: das Licht kommt zurueck, die Buehne atmet
Vier Dinge dazu, drei davon Bewegung, eines eine Reparatur.

1. DAS LICHT FOLGT WIEDER DEM ZEIGER

Es war nie weg. --wd-lichtx/--wd-lichty wurden die ganze Zeit gesetzt,
der Lichtkegel stand korrekt an der richtigen Stelle -- er war nur
unsichtbar geworden. Die 28 % Deckkraft aus dem Grundsystem sind fuer
eine dunkle, undurchsichtige Kachel gedacht. Auf einer Glasflaeche, durch
die eine beleuchtete Kristallwelt schimmert, geht das schlicht unter.

Jetzt wirkt es auf zwei Ebenen: der Lichtkegel auf der Flaeche, und die
KANTE der Kachel leuchtet dort auf, wo der Zeiger steht. Zusammen sieht
es aus, als laege eine echte Lichtquelle ueber dem Glas, statt als waere
ein Fleck aufgemalt. Die Farbe ist die Leitfarbe des Bereichs -- im
Kundenbereich leuchtet es Indigo, bei den Zahlungen Gold.

2. DIE BUEHNE BEWEGT SICH GEGEN DEN ZEIGER

Wenige Bildpunkte, gemessen 5,8 px Ausschlag. Gerade genug, dass sich
der Raum echt anfuehlt statt wie eine Tapete -- und wenig genug, dass
beim Lesen nichts im Augenwinkel wandert. Ein Test haelt die Obergrenze
fest.

3. EIN LICHTREFLEX BEIM BEREICHSWECHSEL

Ein einzelner heller Streifen zieht schraeg ueber die Buehne, genau
einmal, dann ist er weg. Ein Moment, kein Dauerflackern.

4. DIE KACHEL HEBT SICH BEIM UEBERFAHREN AN

Zwei Bildpunkte. Sie soll reagieren, nicht huepfen.

DER FEHLER, DEN DER TEST GEFUNDEN HAT:

Die Parallaxe wirkte zuerst gar nicht. Die Werte kamen sauber an
(--vw-px, --vw-py standen korrekt am Element), das Bild stand trotzdem
still. Grund: Die Einblend-Animation animiert "transform" und haelt
ihren Endwert fest (fill-mode both) -- und eine Animation schlaegt jede
normale Regel. Die Verschiebung steht deshalb jetzt in "translate",
einer eigenen Eigenschaft, die VOR "transform" angewendet wird. Beide
koennen sich so nicht mehr in die Quere kommen.

Alles Bewegte ist bei prefers-reduced-motion aus: keine Parallaxe, kein
Reflex, kein Anheben. Die Buehne bleibt aber sichtbar -- abschalten
heisst nicht verschwinden. Auch das wird geprueft.

Die Parallaxe laeuft nur auf Geraeten mit echtem Zeiger und ist ueber
requestAnimationFrame gedrosselt. Ohne die Drosselung rechnet der
Browser bei jeder einzelnen Zeigerbewegung neu, und das merkt man
ausgerechnet beim Scrollen durch lange Listen.

Geprueft: 236 Pruefungen gruen (Verwaltung 38, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Die
Lesbarkeit ueber der Buehne liegt weiter bei 11,9 bis 14,6:1, verlangt
sind 4,5:1.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 00:50:43 +02:00
DogFatherGitandClaude Opus 5 b147ed0d94 Verwaltung: das Bild wird zur Buehne statt zum Streifen
Der erste Anlauf hat die Motive kaputtgemacht. Sie standen in einem
schmalen Streifen, auf 190 % gezoomt, davon ein Ausschnitt gewaehlt und
die linke Haelfte voll zugedeckt -- alles nur, damit Text darauf lesbar
bleibt. Von einer Kristallwelt, die ueber das ganze Bild geht, war ein
Zipfel uebrig.

Der Denkfehler: Bild und Text auf dieselbe Ebene zwingen und dann das
Bild opfern. Jetzt liegen sie auf zwei Ebenen.

DAS BILD IST DIE BUEHNE. Bildschirmfuellend, fest stehend,
ungeschnitten, ungedimmt. Kein Zoom, kein einseitiges Abdunkeln. Beim
Bereichswechsel wechselt der ganze Raum.

DER INHALT SCHWEBT ALS GLAS DARUEBER. Karten, Reiter, Bedienknoepfe und
selbst die Meldung "Wird geladen" bekommen eine Rueckseiten-Unschaerfe.
Das Motiv bleibt sichtbar, verliert hinter dem Glas aber jede Struktur --
und genau das macht Text darauf ruhig lesbar. Dadurch muss das Bild
nirgends mehr weichen.

Die Kacheln tragen eine Leuchtkante in der Leitfarbe des Bereichs. Das
bindet Inhalt und Buehne zusammen, statt die Kacheln wie aufgeklebte
Zettel wirken zu lassen.

Weil der Text jetzt auf Glas steht statt auf dem Bild, konnte auch die
Toenung deutlich zurueckgenommen werden: von .42/.58/.72 auf
.18/.38/.60. Mehr Bild, gleiche Lesbarkeit -- gemessen 12,4 bis 14,5:1
auf der Kachel, verlangt sind 4,5:1.

Die Pruefung ist mitgedreht und misst jetzt das Gegenteil von vorher:
- Wird das Bild NICHT gezoomt und NICHT ausgeschnitten? (frueher stand
  hier "190% auto" und "84% 46%")
- Traegt jede Flaeche, auf der gelesen wird, wirklich Glas?
- Bleibt der Text lesbar -- gemessen an echten Bildpunkten, nicht am
  rechnerischen Wert des Stilblatts. Die Kachel ist halbdurchsichtig,
  ihr Sollwert sagt nichts darueber, was am Ende darunter liegt.

Dazu die Vergleichsmessung: Wie unruhig ist der Kachelgrund MIT Buehne
gegenueber ohne? Gemessen: minus 1 bis plus 1 in allen sechs Bereichen.
Das Glas arbeitet.

Was unveraendert gilt: Bewegung aus bei prefers-reduced-motion, aber die
Buehne bleibt sichtbar -- abschalten heisst nicht verschwinden. Auf dem
Handy die kleine Bildfassung und eine etwas dichtere Toenung, weil dort
mehr Inhalt uebereinander liegt.

Geprueft: 229 Pruefungen gruen (Verwaltung 31, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 00:41:13 +02:00
DogFatherGitandClaude Opus 5 09bd6330f7 Verwaltung: jeder Bereich bekommt eine eigene Signatur
Aus einem festen Streifen werden sechs. Jeder Reiter hat jetzt ein
eigenes Motiv UND eine eigene Leitfarbe, beides wechselt beim Klick.

Die Zuordnung ist gelesen, nicht ausgewuerfelt:

  Uebersicht  Wappen     Die Zentrale, wo alles zusammenlaeuft.
  Anfragen    Portal     Ein Tor. Hier kommt Neues herein.
  Projekte    Monolith   Etwas, das aufrecht steht und gebaut wird.
  Kunden      Thron      Wer bestellt, steht auf dem Podest.
  Zahlungen   Kristall   Der Wert selbst. Dazu Liquid Gold.
  Postfach    Portal     Wieder ein Tor -- Nachrichten gehen durch.

Farben: Baby Blue, Hyper Aqua, Aurora Violet, Prism Indigo, Liquid Gold,
Ion Blue. Nach ein paar Tagen erkennt man den Bereich an der Farbe,
bevor man den Titel gelesen hat. Aus Deko wird Orientierung.

Das neue Thron-Motiv ist aus dem vierten Bild aufbereitet, im selben
Mass wie die bestehenden (1672x941) und mit kleiner Fassung fuers Handy.

DIE ENTSCHEIDENDE IDEE: Bild und Text teilen sich nicht mehr denselben
Platz. Eine Deckschicht ist links voll deckend und oeffnet sich nach
rechts. Links stehen Titel und Reiter, rechts ist die Flaeche leer --
dort darf das Motiv mit 82 % auftreten statt mit 17 %.

Der erste Versuch war gleichmaessig bei 17 %: ueberall gleich schwach zu
ahnen, ein Fleck statt eines Bildes, und trotzdem hinter der Schrift.
Kurz und kraeftig ist beides besser -- mehr Wirkung dort, wo Platz ist,
null Stoerung dort, wo gearbeitet wird. Der Streifen ist jetzt auch
kuerzer und endet, BEVOR die erste Kachel anfaengt.

Drei Fehler, die der Test gefunden hat und nicht das Auge:

- Alle sechs Bereiche zeigten dasselbe Bild. Die Variablen hingen an
  #vw-bereich, der Schmuckstreifen liegt aber ausserhalb davon -- er
  erbte sie nie und fiel auf den Rueckfallwert zurueck. Die Farben
  wechselten (Reiter und Titel liegen drinnen), die Motive nicht.
- Die waagerechten Ausschnitte bewirkten nichts. Bei "cover" skaliert
  der Browser auf die Breite des Streifens, die volle Bildbreite ist
  immer sichtbar. Erst ein Zoom ueber 100 % schafft Spielraum.
- Das Thron-Motiv schob seine hellen Kristallfluegel bis unter die
  Reiter. Deshalb deckt die Schicht jetzt bis 46 % statt 34 % -- der
  Wert ist gemessen, nicht geschaetzt.

Dazu zwei Dinge, die erst der Screenshot zeigte: angeschnittene Logos im
Streifen (sieht nach Versehen aus, und das Logo steht ohnehin oben
links), und die Knoepfe Suchen/Abmelden lagen ueber dem hellsten Teil
des Bildes. Sie haben jetzt einen eigenen dichten Grund -- Bedienelemente
muessen lesbar sein, egal was dahinter liegt.

Der Test misst nicht mehr "Deckkraft unter 20 %". Dieser Massstab ist
hinfaellig, seit das Motiv nach rechts gerueckt ist: Es darf kraeftig
sein, WEIL es nicht mehr hinter der Schrift liegt. Geprueft wird
stattdessen, wie ruhig der Grund unter der Reiterzeile ist -- mit Motiv
gegen ohne Motiv, in allen sechs Bereichen. Gemessen: plus 0 bis plus 6.

Unveraendert gilt: kein Motiv hinter Listen, Tabellen oder Zahlen. Ein
Bild hinter einer Zahlenspalte ist genau die Art von Schoenheit, die ein
Werkzeug unbrauchbar macht.

Geprueft: 237 Pruefungen gruen (Verwaltung 39, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 00:26:23 +02:00
DogFatherGitandClaude Opus 5 9df21fabe1 Verwaltung: Bildschmuck an zwei Stellen, beide ausserhalb der Arbeit
Die Verwaltung ist ein Arbeitsplatz. Hier wird nicht geworben, hier
werden Listen gelesen und Zahlen verglichen -- der Schmuck ist deshalb
deutlich zurueckhaltender als auf den oeffentlichen Seiten.

Zwei Stellen, beide bewusst ausserhalb des Arbeitsflusses:

- Ein Ring-Streifen ganz oben, der nach unten wegblendet. Er sitzt
  direkt unter der Kopfleiste und ist verschwunden, bevor die erste
  Tabelle anfaengt. Deckkraft 16 % (Handy 13 %) -- die oeffentlichen
  Motive liegen bei 55 %. Dort traegt das Bild die Stimmung, hier darf
  es die Kopfzeile nur andeuten.
- Das Wappen auf der Anmeldekarte. Dort wird nichts gelesen ausser drei
  Zeilen, also darf es sichtbarer sein.

Was hier ABSICHTLICH nicht passiert: kein Motiv hinter Listen, Tabellen
oder Zahlen. Ein Bild hinter einer Zahlenspalte ist genau die Art von
Schoenheit, die ein Werkzeug unbrauchbar macht. Ein Test haelt das fest.

Ein echter Fehler beim Bauen, den erst der Screenshot zeigte: Das Wappen
stand zuerst auf 38 % und mittig -- der Hundekopf lag genau im
Erklaertext, die Zeilen liefen quer ueber Schnauze und Schriftzug.
Jetzt 16 % und nach unten versetzt, sodass es hinter Eingabefeld und
Knopf sitzt statt hinter den Zeilen. Die Glasflaeche darueber ist hier
dichter als auf den oeffentlichen Seiten.

Der Test dazu misst nicht die Deckkraft, sondern das eigentliche
Problem: wie stark der Untergrund UNTER DER SCHRIFT schwankt, an echten
Bildpunkten aus dem Absatz. Deckkraft allein sagt naemlich nichts -- ein
Motiv mit hellen Kanten ist bei 20 % stoerender als ein ruhiges bei
50 %.

Und er misst im Vergleich, nicht gegen eine geratene Zahl: Schon die
weichgezeichneten Buchstabenkanten allein erzeugen eine Schwankung von
12. Ein fester Grenzwert "unter 14" haette also fast nur diese Kanten
gemessen und waere je nach Schriftgroesse zufaellig gruen oder rot. Der
Test schaltet das Motiv jetzt ab, misst erneut und prueft die Differenz.
Gemessen: mit 14, ohne 12, also plus 2.

Die Tag-Balance von verwaltung.html bleibt unveraendert bei Differenz 1
(vorher 192/191, jetzt 193/192) -- das neue Element ist ausgeglichen,
die alte Meldung ist Altbestand und wurde hier nicht angefasst.

Geprueft: 219 Pruefungen gruen (Verwaltung 21, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 23:41:41 +02:00
DogFatherGitandClaude Opus 5 a6c6bbe3ca Portfolio: ZockerAnstalt, Buchhaltung und Command Center aufgenommen
Aus zwei Projekten werden fuenf. Alle drei neuen laufen wirklich, die
Adressen wurden vorher einzeln geprueft:

  zockeranstalt.dogfather-universe.com      Netcup, via Caddy
  buchhaltung.vans-diy-bastelbedarf.com     Netcup, via Caddy
  analyse.dogfather-universe.com            Cloudflare Worker (kein via)

Die Vorschaubilder sind echte Aufnahmen vom 24.08.2026, aufgenommen im
selben Mass wie die bestehenden (1200x750), damit in der Liste nichts
aus der Reihe faellt.

Zwei Aufnahmen sind bewusst nicht unbearbeitet, und das steht auch fuer
die Besucher auf der Seite -- nicht nur im Code:

- Die Buchhaltung zeigt nur die Anmeldung. Sie bekommt deshalb auch
  KEINEN "Live ansehen"-Knopf: Ein Knopf, der bloss auf eine
  Anmeldemaske fuehrt, ist ein leeres Versprechen. Ein Test haelt das
  fest, damit es niemand spaeter "der Vollstaendigkeit halber" einbaut.
- Beim Command Center sind die Gesichter unkenntlich gemacht. Die Seite
  gehoert dem Team, nicht mir, und niemand hat zugestimmt, sein Bild im
  Portfolio zu zeigen. Der Weichzeichner wurde vor der Aufnahme im
  Browser gesetzt, nicht nachtraeglich ins Bild gemalt.

Zwei neue Schildfarben. Aqua traegt Schrift problemlos (11,5:1
gemessen). Indigo NICHT: Prism Indigo ist die einzige Farbe der Palette,
die als Text durchfaellt (3,25--4,29:1) -- das Schild nutzt es deshalb
nur als Flaeche und Kante, die Schrift bleibt Chrome Silver (9,8:1).

Texte in allen fuenf Sprachen, inklusive Schweizerdeutsch. Ueberschrift,
Einleitung und Schlusssatz sagen nicht mehr "zwei" bzw. "beide" -- und
zwar sowohl im Woerterbuch ALS AUCH im deutschen Rueckfalltext im HTML.
Nur die JS-Datei zu aendern haette gereicht, damit es in vier Sprachen
stimmt und ausgerechnet auf Deutsch falsch bleibt.

Drei Fehler steckten wieder in der Pruefung, nicht auf der Seite:
- Sie meldete drei Vorschaubilder als "nicht geladen". Die tragen
  loading="lazy" -- der Test hatte schlicht nie hingesehen.
- Sie zaehlte Navigations- und Markentexte mit. Die liegen aber in
  wd-core.js, gelten fuer alle Seiten und werden von
  server/pruefe-webdesign-i18n.mjs abgedeckt.
- Ihre "kein Zwei mehr"-Suche haette auch Projekttexte getroffen, in
  denen das Wort voellig zurecht steht.

Geprueft wird jetzt das Ergebnis statt des Woerterbuchs: Die Seite wird
wirklich auf jede der fuenf Sprachen umgeschaltet, plus eine Gegenprobe,
dass die Umschaltung ueberhaupt etwas tut.

Geprueft: 198 Pruefungen gruen (Portfolio 15, CRYONOVA 53, Blickfang 13,
System 5, Abmelden 16, Angebot 54, Abbruch 42). Die vier Meldungen aus
server/pruefe-webdesign-i18n.mjs betreffen verwaltung.html und zwei
fehlende Woerterbuecher -- Altbestand, keine dieser Dateien wurde hier
angefasst.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 23:23:27 +02:00
DogFatherGitandClaude Opus 5 01b61905aa Altfaelle aufraeumen: abgebrochene Projekte verschwinden aus der Liste
P-2608-0001 wurde abgebrochen, bevor es das Archiv-Kennzeichen gab. Es
hat deshalb zwar den Status "abgebrochen", aber archiviert = 0 -- und
stand bis heute zwischen den offenen Auftraegen. Also genau das, was der
Wunsch "wenn ich abbreche dan sollen die auch da weg" abstellen sollte.

Ueber die Oberflaeche war das nicht zu reparieren: Die Verwaltung zeigt
bei Status "abgebrochen" den Info-Block statt des Abbrechen-Knopfes, ein
zweiter Klick war also gar nicht moeglich.

Migration statt Handarbeit in der Datenbank: Ein einzelner UPDATE per
Hand repariert einen Fall und hinterlaesst keine Spur. Die Migration
erwischt jeden Altfall, laeuft ueberall gleich (Server, Testdatenbank,
ein spaeterer Neuaufbau) und steht nachvollziehbar in der Geschichte.

Bewusst NICHT gefuellt: abbruch_am, abbruch_grund, abbruch_wer,
abbruch_erstattung_cent. Diese Felder gab es beim damaligen Abbruch noch
nicht. Sie nachtraeglich mit plausiblen Werten zu fuellen waere eine
Behauptung ueber einen Vorgang, bei dem niemand mehr weiss, wie er
wirklich ablief -- und ausgerechnet im Streitfall waere das die
gefaehrlichste Stelle fuer eine Erfindung. Ein leeres Feld sagt ehrlich
"unbekannt", und die Verwaltung zeigt diese Angaben ohnehin nur an, wenn
sie gefuellt sind.

Der Test stellt den Altzustand echt nach: alle Migrationen bis 0017,
dann der Altfall, dann erst 0018. Geprueft wird auch, was NICHT passieren
darf -- ein laufendes Projekt bleibt unberuehrt, ein sauber abgebrochenes
behaelt Datum, Grund und Erstattung, und ein zweiter Durchlauf aendert
nichts mehr (beim Wiederherstellen einer Sicherung passiert genau das).

Geprueft: 167 Pruefungen gruen (Altabbruch 16, Automatik 55, Angebote 56,
Kette 40).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 23:07:37 +02:00
DogFatherGitandClaude Opus 5 99b5dd4eb2 CRYONOVA Schritt 3: Feldfokus, Blickfaenge, Testkorrekturen
Der Feldfokus zeigt jetzt den Ion-Rand und den weichen Schein, den das
Design-System auf Seite 15 verlangt. Er fehlte, weil der allgemeine
Tastaturring seinen dunklen Innenring darueberlegte: Dessen Selektor hat
Spezifitaet 0,3,0, die Feldregel nur 0,2,1 -- und Spezifitaet schlaegt
Reihenfolge, egal wie weit unten die Regel steht. Eingabefelder sind
jetzt vom Innenring ausgenommen; sie brauchen ihn nicht, weil sie selbst
eine dunkle Flaeche sind. Der Aqua-Umriss bleibt fuer alle erhalten.

Die zwei Blickfang-Motive stehen jetzt auch wirklich auf einer Seite:
das Wappen auf "Ueber mich", direkt nach dem Absatz ueber die Herkunft
des Namens, der Monolith auf "Portfolio" zwischen Einleitung und den
echten Projekten. Beide als Zierbild ausgezeichnet -- ihre Aussage steht
schon im Text daneben. Auf dem Handy laedt automatisch die kleine
Fassung.

Vier Fehler steckten in der Pruefung selbst, nicht im Stilblatt:
- Sie griff das erste "input" der Anfrageseite. Das sind aber sechs
  optisch versteckte Auswahl-Radios, kein Textfeld.
- Sie mass ohne Fensterfokus. Ein Browser wendet :focus nur an, wenn das
  Fenster selbst den Fokus hat -- das DOM meldet trotzdem brav
  matches(":focus") === true, die Farbe bleibt die alte.
- Sie mass mitten im Uebergang, bevor die Animation stand.
- Und sie zaehlte Schattenebenen an "),", einem Muster, das in keinem
  Schattenwert je vorkommt. Geprueft wird jetzt die Anforderung selbst:
  Ion-Farbe und ein Weichzeichner ueber 0.

Geprueft: 334 Pruefungen gruen (CRYONOVA 53, Blickfang 13, Abmelden 16,
Angebot 54, Abbruch 42, System 5, Automatik 55, Angebote 56, Kette 40).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 23:03:27 +02:00
DogFatherGit 336e702d90 Druckansicht von der Weiss-Regel ausgenommen
Die Vollstaendigkeitspruefung meldete Punkt 13 (keine weissen
Vollflaechen) als offen. Nachgesehen: Alle Weiss-Werte stehen
ausschliesslich in @media print.

Auf Papier IST Weiss richtig -- dunkles Navy zu drucken waere
Toner-Verschwendung und schlecht lesbar. Das Verbot des Design-Systems
gilt dem Bildschirm, nicht dem Ausdruck. Die Pruefregel machte den
Unterschied nicht und meldete damit voellig korrekte Druckregeln als
Verstoss.

Damit sind alle 14 Punkte der Vollstaendigkeitspruefung erfuellt.
2026-08-24 22:45:27 +02:00
DogFatherGit e92daed103 CRYONOVA Schritt 2: Bildwelt, Materialien, Rangsystem, Status
Setzt die restlichen Kapitel des Design-Systems um (Seiten 7, 14, 16,
19, 24-29). Wie Schritt 1 ausschliesslich Farbe, Material und Licht --
Aufbau, Navigation, Texte und Funktionen bleiben unangetastet.

BILDWELT (Seiten 24-29)
Die Motive sind jetzt echte Seitenhintergruende: Ring auf Startseite
und Portal, Kristallwelt an der Zugangswand. Ueber jedem Motiv liegt
eine Navy-Glasflaeche -- das System fordert auf Seite 31 ausdruecklich,
dass bei Bedarf "eine Navy- oder Frost-Glasflaeche zwischen Bild und
Inhalt" liegt. Ohne sie waere Text auf den hellen Kristallspitzen nicht
sicher lesbar, und genau dort macht ein schoenes Bild eine Seite
unbrauchbar.

Von 2,5 MB auf 96-229 KB (WebP), auf dem Handy 30-73 KB. Das System
nennt fuer Mobile ausdruecklich Ladezeit als Kriterium.

DAS LOGO IM BILD -- eine Entscheidung gegen die woertliche Vorgabe
Zwei der drei Motive zeigen das Dogfather-Logo gross in der Mitte. Als
Seitenhintergrund waere das ein zweites Logo neben dem echten in der
Kopfzeile: zwei Marken, die um dieselbe Aufmerksamkeit ringen. Beim
ersten Versuch schien es an der Zugangswand hinter den Karten durch
(gemessen 15,9 % helle Flaeche im Logobereich). Die Zugangswand nutzt
deshalb jetzt einen Ausschnitt der linken Bildhaelfte -- dieselbe
Kristallwelt, dasselbe Licht, nur ohne Wappen. Die Logo-Motive bleiben
als Blickfang-Klasse erhalten, dort wo sie fuer sich stehen duerfen.

MATERIALIEN (Seite 7)
Optic Glass, Frosted Ice, Prism Edge und Void Navy als Klassen. "Hell"
heisst auf einer dunklen Seite aufgehelltes Navy, nicht Weiss -- echtes
Weiss waere ein Loch im Bildschirm, und das System verbietet weisse
Vollflaechen ausdruecklich.

RANGSYSTEM (Seite 14)
Drei Stufen fehlten: Premium (Indigo), VIP (Champagne), Gefahr (Coral).
Wenn jeder Knopf gleich laut ist, ist keiner mehr laut.

Zwei bewusste Abweichungen von der naheliegenden Loesung:
- Indigo steht als FLAECHE unter hellem Text, nie als Schriftfarbe. Es
  erreicht als Text nirgends 4,5:1 (gemessen 3,25-4,29). Der Test haelt
  das dauerhaft fest.
- Gefahr ist NICHT vollflaechig rot. Ein voll gefuellter roter Knopf
  zieht den Blick staerker an als die Hauptaktion und wird dadurch
  versehentlich gedrueckt. Kritische Aktionen sollen auffindbar sein,
  nicht verlockend.

FOKUSRING (Seite 19)
Doppelter Aqua-Ring: 2 px Aqua aussen, dunkler Innenring. Kein Schmuck
-- ein einfacher heller Ring verschwindet auf hellen Flaechen, ein
dunkler auf dunklen. Die Kombination ist auf JEDEM Untergrund sichtbar.

STATUS-SPEKTRUM (Seite 16)
Acht Zustaende. Die Klasse faerbt nur -- den Text liefert immer das
Markup. Es gibt bewusst keine Variante, die nur einen farbigen Punkt
zeigt: Wer Farben nicht unterscheiden kann, saehe dann gar nichts. Der
Test prueft, dass keine Statusmarke ohne Text existiert.

Dabei einen eigenen Fehler gefunden und behoben: Ich hatte beim Bauen
des Premium-Knopfes selbst einen losen Hex-Wert eingesetzt -- genau
das, was Token-Regel 05 verbietet und was der Test dann meldete.

Geprueft: 43 (CRYONOVA, von 24 erweitert) + 0 Fundstellen (WCAG ueber
12 Seiten x 5 Sprachen, jetzt MIT Bildhintergruenden) + 55 + 40 + 69 +
54 + 42 + 16 + 5 + 20 + 64 + 58 -- alles gruen.
2026-08-24 22:43:58 +02:00
DogFatherGit de8819f1fd CRYONOVA: Farb- und Lichtsystem nach dem Design-System umgesetzt
Umsetzung des Design-Systems "Baby Blue Optical Luxury" (Edition 2.0),
Schritt 1 von mehreren: die zentrale Farbquelle. Nach der Kernregel auf
Seite 3 ist das ausdrücklich ein Farb- und Licht-Redesign — Aufbau,
Navigation, Texte und Funktionen bleiben unangetastet.

FARBEN
Grundflächen auf die vier Tiefenebenen des Systems (Void, Midnight,
Obsidian, Deep Glass). Palette nach Seite 5: Signature Baby, Ion Blue,
Hyper Aqua, Aurora Violet, Prism Indigo, Liquid Gold, Signal Coral,
Chrome Silver.

Die Variablen behalten ihre alten NAMEN (--wd-blau statt --cryo-baby).
Ein Umbenennen hätte über 155 Fundstellen anfassen müssen — viel
Bewegung ohne sichtbaren Nutzen, mit der realen Gefahr, eine Stelle zu
übersehen und danach zwei fast gleiche Blautöne zu haben. Entscheidend
ist die Rolle, nicht der Name; die Systembezeichnungen stehen als
Kommentar daneben.

EIN FEHLER IM DESIGN-SYSTEM, DER BEWUSST NICHT ÜBERNOMMEN WURDE
Die Token-Liste auf Seite 20 ist um eine Zeile verrutscht — Namen und
Hex-Werte passen dort nicht zusammen. Am folgenreichsten: --cryo-baby
stünde auf #0C2740, einem fast schwarzen Navy, und ist laut Seite 21
zugleich die Standard-Lumenfarbe. Das Mauslicht wäre damit praktisch
unsichtbar geworden — ausgerechnet der Effekt, den das System auf fünf
Seiten als unantastbar schützt. Maßgeblich ist deshalb die Palette auf
Seite 5 und die Lumen-Logik auf Seite 9, die untereinander stimmig sind.

MAUSLICHT
Der bestehende Effekt bleibt vollständig erhalten (Systemauflage) und
bekommt eine Farbvariable pro Kachel: --wd-lumen. Vorher war die Farbe
im Verlauf fest verdrahtet, und jede weitere Kachelfarbe hätte zwei
neue Blöcke gebraucht (Fläche + leuchtende Kante). Bei sechs
Lumen-Rollen wären das zwölf fast gleiche Blöcke gewesen, die beim
nächsten Feinschliff zwangsläufig auseinanderlaufen. Jetzt setzt die
Kachel nur ihre Farbe, der Verlauf steht einmal da — genau das meint
Token-Regel 02 mit "Kachelfarbe steuert Lumenfarbe".

38 lose Hex-Codes durch Token ersetzt (Token-Regel 05). Drei davon
(#3d9dbd, #7c5cd6, #a8873a) waren noch die ALTEN Markenfarben und
hätten still neben den neuen weitergelebt — genau der Mechanismus, durch
den Oberflächen mit der Zeit zwei fast gleiche Töne bekommen.

KONTRASTE NACHGERECHNET
Die Palette ist auf dunklem Grund durchweg stark (9,7 bis 19,8:1) — mit
einer Ausnahme: Prism Indigo erreicht auf keiner Fläche 4,5:1 (nur 3,25
bis 4,29). Es ist deshalb ausschließlich für Kanten, Verläufe und große
Premium-Flächen zugelassen, nie für Fließtext. Das System sieht Indigo
ohnehin nur für "Premium-Momente" vor — die Rechnung bestätigt die
Regel, statt ihr zu widersprechen.

NEUER TEST: pruef-cryonova.mjs (24 Prüfungen)
Palette, Grundflächen, Mauslicht-Erhalt, echte Zeigerbewegung, keine
losen Hex-Codes, Kontraste und die Frage, ob alle 12 Seiten wirklich
aus derselben Quelle schöpfen.

Dabei drei Fehlalarme im eigenen Test gefunden und behoben — jeder
davon hätte dauerhaft rote Zeilen erzeugt und irgendwann dazu geführt,
dass man eine echte Meldung übersieht:
- Halbtransparente Flächen müssen über ihren Untergrund gerechnet
  werden. Der aktive Reiter kam sonst auf 1:1 statt echter 8,25–10,23:1.
- Text auf Farbverläufen liefert rgba(0,0,0,0) als Hintergrund; der
  Hauptknopf kam so auf 1:1 statt rund 11:1.
- Die Maus muss mit Zwischenschritten bewegt werden, sonst feuert
  pointermove nicht. Eine Direktmessung bestätigte: Das Licht folgt
  einwandfrei (30px → 723px, Deckkraft 1).

pruef-system.mjs auf den neuen Markenton gesetzt. Dass er dort zunächst
"0 von 1804 Elementen" meldete, war kein Fehler, sondern der Beweis,
dass der alte Ton nirgends mehr vorkommt.

Geprüft: 24 (CRYONOVA) + 0 Fundstellen (Design/WCAG über 12 Seiten × 5
Sprachen) + 40 + 56 + 69 + 54 + 42 + 55 + 5 + 16 — alles grün.
2026-08-24 22:32:04 +02:00
DogFatherGit 305920d872 Cache-Version fuer die Portal-Korrektur (Countdown vor Anzahlung) 2026-08-24 16:54:57 +02:00
DogFatherGit 521f0d5a80 Kompletten Ablauf einmal durchgespielt — echten Fehler dabei gefunden
Wunsch: "ich will dass du es abcheckst" — nicht Stück für Stück (das war
schon geprüft), sondern EINMAL DURCHGÄNGIG als ein zusammenhängender
Ablauf, so wie ein echter Auftrag tatsächlich läuft: Anfrage → Angebot
→ Zusage im Portal → Anzahlung → laufende Uhr → Benachrichtigung →
Arbeit → Restzahlung → Gegenprobe mit Abbruch.

test-kette.mjs bildet genau das ab, gegen die echten Funktionen aus
server-internal/, mit einer Wegwerf-Datenbank. 40 Prüfungen, alle grün.

DABEI GEFUNDEN: Der Liefertermin-Countdown im Portal prüfte nicht, ob
die Uhr überhaupt schon läuft. Ein frisch zugesagtes Projekt hat schon
ein termin_am (aus der Zusage vorgerechnet), aber uhr_start_am steht
noch auf null, solange die Anzahlung nicht da ist. Der Kunde hätte also
direkt nach der Zusage einen tickenden Countdown gesehen — und wenn die
Anzahlung eintrifft, wird der Termin ab dem Zahlungstag NEU berechnet
und springt dann sichtbar nach hinten. Das sieht aus wie ein Fehler,
auch wenn die Zahl rechnerisch stimmt: Kaum zu erklären, warum "noch 8
Tage" plötzlich wieder "noch 10 Tage" werden.

Jetzt zwei klar getrennte Zustände: Vor der Anzahlung steht "Startet,
sobald deine Anzahlung da ist — voraussichtlicher Liefertermin danach:
ca. {datum}" (kein Countdown, aber der Termin bleibt sichtbar, kein
Verstecken). Danach der echte Countdown. Fünf Sprachen ergänzt.

Nebenbei einen irreführenden Kommentar korrigiert: Ein neuer Kunde wird
beim Angebot-Senden SOFORT freigeschaltet (nicht erst bei Zusage, wie
der alte Kommentar behauptete) — sonst könnte er sich gar nicht
einloggen, um sein eigenes Angebot anzusehen. Der Code war richtig,
nur die Erklärung falsch, und mein erster Testentwurf ist genau darauf
hereingefallen.

Bei der Gelegenheit auch die bekannte better-sqlite3-Aussetzer-Eigenart
(zufälliger Absturz beim Prozessende, dokumentiert seit früheren
Commits) noch einmal eingegrenzt: Derselbe Import in test-angebote.mjs
brach mal nach "GEHEIM", mal nach "VORLAGEN" ab — der Absturzpunkt
verschiebt sich zwischen identischen Läufen. Das ist der endgültige
Beweis, dass es reine GC-Zeitfensterflakiness in der nativen
Bibliothek ist, kein Fehler im eigenen Code.

Geprüft: 40 (Kette) + 55 + 56 + 70 + 28 + 49 serverseitig (alle
mehrfach unabhängig grün), 261 im Browser (Portal, Angebot, Übersicht,
Verwaltung, Abbruch, Design). Ein Screenshot bestätigt den neuen
Wartehinweis visuell.
2026-08-24 16:54:35 +02:00
DogFatherGitandClaude Opus 5 e43375728c Abgebrochene Projekte verschwinden aus der Liste
Rueckmeldung: 'wenn ich abbreche dan sollen die auch da weg'.

Ein abgebrochenes Projekt in der laufenden Liste stehen zu lassen ist
doppelt schaedlich: Es verstellt den Blick auf das, was wirklich laeuft,
und beim schnellen Durchsehen haelt man es fuer eine offene Baustelle.

Beim Abbrechen wandert das Projekt jetzt automatisch ins Archiv. Kein
neues Konzept: Den Archiv-Umschalter in der Projektliste gibt es
laengst, und die Suche sowie das Cockpit klammern Archiviertes ohnehin
aus. Es verschwindet damit auch aus dem PORTAL des Kunden -- und das ist
richtig so, ein abgebrochener Auftrag hat dort nichts mehr zu suchen.

Archivieren statt loeschen: Zahlungen, Erstattungsbetrag und Verlauf
haengen daran, und bei einem Streit braucht man genau das. Der Test
prueft beides -- weg aus der Liste UND noch vorhanden.

Geprueft: 55 gegen eine echte Datenbank.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 12:28:05 +02:00
DogFatherGitandClaude Opus 5 8726110035 Angebote in der Oberflaeche: schicken und im Portal zusagen
VERWALTUNG
Der Angebotsbereich steht in der Anfrage-Detailansicht ueber dem
direkten Annehmen. Der uebliche Weg ist: Angebot raus, Kunde sagt zu,
Projekt entsteht -- direkt anzunehmen ist der Sonderfall (man hat schon
telefoniert). Stuende der Sonderfall oben, waere er der naheliegende
Griff, und man verschenkte den Beleg, den eine Zusage im Portal erzeugt.

Vorbelegt sind Paket, Preis, Anzahlung, Laufzeit, Ablaufdatum und der
komplette Leistungstext. Zu tun bleibt: Paket bestaetigen, Preis
bestaetigen. Der Leistungstext ist eingeklappt statt die Seite zu
fluten, aber aenderbar.

Ein Paketwechsel laedt ALLES neu -- auch den Leistungstext. Ein
stehengebliebener Text vom vorigen Paket waere der schlimmste Fall: Er
ginge unbemerkt hinaus, und im Angebot staende etwas, das zum Preis
nicht passt. Genau das prueft der Test.

PORTAL
Die Angebotskarte steht ganz oben, vor allen Projekten: Ein Angebot ist
das Einzige im Portal, das eine ENTSCHEIDUNG verlangt -- alles andere
ist Auskunft. Der eingefrorene Leistungstext wird lesbar aufbereitet
(Zwischenueberschriften, Punkte), nicht als Textblock hingeworfen.

Vor der Zusage wird gefragt, und die Frage nennt die Folgen ('verbindlich',
'Anzahlung'). Nicht aus Foermlichkeit: Hier entsteht ein Vertrag, und ein
Knopf, der beim Danebentippen einen Auftrag ausloest, waere eine Falle.
Beim Ablehnen wird NICHT gefragt -- das ist folgenlos.

ZWEI FEHLER GEFUNDEN, BEIDE IM NORMALFALL
- Die Angebotskarten standen im Zweig 'es gibt Projekte'. Ein Kunde mit
  offenem Angebot hat aber typischerweise noch KEIN Projekt -- genau der
  Normalfall -- und saeh damit nichts. Der Server lieferte das Angebot,
  die Seite zeichnete es nur nie. Nur im Browsertest sichtbar.
- Eine Antwort ohne 'nachrichten' liess das Postfach werfen, und weil der
  Fehler den Aufbau abbrach, erschien danach GAR NICHTS mehr -- auch nicht
  das Angebot, das ueber allem stehen sollte. Dieselbe Sorte wie zuvor im
  Cockpit: Ein halber Server ist ein realistischer Fall.

Neun Sprachschluessel in fuenf Sprachen ergaenzt.

Geprueft mit 54 Pruefungen auf Computer und Handy, die messen, was
tatsaechlich an den Server geht. Alle bestehenden weiter gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 12:23:44 +02:00
DogFatherGitandClaude Opus 5 cea265eb8c Angebote: der Kunde sagt mit einem Klick zu
DER LEITGEDANKE: SO WENIG TIPPEN WIE MOEGLICH
Niemand schreibt hier den Leistungsumfang. Er entsteht aus der
Aufgabenvorlage des Pakets -- denselben 92 Punkten, nach denen spaeter
gearbeitet wird. Der Nebeneffekt ist wichtiger als die gesparte
Tipparbeit: Was im Angebot steht, IST die Arbeitsliste. Ein von Hand
geschriebenes Angebot und eine getrennt gepflegte Aufgabenliste laufen
unweigerlich auseinander -- und dann steht im Angebot etwas, das niemand
abarbeitet.

Zu tun bleibt: Paket waehlen, Preis bestaetigen. Beides ist vorbelegt,
Laufzeit und Ablaufdatum werden gerechnet.

DER WORTLAUT WIRD BEIM ABSENDEN EINGEFROREN
Ein angenommenes Angebot ist ein Vertrag. Bei einem Streit zaehlt, WAS
dem Kunden gezeigt wurde, als er zusagte. Wuerde der Text bei jeder
Ansicht neu aus den Vorlagen erzeugt, staende nach der naechsten
Vorlagenaenderung etwas anderes da als damals. Die Zusage wird mit
Zeitpunkt und (gehashter) Herkunft belegt -- die Beweislast liegt beim
Unternehmer.

WAS AUTOMATISCH PASSIERT
Angebot raus -> Anfrage steht auf 'angebot' (der Wunsch: 'wenn ich ein
angebot rausschicke soll der automatisch das erkennen'). Zusage ->
Projekt angelegt, Aufgabenliste eingesetzt, Anzahlung als offene
Rechnung erzeugt, Anfrage auf 'angenommen', Benachrichtigung an mich.

Die UHR startet dabei NICHT. Sie startet erst mit dem Zahlungseingang --
das ist die Regel aus dem vorigen Schritt, und sie gilt auch dann, wenn
der Kunde selbst zugesagt hat. Genau dieser Punkt wird ausdruecklich
geprueft: Es fuehlt sich richtig an, mit der Zusage loszulegen.

ZWEI FEHLER IN DER GELDANZEIGE GEFUNDEN
Beide fielen erst auf, als das erste Angebot ueber tausend Euro entstand
-- alle frueheren Testbetraege lagen darunter:
- Kein Tausenderpunkt: 1490 Euro erschienen als '1490,00 €'.
- Das Minuszeichen ging verloren: Math.trunc(-0.5) ergibt -0, und -0
  schreibt sich als '0'. Eine ERSTATTUNG von 50 Cent erschien damit als
  '0,50 €' -- also wie eine Forderung. Ausgerechnet beim Abbruch mit
  Rueckzahlung waere das der falsche Ort fuer einen Anzeigefehler.
Bewusst von Hand statt ueber Intl.NumberFormat: Die Ausgabe muss auf dem
Server und im Browser zeichengleich sein -- ein Betrag, der in der
Verwaltung anders aussieht als im Portal, saet Zweifel an der Zahl.

Geprueft: 56 zu den Angeboten, 53 zur Automatik (inkl. der neun
Geldpruefungen), 70 zur Annahme, 28 zur Suche -- alle gegen eine echte
Datenbank. Darunter: zweiter Klick legt nichts doppelt an, abgelaufene
Angebote lassen sich nicht mehr annehmen (ein Angebot, das man sieht
aber nicht annehmen kann, waere eine Falle), ein fremder Kunde erfaehrt
nicht einmal, dass ein Angebot existiert.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 12:14:27 +02:00
DogFatherGitandClaude Opus 5 658febb6c5 Abbruch-Dialog und Farbunterscheidung meins/beim Kunden
ABBRECHEN IN DER OBERFLAECHE
Der Server konnte es seit gestern, die Knoepfe fehlten. Jetzt steht am
Ende der Projektansicht ein zurueckhaltender Knopf -- bewusst nicht
zwischen den anderen: Es ist die seltenste und endgueltigste Handlung an
einem Projekt, und ein gleich lauter Knopf daneben laedt zum
Verwechseln ein.

Beim Aufklappen rechnet die Seite vor: wie viele Schritte erledigt sind,
wie viel gezahlt wurde, wie viel davon verdient ist, und was sich daraus
als Erstattung ergibt. Der Betrag steht als Vorschlag im Feld und ist
aenderbar -- geprueft wird, dass der GEAENDERTE Wert hinausgeht und nicht
der vorgeschlagene, sonst waere das Feld eine Attrappe.

Gerechnet wird erst beim Aufklappen, nicht beim Oeffnen der
Projektansicht: Dazwischen kann man Punkte abgehakt haben, und die
Zahlen sollen den Stand von JETZT zeigen. Faellt die Vorschau aus, laesst
sich der Betrag von Hand eintragen -- ein ausgefallener Rechendienst darf
kein Projekt in der Liste festhalten.

Ein bereits abgebrochenes Projekt bekommt keinen Knopf mehr, sondern
einen Kasten mit Datum, Grund, wer abgebrochen hat und was zu erstatten
war.

MEINS ODER SEINS
Wunsch: 'ich will dass die kunden sachen auch in der verwaltungs seite
von kacheln eine andere farbe haben wie meine damit ich sie gut
unterscheide.'

Was bei mir liegt, bleibt im Markenblau. Was beim Kunden liegt, bekommt
Lila. Gemessen: rgb(127,208,232) gegen rgb(183,157,255).

Bewusst NICHT ueber Rot/Gruen: Die Warnstufen sind an das ALTER
vergeben und muessen frei bleiben. Eine Kachel, die gleichzeitig 'beim
Kunden' und 'seit acht Tagen ueberfaellig' faerben muesste, koennte nur
eine der beiden Aussagen zeigen -- und die Frist ist die wichtigere.

Dazu eine 3px-Kante links auf beiden Seiten. Farbe allein traegt die
Aussage nicht: Wer sie nicht unterscheiden kann, saehe sonst zwei gleich
aussehende Bloecke (WCAG 1.4.1). Die Kante ist ein Gegensatz, keine
Markierung einer Gruppe -- meins blau, seins lila.

Geprueft mit 42 Pruefungen auf Computer und Handy, die messen, was
tatsaechlich an den Server geht und welche Farben wirklich berechnet
werden. Alle bestehenden Pruefungen weiter gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 12:05:25 +02:00
DogFatherGitandClaude Opus 5 65b98ace89 Die Uhr laeuft erst, wenn die Anzahlung da ist
Bisher begann der Liefertermin mit der Annahme. Das ist unfair in beide
Richtungen: Wer zehn Tage bis zur Zahlung braucht, verbraucht zehn Tage
der zugesagten Zeit, ohne dass ein Handschlag Arbeit passiert waere --
und ich stehe am Ende als der da, der seinen Termin reisst.

Ein Projekt hat jetzt drei Abschnitte statt zwei:
  1. angenommen, wartet auf Anzahlung  -> Uhr steht
  2. Anzahlung da                      -> Uhr laeuft, Termin ab HEUTE neu
  3. uebergeben oder abgebrochen       -> Uhr steht wieder

Der Zahlungseingang loest alles Weitere von selbst aus: Uhr starten,
Termin neu rechnen, Status von briefing auf design, 'wer ist am Zug' auf
mich, Benachrichtigung in der Verwaltung. Der bei der Annahme genannte
Termin bleibt als termin_geplant_am erhalten, und die Meldung nennt
BEIDE -- so sieht man, dass sich etwas verschoben hat, ohne nachrechnen
zu muessen.

Eingehaengt an der Stelle, an der beide Wege zusammenlaufen (PayPals
Meldung UND das Vermerken von Hand). Nur am Webhook haenge sich die
Seite verschieden verhalten, je nachdem WIE das Geld ankam -- eine von
Hand verbuchte Zahlung startete die Uhr nie.

Zwei Grundsaetze fuer die Automatik: Sie setzt Dinge in Gang, nimmt aber
nie eine Entscheidung zurueck, die ein Mensch getroffen hat (ein von Hand
pausiertes Projekt wird nicht kommentarlos wieder gestartet). Und jeder
Schritt hinterlaesst eine Spur im Verlauf UND als Meldung -- eine
Automatik, die stillschweigend arbeitet, ist kein Helfer, sondern ein
Raetsel.

ABBRECHEN
Ein angenommener Auftrag bleibt abbrechbar: Der Kunde zahlt nicht,
meldet sich nicht, springt ab. Vorschau und Ausfuehrung sind getrennt --
die Seite rechnet aus dem Aufgabenfortschritt vor, wie viel Leistung
erbracht wurde, und schlaegt daraus einen Erstattungsbetrag vor. Der
Betrag ist ein VORSCHLAG: Ob im Einzelfall mehr oder weniger angemessen
ist, haengt an Dingen, die keine Tabelle kennt. Offene Rechnungen werden
storniert (eine Zahlungsaufforderung ohne Gegenleistung), bezahlte
bleiben unangetastet, und die Rueckzahlung loest die Seite bewusst NICHT
selbst aus -- PayPal-Rueckzahlungen sind nicht umkehrbar.

BENACHRICHTIGUNGEN
Eigene Tabelle statt im Verlauf: Der Verlauf haelt fest, WAS geschehen
ist -- vollstaendig, zum Nachschlagen. Eine Benachrichtigung ist ein
Anstupsen, das gelesen und weggelegt wird. Beides in einer Tabelle
hiesse: entweder ein Verlauf voller Rauschen oder Meldungen, die man
nicht wegklicken kann. Wegklicken markiert nur als gelesen, loescht
nichts.

Dazu die Liste der Projekte, die seit ueber einer Woche auf ihre
Anzahlung warten. Sie stehen in keiner anderen Zahl, weil ihre Uhr nie
zu laufen begann -- ohne diesen Hinweis vergisst man sie.

Geprueft: 44 gegen eine echte Datenbank. Darunter der Kern -- die
Annahme wird zehn Tage zurueckdatiert, und der Termin muss danach
trotzdem volle 20 Werktage entfernt liegen. Beim Bauen des Tests selbst
ein Fehler gefunden: Die erste Fassung datierte nur die Annahme zurueck,
nicht den damals errechneten Termin, und bildete damit genau den Fall
nicht ab, um den es geht.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 11:56:08 +02:00
DogFatherGitandClaude Opus 5 6b818fd6f6 Abmelden meldet jetzt wirklich ab
Rueckmeldung: 'der abmelde button klappt auch nicht auf dem handy'. Die
Ursache war nicht das Handy -- der Fehler war auf beiden Geraeten
derselbe, auf dem Handy sieht man das kurze Aufblitzen nur eher.

Der Knopf beendete die Team-Sitzung, loeschte den Schluessel und lud neu.
Das Merkmal der Zugangswand blieb dabei im Browser stehen. Beim
Neuladen holt sich die Seite damit sofort wieder einen Ausweis -- man
war nach einer Zehntelsekunde erneut angemeldet, und der Knopf schien
nichts zu tun.

Jetzt werden BEIDE Sitzungen beendet (der Endpunkt dafuer gab es
laengst, die Verwaltung rief ihn nur nie auf), und man landet an der
Zugangswand statt in einer Codeeingabe. Das ist auch die ehrliche
Bedeutung des Wortes: Wer sich abmeldet, will draussen sein.

Beide Aufrufe sind einzeln abgesichert -- faellt einer aus, laeuft der
andere trotzdem. Ein halbes Abmelden waere schlimmer als keins: Man
hielte sich fuer abgemeldet und waere es nicht.

Geprueft mit 16 Pruefungen auf Computer und Handy, die messen, was
tatsaechlich hinausgeht statt ob sich etwas auf dem Schirm bewegt.
Dabei ein Artefakt im eigenen Test gefunden und behoben: Das
Vorbereitungsskript lief bei jeder Navigation und setzte den Schluessel
auf der Zugangswand gleich wieder -- zwei Pruefungen massen also sich
selbst.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 11:29:28 +02:00
DogFatherGitandClaude Opus 5 2ae77fc54f Verwaltung: eine Suche ueber alles, mit Tastatur bedienbar
Bisher gab es genau ein Suchfeld, und es durchsuchte nur die
Anfragenliste. Wer den Namen eines Kunden im Kopf hatte, musste raten,
in welchem Reiter er nachsehen muss: War das eine Anfrage, ein laufendes
Projekt, eine offene Rechnung? Bei drei Vorgaengen merkt man sich das,
bei dreissig nicht mehr.

Jetzt: Strg+K von ueberall, auf dem Handy der Lupenknopf oben. Ein
Aufruf durchsucht Anfragen, Kunden, Projekte und Zahlungen; jeder
Treffer traegt seinen Zusammenhang (Nummer, Kunde, Betrag, Liefertermin)
und fuehrt per Enter in den passenden Reiter, bei einer Anfrage direkt in
die Detailansicht.

Gebaut nach dem ARIA-Muster 'Combobox mit Listbox-Popup' aus den W3C
Authoring Practices -- nachgeschlagen, nicht aus dem Gedaechtnis:
role=combobox am Eingabefeld, aria-expanded, aria-controls,
aria-activedescendant, role=listbox, role=option mit aria-selected. Der
Fokus bleibt dabei im Eingabefeld, damit man weitertippen kann; die
Auswahl wandert ueber aria-activedescendant. Ohne diese Auszeichnung
waere ein Feld, das Vorschlaege einblendet, fuer einen Screenreader
stumm -- das sieht man beim Testen mit den Augen nie.

Vier Fallen ausdruecklich behandelt:
- Nicht bei jedem Tastendruck suchen (180 ms Wartezeit): 'Musterbau'
  haette sonst neun Abfragen ausgeloest, acht davon veraltet.
- Das Wettrennen der Antworten: Jede Abfrage bekommt eine laufende
  Nummer, nur die neueste darf zeichnen. Sonst ueberschreibt eine spaet
  eintreffende alte Antwort die neue.
- Leer, laedt und Fehler sind eigene Zustaende. Ein Kasten, der bei
  einem Serverfehler leer bleibt, sieht aus wie 'nichts gefunden'.
- LIKE-Sonderzeichen: '%' waere ein Platzhalter, '_' ein beliebiges
  Zeichen -- und Unterstriche stehen regelmaessig in E-Mail-Adressen.
  Der Fehler ist tueckisch, weil die Suche trotzdem Treffer liefert, nur
  die falschen.

Ausserdem zum dritten Mal dieselbe Spezifitaetsfalle gefunden: '.wd p'
schlug die eigene Regel, der Tastaturhinweis erschien in 17,9px statt
11,5px -- so gross wie der Inhalt, den er erklaert. Jetzt festgenagelt
durch eine Pruefung, die Groessenverhaeltnisse vergleicht.

Geprueft: 28 gegen eine echte Datenbank (darunter alle
LIKE-Sonderzeichen), 64 im Browser auf Computer und Handy inkl. der
ARIA-Vorgaben, des Wettrennens und des Fehlerzustands.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 23:13:23 +02:00
DogFatherGitandClaude Opus 5 659a1ee9ce Verwaltung fragt nicht mehr grundlos nach dem Zugangscode
Zwei Ursachen, die zusammenwirkten.

1. Nach einem Neuladen war die HERKUNFT des Schluessels vergessen. Der
   Code speicherte nur den Schluessel selbst, nicht ob er aus einer
   Codeeingabe oder aus dem Ausweis der Zugangswand stammt.

2. Weil die Herkunft fehlte, wurde vorsichtshalber 'code' angenommen.
   Als Vorsicht gedacht, mit unangenehmer Kehrseite: Ein Ausweis gilt nur
   15 Minuten. Als 'code' behandelt wurde er nie erneuert -- nach einer
   Viertelstunde kam der erste 401, und man landete in der Codeeingabe,
   obwohl alles in Ordnung war. Der Schutz 'eine Code-Sitzung wird nie
   durch einen Ausweis ersetzt' war damit ab dem ersten Neuladen ohnehin
   wirkungslos.

Die Herkunft steht jetzt neben dem Schluessel und ueberlebt ein
Neuladen. Fehlt sie -- etwa aus einer aelteren Sitzung --, bleibt es bei
der vorsichtigen Annahme 'code'.

Das behebt die Haelfte des Problems. Die andere Haelfte ist eine
Einstellung auf dem Server: Die beiden Dienste benutzen nicht dasselbe
WEBDESIGN_API_SECRET, weshalb jeder Ausweis abgelehnt wird. Der Kommentar
in server/.env.example sagt ausdruecklich, dass beide Werte exakt gleich
sein muessen. Das kann nur Filipe angleichen -- /home/dogiintern/ ist
fuer claudian gesperrt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 22:50:22 +02:00
DogFatherGitandClaude Opus 5 131bc8a755 Anfragen annehmen und ablehnen — mit laufender Zeit bis zur Übergabe
Annehmen war bisher nur ein Etikett: Man stellte den Status um, und es
passierte nichts. Kunde, Projekt, Aufgabenliste, Termin und Zugang musste
man danach von Hand in fünf Schritten nachbauen. Der alte Code gab das im
Kommentar selbst zu -- schlug der zweite von zwei Aufrufen fehl, stand der
Kunde schon in der Datenbank und man durfte es nicht noch einmal
versuchen.

Jetzt macht das EIN Aufruf, ganz oder gar nicht:
Kunde finden oder anlegen, Projekt anlegen, die komplette Aufgabenliste
des Pakets einsetzen, Liefertermin berechnen, Einladungslink erzeugen.
Der Kunde verfolgt ab diesem Moment alles in seinem Portal.

Die Zeit läuft wirklich:
- Eigenes Datumsfeld (termin_am) neben dem freien Text. Aus 'Mitte
  Oktober' kann man keine verbleibenden Tage rechnen.
- Werktage statt Kalendertage, inklusive luxemburgischer Feiertage. Die
  beweglichen werden über die Osterformel berechnet statt gepflegt --
  eine Liste ist im übernächsten Jahr lautlos falsch.
- Vorschlag je Paket (Onepager 10, Website 20, Shop 30 Werktage),
  überschreibbar vor dem Bestätigen.
- Verbleibende Zeit wird SERVERSEITIG gerechnet. Der Browser kennt die
  Feiertage nicht; zwei verschiedene Zahlen für denselben Termin wären
  schlimmer als gar keine.

Ablehnen mit Grund und vorbereiteter Absage in fünf Sprachen, Text
serverseitig erzeugt und vor dem Abschicken lesbar. Ein laufendes
Projekt lässt sich nicht nachträglich als Anfrage ablehnen.

Sechs Fehler dabei gefunden:
- Ein unbekanntes Paket hätte GAR KEINEN Termin bekommen statt des
  Ersatzwerts. Zwei Funktionen lasen dieselbe Tabelle, nur eine hatte
  einen Rückfallwert. Eine fehlende Zahl fällt nirgends auf.
- wd_anfragen hat keine Spalte 'firma' -- better-sqlite3 weist undefined
  ab, die ganze Annahme wäre gescheitert.
- Migrationen stehen in einer ausdrücklichen Liste; 0015 fehlte darin.
- datumKurz() wurde aufgerufen, gab es aber nicht. Der Fehler wäre erst
  NACH dem Anlegen aufgetreten.
- Der Installations-Hinweis lag fest über dem Annehmen-Knopf und machte
  ihn auf dem Handy untreffbar. Die Seite reserviert jetzt Platz dafür.
- 'richttermin' ist freier Text, lief aber durch einen Datumsformatierer:
  Der Kunde las 'Invalid Date' an der wichtigsten Stelle seines Projekts.

Geprüft: 49 Prüfungen der Terminrechnung gegen nachschlagbare Osterdaten
und Wochentage, 70 gegen eine echte Datenbank (darunter: zweiter Klick
legt nichts doppelt an, Abbruch mittendrin lässt NICHTS zurück, gesperrter
Kunde wird nicht still entsperrt), 42 im Browser auf Computer und Handy,
40 im Portal. Alle bestehenden Prüfungen weiter grün.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 22:44:08 +02:00
DogFatherGitandClaude Opus 5 37f1b4a8f5 Handy: Sicherheitsabstaende, Wisch-Reiter, zweispaltige Uebersicht
Der wichtigste Fund: env(safe-area-inset-*) stand bereits im CSS, lieferte
aber immer null -- viewport-fit=cover fehlte auf allen dreizehn Seiten.
Der Code sah richtig aus und tat nichts. Zusammen mit
apple-mobile-web-app-status-bar-style=black-translucent (schon gesetzt)
hiess das: installiert lag die Kopfzeile auf einem iPhone hinter Uhr und
Akkuanzeige.

Behoben:
- viewport-fit=cover auf allen 13 Seiten.
- Vier Sicherheitsabstaende zentral benannt statt an jeder Stelle
  einzeln geschrieben. Kopfzeile weicht der Statusleiste, Container dem
  seitlichen Notch im Querformat, Fusszeile der Gestenleiste.
- Der 'Ueberspringen'-Knopf des Vorspanns lag mit bottom:2rem praktisch
  AUF dem Entsperr-Strich (34px). Man haette die App verlassen statt
  uebersprungen.
- Eigener Zweig fuer den installierten Betrieb: kein Gummiband-
  Nachfedern (sieht in einer App nach einem Fehler aus), kein
  Installations-Hinweis.
- Die vier Sprungmarken auf der Rechtsseite waren 39px hoch -- fuenf
  unter dem Daumenmass. Ausgerechnet dort muss man zum Widerrufsrecht
  springen koennen.
- 'Waehle links einen Verlauf aus': Auf dem Handy gibt es kein links,
  die Liste steht darueber. Richtungswort entfernt.
- Sechs Reiter brauchten auf dem Handy drei Zeilen. Jetzt ein
  Wischstreifen mit Einrasten und Auslauf am Rand; der aktive Reiter
  wird herangeholt, wenn man ueber eine Kachel springt.
- Uebersicht zweispaltig statt zwoelf Zeilen untereinander: 2635px ->
  1924px. Eine Uebersicht, an der man vorbeiwischen muss, ist keine.

Geprueft mit 55 neuen Handy-Pruefungen auf iPhone 14 Pro, Pixel 7 und
320px Breite, jeweils im Browser und im installierten Zweig. Drei
Fehlalarme der eigenen Pruefung wurden begruendet ausgenommen (Honigtopf
bei left:-9999px, Eingabefeld in einer Beschriftung, Inline-Link im
Fliesstext -- WCAG 2.5.8 nimmt letztere ausdruecklich aus).

Zwei Selbstkorrekturen an der Pruefung dokumentiert: Die Emulation von
display-mode wirkt nicht (Chromium nimmt den Befehl an und ignoriert
ihn) -- ohne das waeren die zwoelf 'installiert'-Zeilen ein zweiter
Browser-Durchgang gewesen. Und die Messung gegen die Systemleisten mass
zuerst die Kastenkante statt der Inhaltskante und meldete elf korrekte
Seiten als fehlerhaft. Eine Selbstpruefung mit einem absichtlich falsch
gesetzten Knopf belegt jetzt, dass die Messung echte Fehler findet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 19:51:10 +02:00
DogFatherGitandClaude Opus 5 c1cb51884f Verwaltung: Übersicht als Startansicht, Reiter unter den Titel
Die Reiter standen neben dem Titel. Das las sich wie eine einzige lange
Zeile, in der der Titel nur der erste von sechs Knöpfen zu sein schien --
man sah nicht auf einen Blick, in welchem Bereich man war. Jetzt zwei
Zeilen: oben WO man ist, darunter WOHIN man kann.

Die Verwaltung öffnete bisher mit der Anfragenliste. Eine Liste ist eine
Ablage: Sie zeigt, WAS es gibt, nicht was zu TUN ist. Neu ist ein
Cockpit, das in drei Stufen antwortet -- was auf mich wartet, was beim
Kunden liegt, wie es ums Geld steht -- und dann laufende Projekte mit
Fortschritt sowie den Verlauf.

Zwei Grundsätze machen die Zahlen brauchbar: Getrennt nach 'wartet auf
mich' und 'wartet auf den Kunden' (zwölf offene Punkte sind entspannt,
wenn elf beim Kunden liegen). Und das ALTER färbt, nicht die Menge --
vier neue Anfragen sind kein Problem, eine seit sechs Tagen liegende
schon. Ein offener Widerruf ist immer rot, weil eine gesetzliche Frist
läuft.

Jede Kachel ist ein echter <button> und führt in den passenden Reiter.
Farbe ist nie der einzige Träger: Neben jedem farbigen Zustand steht der
Text ('älteste seit 8 Tagen').

Drei Fehler dabei gefunden und behoben:
- Der Titel wurde nur beim Klicken gesetzt. Frisch geladen zeigte die
  Seite das Cockpit, während darüber noch 'Projektanfragen' stand. Die
  Zuordnung Reiter->Titel liegt jetzt ausserhalb des Klick-Zuhörers.
- '.wd h2' überstimmte '.vw-ub-h': 44px Überschrift über 33px Zahl, die
  Seite las sich wie ein Plakat. Dieselbe Spezifitätsfalle wie früher
  bei '.wd a'.
- Eine Antwort mit ok:true aber ohne Inhalt riss die ganze Verwaltung
  mit. Wird jetzt abgefangen -- ein halber Server ist ein realistischer
  Fall.

Geprüft: 22 Serverprüfungen gegen eine echte Datenbank (inkl. vier
Fällen, die belegen, dass die Rechteprüfung wirklich greift), 56 im
Browser auf Computer und Handy, 69 im bestehenden Verwaltungstest,
0 Fundstellen im Design-/WCAG-Test, 5 im Farbsystemtest.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 19:21:12 +02:00
DogFatherGitandClaude Opus 5 4fe66e1e0e "Preise & Zeit" entfernt -- Inhalte in die Leistungen uebernommen
"leistungen und preis&zeit ist ja das gleiche, nimm die kategorie
preise&zeit komplett weg"

Stimmt bei den Paketen und Preisen: Beide Seiten zeigten dieselben
Zahlen. Zwei Orte fuer dieselbe Zahl sind eine Einladung, dass sie
irgendwann auseinanderlaufen -- und dann steht ein falscher Preis auf
der Seite, ohne dass es jemand merkt.

NICHT ALLES WAR DOPPELT

Vor dem Loeschen nachgesehen statt einfach geloescht. Zwei Abschnitte
gab es NUR dort:

  "Was den Preis bewegt"  -- beantwortet die Frage, die nach jeder
       Preisliste kommt: Warum kostet es bei mir mehr oder weniger?
       Ohne sie ist eine Preisspanne eine Behauptung.

  "Wie bezahlt wird"  -- Angebot, 30 % Anzahlung, Restbetrag, Betreuung.
       Rechtlich relevant und aus den AGB verlinkt.

Beide sind in die Leistungen umgezogen, samt ihrer 24 Textbausteine in
allen fuenf Sprachen. Waeren sie mitgeloescht worden, haette es niemand
sofort bemerkt -- und die Seite haette ab dann Preise genannt, ohne sie
zu erklaeren.

WEITERLEITUNG STATT 404

Die Adresse /webdesign/preise.html bleibt bedient und fuehrt auf die
Leistungen. Lesezeichen, alte Links aus Nachrichten und die installierte
App zeigen sonst ins Leere, und ein 404 auf einer Preisseite ist der
denkbar schlechteste erste Eindruck.

302 statt 301: Eine dauerhafte Weiterleitung merken sich Browser
hartnaeckig; sollte die Seite je zurueckkehren, muesste jeder Besucher
seinen Zwischenspeicher leeren.

EIN FEHLER BEIM EINBAU

Die Weiterleitung stand zuerst weiter unten in der Schranke, bei den
ohne Code erreichbaren Seiten. Dort haette sie nur fuer NICHT angemeldete
Besucher gegriffen -- wer angemeldet ist, passiert die Schranke vorher
mit next() und haette einen 404 auf die geloeschte Datei bekommen.
Genau die Person also, die die Seite am ehesten im Lesezeichen hat.
Jetzt steht sie ganz vorne, vor jeder Zugangspruefung.

GEPRUEFT: 10 Pruefungen -- beide uebernommenen Abschnitte erscheinen in
allen fuenf Sprachen, kein unuebersetzter Schluessel sichtbar. Kein
Verweis auf die alte Seite blieb irgendwo stehen (ablauf.html hatte
einen, der ist umgebogen). Gestaltung 0 Befunde, Textkodierung sauber,
Designsystem 5.

Versionsstempel und Cache auf v20.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 19:00:49 +02:00
DogFatherGitandClaude Opus 5 d6e75ce56a Kacheln: Licht folgt dem Zeiger, Rand glueht mit
"ich will das die kacheln richtig speziell sind und modern ... dass viel
mehr auffaellt und doch modern und profissionell"

Die Kacheln hatten Verlaufsrand, Verlaufsflaeche, Schatten und
Lichtkante -- alles richtig, alles STATISCH. Was fehlte, war das Gefuehl
von Material: eine Flaeche, die auf Licht reagiert.

ZWEI EBENEN

Ein weicher Lichtfleck folgt dem Zeiger ueber die Flaeche. Und -- der
Teil, der wirklich auffaellt -- der RAND glueht an derselben Stelle auf
und verlaeuft nach beiden Seiten aus.

Das geht ohne zusaetzliches Element: Die Kachel hat bereits zwei
Hintergrundebenen (Flaeche padding-box, Verlaufsrand border-box). Beim
Ueberfahren bekommt die Randebene einen Lichtpunkt an der Zeigerstelle.
Der Rand ist nur einen Pixel breit -- genau dort laesst sich Licht am
staerksten zeigen, ohne kitschig zu werden. Ein Lichtfleck OHNE
mitleuchtende Kante wirkt wie ein Fleck AUF der Kachel; mit ihr wirkt
die ganze Kachel beleuchtet.

Die Flaeche selbst bleibt beim Ueberfahren unveraendert -- sonst wuerde
der Text flackern.

VIER ENTSCHEIDUNGEN GEGEN BILLIGE OPTIK

Die Farbe folgt der Kachel: blaue leuchten blau, lila lila. Sonst braeche
der Effekt die Farbordnung, die ueberall sonst Bedeutung traegt.

Nur bei echtem Zeiger (hover: hover, pointer: fine). Auf dem Handy gibt
es keinen; dort bliebe der Lichtfleck an einer zufaelligen Stelle kleben.

Aus bei prefers-reduced-motion. Auch wenn sich nichts bewegt: Etwas, das
dem Zeiger folgt, ist Bewegung.

Erste Fassung war mit 13 % zu zurueckhaltend -- am Screenshot geprueft
und auf 22 % angehoben. "Man soll es spueren" ist richtig, aber es muss
auch ankommen.

SPARSAM GEBAUT

EIN Zuhoerer am Dokument statt einem je Kachel -- sonst muesste jede
nachgeladene Kachel einzeln nachgeruestet werden. Geschrieben wird erst
im naechsten Bildaufbau: Der Zeiger meldet sich bis zu tausendmal je
Sekunde, der Bildschirm zeichnet sechzigmal; ohne Drosselung rechnet der
Browser neunhundertmal umsonst.

Der Inhalt bekommt z-index 1. Ohne das legt sich der absolut gesetzte
Lichtfleck ueber den Text -- absolut positionierte Elemente malen ueber
Fluss-Inhalt, auch wenn sie im Quelltext davor stehen. Das Ergebnis waere
ein Schleier auf der Schrift, den man erst beim Vergleich zweier Kacheln
bemerkt. Eigens geprueft.

GEPRUEFT: 7 Pruefungen fuer den Effekt selbst -- unsichtbar im
Ruhezustand, erscheint beim Ueberfahren, folgt dem Zeiger auf 6 Pixel
genau, Inhalt liegt darueber, lila Kacheln leuchten lila, bei
Bewegungsempfindlichkeit vollstaendig abgeschaltet.

Gestaltung 0 Befunde, Designsystem 5, Verwaltung 69, Portal 32.

Versionsstempel und Cache auf v19.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 18:53:05 +02:00
DogFatherGitandClaude Opus 5 e495542fe5 Typografie: Lesebreite begrenzt -- 105 zu breite Absaetze behoben
Gemessen ueber alle Seiten: 105 von 200 Textabsaetzen waren zu breit,
der schlimmste mit 151 Zeichen pro Zeile.

WARUM DAS ZAEHLT

Beim Zeilensprung muss das Auge zurueck an den Zeilenanfang finden. Je
laenger die Zeile, desto haeufiger landet es in der falschen -- man
liest eine Zeile doppelt oder ueberspringt eine und merkt es erst zwei
Saetze spaeter. Der Text ist dann nicht "schwer", er ist schlecht
gesetzt.

Bewaehrt sind 45 bis 75 Zeichen. Das ist der aelteste und
bestbelegte Grundsatz der Typografie ueberhaupt -- jedes ordentlich
gesetzte Buch haelt sich daran.

Jetzt 68 Zeichen fuer Fliesstext, 60 fuer Kleingedrucktes. Die Einheit
ist ch statt Pixel: Damit haengt die Breite an der SCHRIFTGROESSE.
Kleingedrucktes bekommt automatisch einen schmaleren Block, grosse
Schrift einen breiteren -- beide landen bei aehnlich vielen Zeichen.

max-width macht nie etwas breiter. In schmalen Karten und Spalten
aendert sich also nichts; die Regel greift nur dort, wo eine Zeile
wirklich ueber das Lesbare hinauswaechst. Ergebnis: von 105 auf 0.

DREI FEHLER BEIM EINBAU, ALLE VON DER MESSUNG GEFUNDEN

1. Zentrierter Text stand ploetzlich links. Eine Breitenbegrenzung
   zentriert den KASTEN nicht mit -- die Schrift war mittig, ihr Kasten
   klebte am linken Rand, mit 384 Pixeln Luft rechts und null links.
   Gefunden, weil die Pruefung den Abstand links mit dem rechts
   VERGLEICHT, statt nur zu zaehlen, ob zentriert ist.

2. Meine erste Regel deckte nur den Fall ab, dass das ELTERNELEMENT
   zentriert -- nicht den, dass der Absatz es selbst tut.

3. Und dann blieb einer uebrig, bei dem beides stimmte. Ursache: ein
   Inline-Stil "margin:1.2rem 0 0". Die Kurzschreibweise setzt links und
   rechts hart auf 0 und schlaegt jede Stilvorlage. 11 solche Stellen
   ueber fuenf Seiten auf margin-top umgestellt -- gemeint war ohnehin
   immer nur der Abstand nach oben.

Ausnahmen bewusst gesetzt: Nachrichtenblasen tragen ihre Breite schon
selbst, Tabellenzellen und Beschreibungslisten richten sich nach ihrer
Spalte, die Fusszeile ist mehrspaltig und ohnehin schmal.

ALLES GRUEN: Gestaltung 0 Befunde, Designsystem 5, Verwaltung 69,
Portal 32, Widerruf 58, Anfragedetail 20.

Versionsstempel und Cache auf v18.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 18:47:34 +02:00
DogFatherGitandClaude Opus 5 10a075cc33 Gestaltung: Hoehensystem -- mehrschichtige Schatten und Lichtkante
Bis hierher trug jede Karte EINEN Schatten. Das ist solide und genau der
Grund, warum eine Oberflaeche "ordentlich" statt "teuer" wirkt.

Echtes Licht erzeugt zwei Dinge gleichzeitig: einen engen, dunklen
Kontaktschatten an der Kante -- er sagt dem Auge, dass etwas AUFLIEGT --
und einen weiten, weichen Umgebungsschatten, der sagt, wie HOCH es
darueber schwebt. Nur einen von beiden zu setzen sieht aus wie ein
Aufkleber; beide zusammen ergeben einen Gegenstand.

Dazu die LICHTKANTE: eine 1 Pixel hohe, sehr schwache weisse Linie an der
Oberkante. Sie tut so, als kaeme das Licht von oben und braeche sich an
der Kante. Das ist das meistuebersehene Detail in dunklen Oberflaechen
und dasjenige mit der groessten Wirkung -- ohne sie wirkt eine Karte wie
ein Loch im Hintergrund, mit ihr wie ein Stueck Material.

Drei Stufen, mehr braucht es nicht: ruhende Flaechen, hervorgehobene
Flaechen, Schwebendes. Dazu eine vierte, umgekehrte fuer Eingabefelder:
Die liegen nicht AUF der Flaeche, sie sind hineingeschnitten -- oben
dunkel, unten hell.

Weil es die gemeinsamen Bausteine sind, wirkt es sofort auf allen 13
Seiten: Hauptseite, Portal und Verwaltung.

Dazu Feinheiten, die einzeln niemand bemerkt und in Summe den
Unterschied machen: Uebergaenge auf 160-180 ms verkuerzt (alles darueber
wirkt beim Ueberfahren traege), eigene Rueckmeldung beim Druecken,
optische Laufweitenkorrektur fuer grosse Ueberschriften (-.028em bei h1;
das Auge sieht bei 48px mehr Weissraum zwischen Buchstaben als bei 16px),
text-wrap: balance gegen einzelne Woerter in der letzten Zeile.

EIN FEHLER BEIM EINBAU, GEMESSEN STATT UEBERSEHEN

Nach dem ersten Durchgang hatten die Karten ihre Lichtkante, der
Hauptknopf nicht. Grund: Die Grundregel lautet
".wd .wd-btn--haupt, .wd-btn--haupt" -- der erste Teil zaehlt ZWEI
Klassen. Mein Nachtrag zaehlte eine und verlor, obwohl er spaeter steht.
Dieselbe Falle wie am 22.08.2026 bei ".wd a" gegen ".wd-btn--haupt".
Aufgefallen nur, weil die Pruefung die Lichtkanten ZAEHLT.

PRUEFWERKZEUGE ANGEPASST

diagnose.html ist jetzt ausgenommen. Sie traegt bewusst alles fest in
sich -- eigene Farben, keine Uebersetzung -- weil sie funktionieren muss,
wenn genau das kaputt ist, was sie untersucht. Sie an den Regeln der
eigentlichen Seite zu messen erzeugte zwei Meldungen, die man auf Dauer
wegsieht. Und irgendwann sieht man dann auch eine echte weg.

ALLES GRUEN: Gestaltung 0 Befunde (12 Seiten x 5 Sprachen gegen
WCAG 2.2 AA), Designsystem 5, Verwaltung 69, Portal 32.

Versionsstempel und Cache auf v17.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 18:37:17 +02:00
DogFatherGitandClaude Opus 5 a964a078f0 Zahlungen: Verwaltung sticht Serverdatei -- der Schalter wirkt jetzt
Filipes Bildschirm zeigte alles, was noetig war, um es zu erkennen:

  Betriebsart: hinterlegt (auf dem Server) . 7 Zeichen
  Client ID:   hinterlegt . 82 Zeichen
  Secret:      hinterlegt . 80 Zeichen
  Fehler:      "PayPal hat die Anmeldung abgelehnt."

7 Zeichen sind "sandbox". Der Wert kam aus der .env, und die hatte
Vorrang. Sein Klick auf "Echtbetrieb" blieb wirkungslos -- seine echten
Zugangsdaten wurden gegen den TESTSERVER von PayPal geprueft, der sie
zwangslaeufig ablehnt.

Die Fehlermeldung zeigte dabei auf die Zugangsdaten ("stimmen Client ID
und Secret nicht zusammen") und damit in die voellig falsche Richtung.

DER ENTWURFSFEHLER

Ich hatte der .env bewusst Vorrang gegeben, damit ein Fehlgriff im
Formular keine funktionierende Servereinstellung aushebelt. Das klingt
vorsichtig und war falsch:

Ein Formular mit Schaltern, die nichts bewirken, ist schlimmer als gar
kein Formular. Es behauptet eine Wirkung, die es nicht hat, und schickt
bei der Fehlersuche in die Irre.

Der Sinn dieser Ablage ist gerade, dass die Werte OHNE SSH gesetzt
werden koennen. Dann muss das, was dort steht, auch gelten.

Ungefaehrlich, weil diese Werte ausschliesslich der Webdesign-Bereich
liest. Das DogiCrew-Supporter-Abo hat sein eigenes Modul und liest
weiter direkt aus der Umgebung -- ein Eintrag hier kann es nicht
abschalten. Eigens geprueft.

AUCH DIE ANZEIGE WAR UNEHRLICH

Sie sagte nur "hinterlegt (auf dem Server)" und verschwieg, dass genau
dieser Wert die eigene Eingabe ueberstimmt. Jetzt steht dort, welche
Quelle GILT -- "aus der Serverdatei" oder "hier eingetragen" -- und bei
doppelter Belegung zusaetzlich "Serverwert wird nicht benutzt".

GEPRUEFT: 16 Pruefungen auf dem Server, alle bestanden. Darunter der
genaue Fall: Serverdatei sagt sandbox, Formular sagt live -> istLive()
wird WAHR. Und der Rueckweg: Feld leeren -> Serverwert greift wieder.

Beim Testen noch ein Werkzeugfehler behoben: Der Absturz von
better-sqlite3 beim Beenden verschluckte die gesamte gepufferte
Ausgabe -- der Test lief durch und sah aus, als waere er nie gestartet.
Jetzt schreibt er unumgepuffert.

Versionsstempel und Cache auf v16.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 18:12:50 +02:00
DogFatherGit 14488d6df2 Test: unumgepufferte Ausgabe (Absturz beim Beenden verschluckte sie) 2026-08-23 18:11:47 +02:00
DogFatherGit 779fb94c6b Test fuer den Quellen-Vorrang 2026-08-23 18:10:47 +02:00
DogFatherGit ebe10b9c49 WIP Vorrang umgedreht (Test folgt auf dem Server) 2026-08-23 18:09:39 +02:00
DogFatherGitandClaude Opus 5 d870a9c1d7 PayPal: falsche Angabe zur Webhook-ID berichtigt, Abo-Plan-Weg ergaenzt
Filipe: "webhook id faengt bei mir nicht mit w an und wo finde ich plan
fuer betreung"

Er hat recht, ich hatte unrecht. An der Quelle nachgeprueft:

  Webhook-ID:   0NH55953DH663215D        -- OHNE Vorsilbe, ~17 Zeichen
  Ereignis-ID:  WH-3F562076HD293871E-... -- DIE beginnt mit WH-

Meine Anleitung und der Hinweistext im Formular behaupteten beide
"beginnt mit WH-". Wer sich daran haelt, sucht an der falschen Stelle
oder traegt eine Ereignis-Kennung ein -- und die Signaturpruefung
scheitert dann bei der ersten echten Zahlung, mit einer Meldung, die
nicht auf die Ursache zeigt.

Beide Stellen berichtigt, in der Anleitung mit ausdruecklichem Hinweis,
dass dort vorher etwas Falsches stand. Wer sie schon gelesen hat, soll
den Widerspruch erklaert bekommen und nicht stillschweigend eine andere
Fassung vorfinden.

ABO-PLAN: Der Grund fuer die Frage ist ein echter Stolperstein --
Abo-Plaene werden NICHT im Entwicklerbereich angelegt, sondern im
normalen Geschaeftskonto. Unter developer.paypal.com sucht man vergeblich.

Jetzt mit direkter Adresse (paypal.com/billing/plans), dem Weg ueber das
Menue und dem Schritt, den man am ehesten vergisst: den Plan nach dem
Speichern auch AKTIVIEREN. Ein gespeicherter, aber nicht aktivierter
Plan sieht fertig aus und funktioniert nicht.

Ausserdem klargestellt, dass dieser ganze Schritt entfaellt, wenn kein
monatliches Abo verkauft wird -- Anzahlung und Restbetrag laufen ohne.

Versionsstempel und Cache auf v15.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 18:04:55 +02:00
DogFatherGitandClaude Opus 5 9421bd5cfe Verwaltung: Endlosschleife bei abgewiesenem Ausweis behoben
DIE URSACHE FUER "die ganze seite haengt"

In ladeAnfragen stand seit Tagen dieser Code:

    if (!ausweisVersucht) {
      ausweisVersucht = true;
      var frisch = await ausweisHolen();
      if (frisch) {
        token = frisch; tokenSchreiben(token);
        ausweisVersucht = false;     // <-- VOR dem Neuversuch geloest
        return ladeAnfragen();       // <-- und ruft sich selbst auf
      }
    }

Die Sperre wird zurueckgesetzt, BEVOR der neue Versuch laeuft. Solange
der Ausweis akzeptiert wurde, fiel das nie auf. Weist der Server ihn
dagegen ab, dreht es sich unbegrenzt: Ausweis holen, 401, Ausweis holen,
401 -- ohne Fehlermeldung, ohne Anmeldemaske, nur ein Ladehinweis, der
zehn Minuten stehen bleibt. Die schlimmste Sorte Fehler: Sie sieht aus
wie Langsamkeit.

WARUM DER AUSWEIS ABGEWIESEN WIRD

Die Diagnose mit echtem Ausweis zeigte es eindeutig:
  Ausweis erhalten in 23 ms
  Einstellungen abrufen -> 401, {"ok":false,"error":"Kein Zugriff."}

Die Zugangswand stellt also aus, der interne Dienst lehnt ab. Beide
benutzen nicht dasselbe WEBDESIGN_API_SECRET. Auf dem oeffentlichen
Server ist es gesetzt (Fingerabdruck e7d63573174f661a); der interne ist
fuer mich nicht lesbar -- Filipe prueft das mit einem Befehl, der nur
den Fingerabdruck ausgibt, nie den Wert.

UND EIN FEHLER, DEN ICH SELBST EINGEBAUT HATTE

Die Erneuerung aus v12/v13 ersetzte bei jedem 401 den Schluessel durch
einen Ausweis -- auch dann, wenn er aus der Anmeldung mit dem
Zugangscode stammte. Bei untauglichem Ausweis wurde daraus ein
Totalausfall: Alle zehn Minuten ersetzte sie eine FUNKTIONIERENDE
Sitzung durch eine kaputte.

Eine Reparatur, die den Normalfall verschlechtert, ist keine.

Jetzt fuehrt die Seite mit, WOHER der Schluessel stammt. Eine
Code-Sitzung wird nie durch einen Ausweis ersetzt, und die vorsorgliche
Erneuerung laeuft fuer sie gar nicht erst. Nach einem Neuladen gilt ein
vorhandener Schluessel vorsichtshalber als Code-Sitzung -- lieber einmal
zu viel nach dem Code fragen als eine laufende Sitzung zerstoeren.

Die Erneuerung sitzt jetzt an EINER Stelle (api()) mit genau einem
Neuversuch. Die zweite, aeltere Fassung in ladeAnfragen ist raus; zwei
Mechanismen, die sich gegenseitig den Schluessel ueberschreiben, waren
ein Teil des Problems.

GEPRUEFT mit genau dieser Lage -- Zugangswand stellt aus, interner
Dienst lehnt ab:
  2 Ausweis-Abrufe in 3 Sekunden statt unbegrenzt
  abgewiesener Ausweis fuehrt zur Codeeingabe statt zum Haengen
  Anmeldung mit Code oeffnet die Verwaltung
  sechs Reiterwechsel, null Rauswuerfe, null Ausweis-Abrufe
  Zahlungen-Kasten zeigt Inhalt

Verwaltung weiterhin 69 von 69. Versionsstempel und Cache auf v14.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 17:36:34 +02:00
DogFatherGit 4ad99b5c37 Diagnose: Volltest mit echtem Ausweis
Ein 401 ohne Anmeldung beweist nur, dass die Adresse existiert -- nicht,
dass die Antwort auch bei ANGEMELDETEM Zugriff kommt. Genau dort blieb
die Verwaltung stehen.

Die Diagnose geht jetzt denselben Weg wie die Verwaltung: Ausweis von
der Zugangswand holen, damit die Einstellungen abrufen, Zeit messen. Mit
eigenem Zeitlimit von 12 Sekunden, damit die Diagnose nicht selbst
haengt, wenn die Adresse haengt -- ein Pruefwerkzeug, das am selben
Problem scheitert, ist wertlos.

Damit ist der Befund eindeutig statt vermutet: entweder 'Erfolgreich in
X ms', oder eine Statusnummer samt Antworttext, oder 'KEINE Antwort
innerhalb von 12 Sekunden'.
2026-08-23 17:30:23 +02:00
DogFatherGitandClaude Opus 5 2ec935ccc6 Verwaltung: Ausweis vorsorglich erneuern statt erst nach dem Fehlschlag
"wenn ich von postfach auf projekte oder so gehe da werd ich immer wieder
mien zugangscode gefragt ... ich will nur den zugangscode anfrage wenn
ich auf die seite will und fertig"

Die Wiederholung bei 401 (v12) rettet zwar jeden Fall, greift aber erst,
NACHDEM eine Anfrage abgewiesen wurde: ein unnoetiger Umlauf, und
solange steht ein Ladehinweis auf dem Schirm.

Jetzt laeuft der Ausweis gar nicht erst ab. Er gilt 15 Minuten, erneuert
wird alle 10 -- mit Abstand, damit ein langsamer Netzzugang nicht ins
Zeitfenster hineinlaeuft. Im Hintergrund pausiert die Erneuerung, das
waere Verschwendung.

Dazu beim Zurueckkommen ins Fenster: War der Rechner zwischendurch zu,
ist der Ausweis mit Sicherheit abgelaufen. Dann soll der erste Klick
sofort sitzen statt ueber einen Fehlschlag zu gehen. Beim schnellen Hin-
und Herwechseln zwischen zwei Fenstern passiert nichts -- unter einer
Minute wird nicht erneuert.

Die Codeeingabe erscheint jetzt nur noch in genau zwei Faellen: beim
ersten Betreten der Seite und wenn die Zugangssitzung wirklich abgelaufen
ist (8 Stunden oder Seite geschlossen). Genau so war es gewuenscht.

GEPRUEFT: sieben Reiterwechsel hintereinander -- postfach, projekte,
kunden, zahlungen, anfragen, postfach, zahlungen -- und vor JEDEM wurde
der Ausweis kuenstlich fuer ungueltig erklaert. Null Codeabfragen, die
Verwaltung blieb offen, der Inhalt erschien. Verwaltung weiterhin
69 von 69.

Versionsstempel und Cache-Name auf v13.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 17:28:35 +02:00
DogFatherGitandClaude Opus 5 bd9051bfb7 Verwaltung: Ausweis erneuert sich selbst statt nach dem Code zu fragen
Rueckmeldung: "wieso werd ich auch immer meinen zugangscode gefragt wenn
ich die kategorie wechseln tue das ist schwachsin."

Er hat recht, und es war ein echter Fehler.

DIE URSACHE

Der Ausweis fuer die interne Schnittstelle (/webdesign/api-ausweis) gilt
15 Minuten. Danach antwortete jede Anfrage mit 401, und die Verwaltung
tat das Haerteste, was moeglich ist: Token verwerfen und Codeeingabe
zeigen -- beim blossen Wechsel eines Reiters.

Doppelt falsch:

  Die ZUGANGSSITZUNG selbst gilt 8 Stunden. Der Code war also gar nicht
  noetig; ein frischer Ausweis haette genuegt und war jederzeit
  abrufbar.

  Ein Rauswurf mitten in der Arbeit ist die haerteste denkbare Reaktion
  auf ein Problem, das sich unsichtbar loesen laesst. Wer gerade ein
  Angebot beziffert hat, verliert damit seinen Platz.

DIE LOESUNG

Bei 401 wird EINMAL ein neuer Ausweis geholt und die Anfrage wiederholt.
Nur wenn auch das scheitert, kommt die Codeeingabe -- dann ist die
Sitzung wirklich abgelaufen.

Das Wiederholen ist auch bei Absendungen unbedenklich: Eine mit 401
abgewiesene Anfrage hat serverseitig nichts bewirkt.

Alle gleichzeitig wartenden Aufrufe teilen sich EINEN Erneuerungsversuch.
Ohne das holt beim Oeffnen eines Reiters mit drei Abfragen jede ihren
eigenen Ausweis -- drei statt einem.

GEPRUEFT: 5 Pruefungen mit kuenstlich abgelaufenem Ausweis. Der
Reiterwechsel fragt NICHT mehr nach dem Code, der Inhalt erscheint
trotzdem, genau EIN neuer Ausweis wird geholt, und der naechste Wechsel
laeuft ebenfalls durch. Ist dagegen die Zugangssitzung selbst abgelaufen,
erscheint die Codeeingabe weiterhin -- das soll sie dann auch.

Verwaltung weiterhin 69 von 69.

Versionsstempel und Cache-Name auf v12.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 17:23:19 +02:00
DogFatherGitandClaude Opus 5 a5cd4bdcbd Diagnose: Normalzustaende nicht mehr als Befund melden
Filipes Diagnose zeigte zwei "!", obwohl alles in Ordnung war:

  ! Service Worker aktiv       -> 1 registriert
  ! Zwischenspeicher           -> dogfather-webdesign-v11
  ! Anmeldung Verwaltung       -> keiner vorhanden

Alle drei sind der NORMALFALL:

Ein aktiver Service Worker ist gewollt -- ohne ihn liesse sich die Seite
nicht als App installieren. Das war ausdruecklicher Wunsch.

Der Zwischenspeicher enthielt v11 -- also genau die Fassung, die der
Server ausliefert. Die erste Fassung meldete JEDEN gefuellten
Zwischenspeicher als auffaellig, auch den topaktuellen. Das ist
Panikmache, kein Befund.

Die Anmeldung gilt je Tab. In einem frisch geoeffneten Tab ist
zwangslaeufig keine da -- und sie SOLL beim Schliessen ohnehin
verschwinden, das war eine ausdrueckliche Vorgabe.

Ein Pruefwerkzeug, das den Normalzustand anmahnt, ist schlimmer als
keines: Es schickt einen auf die Suche nach einem Fehler, den es nicht
gibt -- und wenn dann einmal ein echter kommt, sieht er genauso aus wie
das Rauschen davor.

Jetzt liest die Diagnose die erwartete Fassung aus dem
Service-Worker-Skript auf dem Server und VERGLEICHT. Nur eine
Abweichung ist ein Befund.

GEPRUEFT mit beiden Faellen: bei v11 sechsmal gruen, bei kuenstlich
gesetztem v3 ein "!" mit beiden Fassungsnummern im Klartext.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 17:18:05 +02:00
DogFatherGitandClaude Opus 5 b9f6829a63 Diagnoseseite: Text landete im Statuskreis statt daneben
Filipes Screenshot zeigte den Beschreibungstext als schmale
Buchstabensaeule ueber die halbe Seite laufen. Die Ursache ist eindeutig
und mein Fehler:

  d.innerHTML = '<span class="mark">✓</span><div><b></b><span></span></div>';
  d.querySelector("span").textContent = text;

querySelector liefert den ERSTEN passenden span -- und das war der
Statuskreis, nicht das Textfeld darunter. Der gesamte Beschreibungstext
wurde also in einen 26 Pixel breiten Kreis geschrieben und lief dort
heraus.

Zwei Regeln machten es schlimmer:
  .zeile span { ... }   traf ebenfalls BEIDE spans
  fehlendes min-width:0 verhinderte, dass die Textspalte schrumpfen darf

Jetzt wird die Zeile Element fuer Element aufgebaut, mit direkten
Verweisen statt Suche -- da gibt es nichts zu verwechseln. Die
CSS-Regeln zielen auf eigene Klassen (.inhalt, .text) statt auf den
Elementnamen, der Kreis ist auf feste 26px genagelt und schneidet
ueberzaehligen Inhalt ab, statt die Seite aufzubrechen.

Auch die feste Platzhalterzeile im HTML nutzte noch den alten Aufbau und
haette denselben Fehler gezeigt, sobald sie sichtbar wird.

GEPRUEFT im Browser: alle sechs Zeilen, jeder Kreis exakt 26x26, Text
danebenstehend. Zusaetzlich als Screenshot angesehen -- bei einem
Anzeigefehler ist Messen allein nicht genug, denn genau das hatte die
erste Fassung ja auch bestanden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 17:13:34 +02:00
DogFatherGitandClaude Opus 5 dd4010e75d Diagnoseseite: findet und behebt haengende Zwischenspeicher
Rueckmeldung: "garnichts laedt in der verwaltungsseite".

Geprueft statt geraten -- die Serverseite ist in Ordnung:
  Dienst aktiv, keine Fehler im Protokoll
  CORS-Vorabanfrage 204 mit allow-origin
  echte Anfrage 401 (korrekt ohne Anmeldung), Antwortzeit unauffaellig
  ausgelieferte Seite enthaelt den neuen Code

Damit bleibt fast nur der Browser: ein alter Service Worker, der
veraltete Dateien ausliefert. Er ueberlebt ein normales Neuladen, und
niemand kann ihn ohne Entwicklerwerkzeuge sehen.

webdesign/diagnose.html prueft sechs Dinge und sagt im Klartext, welches
davon klemmt: Service Worker aktiv? Zwischenspeicher gefuellt? Server
erreichbar und wie schnell? Kennt der Server die neuen Adressen
(404 = Server veraltet)? Liegt eine Anmeldung vor? Und wird die AKTUELLE
Gestaltungsdatei ausgeliefert -- erkennbar an einem Merkmal, das es erst
seit heute gibt.

Ein Knopf meldet den Service Worker ab, loescht die Zwischenspeicher und
laedt die Verwaltung mit Zeitstempel neu. Der Text sagt ausdruecklich,
dass Anmeldung und Daten unberuehrt bleiben -- sonst traut sich niemand
zu klicken.

ZWEI ENTSCHEIDUNGEN

Die Seite laedt KEINE externen Dateien, alles steht inline. Sie muss
funktionieren, wenn genau das kaputt ist, was sie untersucht -- eine
Diagnoseseite, die an derselben veralteten CSS-Datei scheitert, ist
wertlos.

Sie ist von der Zugangswand ausgenommen, wie Widerruf und Rechtstexte.
Gebraucht wird sie genau dann, wenn etwas klemmt; dann darf nicht
ausgerechnet die Zugangswand davorstehen. Sie enthaelt keine Kunden-
oder Projektdaten, sondern prueft nur den eigenen Browser und meldet
Statusnummern.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 17:10:54 +02:00
DogFatherGitandClaude Opus 5 d68d10d002 Zahlungen: Ladefehler sichtbar machen statt ewig "Wird geladen"
Rueckmeldung 23.08.2026: "da steht das aber passiert nichts" -- der
Reiter Zahlungen zeigte in beiden Kaesten dauerhaft "Wird geladen...".

Geprueft statt vermutet: Der Server antwortet (401 auf unangemeldete
Anfragen, Webhook 400), die ausgelieferte Seite enthaelt die Funktion,
und lokal nachgestellt laeuft der Ablauf sauber durch. Die Anfrage
bricht nach 15 Sekunden von selbst ab.

Der eigentliche Mangel liegt woanders und ist meiner: Ein Ladehinweis
ohne Ende ist die schlechteste aller Rueckmeldungen. Er sieht aus wie
Arbeit und ist doch nur Stillstand -- man kann nicht unterscheiden, ob
die Anfrage laeuft, fehlgeschlagen ist oder das Skript gar nicht
angesprungen ist. Und wenn der Fehler dann kommt, verschwindet er in
einer Meldung am Bildschirmrand.

DREI AENDERUNGEN

Jeder Fehler wird im Kasten selbst angezeigt, mit STATUSNUMMER im
Klartext ("Konnte nicht geladen werden (Status 403)"). Die kann Filipe
mir nennen, ohne die Entwicklerwerkzeuge zu oeffnen -- 403 heisst etwas
voellig anderes als 500 oder "keine Verbindung", und ohne diese Zahl
raet man.

Dazu ein Knopf "Nochmal versuchen". Bei einem Aussetzer im Mobilfunk ist
das der ganze Unterschied zwischen "geht nicht" und "geht doch".

Die statischen "Wird geladen..."-Texte im HTML sind raus. Der Kasten ist
jetzt leer, bis das Skript ihn fuellt -- und die Ladetexte lauten anders
als vorher. Bleibt spaeter der alte Text stehen, weiss man sofort: Die
Funktion ist nie angelaufen. Das ist eine Aussage, "Wird geladen" war
keine.

Auch die Zahlungsliste verschluckt ihren Fehler nicht mehr. Sie leerte
den Kasten stillschweigend -- ein leerer Bereich ohne Erklaerung ist
nicht weniger verwirrend als ein haengender Ladehinweis.

GEPRUEFT: alle vier Zustaende im Browser nachgestellt -- Serverfehler
500, kein Zugriff 403, Netzausfall und Erfolgsfall. In den ersten drei
erscheint Klartext samt Wiederholen-Knopf, im vierten der normale
Inhalt. Verwaltung weiterhin 69 von 69.

Versionsstempel und Cache-Name auf v11.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 17:07:35 +02:00
DogFatherGitandClaude Opus 5 497c576d1c Anleitung auf den neuen Weg umgestellt -- ohne Konsole
Die Anleitung war nach dem Einbau des Formulars veraltet und haette
Filipe weiter in die Konsole geschickt.

Schritt 0 (nachsehen, was schon da ist) ging ueber SSH und grep. Das
zeigt jetzt die Verwaltung selbst an: "hinterlegt (auf dem Server)",
"hinterlegt" oder "fehlt". Kein Terminal noetig, um zu erfahren, was
noch fehlt.

Schritt 6 war "nano .env + systemctl restart". Jetzt: Verwaltung ->
Zahlungen -> Felder ausfuellen -> Speichern. Mit dem Hinweis, dass ein
leeres Feld "nicht anfassen" bedeutet und nicht "loeschen" -- ohne den
traut sich niemand, ein einzelnes Feld nachzutragen.

Schritt 7 ist neu: der Knopf "Verbindung testen" mit einer Tabelle, was
die drei moeglichen Meldungen bedeuten.

Beim Umstellen ist mir Schritt 8 (der 1-Euro-Test) aus der Datei
gefallen -- der Ersetzungsbereich reichte zu weit. Wieder eingesetzt und
gleich vervollstaendigt: jetzt mit Testkunde anlegen, Einladungslink im
eigenen Browser oeffnen und Aufraeumen danach. Vorher stand dort nur
"Zahlung erzeugen und bezahlen", was den halben Weg ausliess.

Auch die Formulierung "Beide brauche ICH" korrigiert -- ich brauche die
Werte gar nicht, er traegt sie selbst ein.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 16:53:52 +02:00
DogFatherGitandClaude Opus 5 6a2aa13dd8 PayPal ohne Konsole einrichten -- Formular in der Verwaltung
Auf die Frage "soll ich das Secret hier reinschicken?" ist die Antwort
nein: Es stuende dauerhaft im Gespraechsverlauf, und schreiben koennte
ich es trotzdem nicht (kein Zugriff auf /home/dogiintern, sudo nur fuer
apt/systemctl/docker). Risiko ohne Nutzen.

Der Fehler lag aber bei mir: Ich hatte SSH-Befehle als Loesung angeboten.
Filipe soll gar nicht in die Konsole. Jetzt traegt er die Werte in seiner
Verwaltung ein.

AUFBAU

lib/webdesign-geheimnisse.js legt die Werte verschluesselt in
app_settings ab -- derselbe AES-GCM-Weg wie fuer die Zugangscodes des
Universe. Wer die Datenbankdatei in die Haende bekommt, etwa ueber eine
alte Sicherung, hat damit nichts.

Die .env behaelt VORRANG. Sonst koennte ein Fehlgriff im Formular
stillschweigend eine funktionierende Servereinstellung aushebeln und
laufenden Zahlungsverkehr umleiten. Die Datenbank ergaenzt, was dort
fehlt -- sie konkurriert nicht.

Kein Neustart noetig: Nach jedem Speichern werden die Werte neu geladen.

DER KNIFF MIT DEM SYNCHRONEN ZUGRIFF

Entschluesseln ist asynchron, die Pruefungen des PayPal-Moduls
(istLive, istEingerichtet, fehlendeEinstellungen) sind synchron und
werden an einem Dutzend Stellen aufgerufen, teils mitten im Aufbau einer
Antwort. Sie alle auf async umzustellen waere ein Eingriff quer durch den
Bezahlvorgang gewesen -- viel Flaeche fuer Fehler genau dort, wo Fehler
Geld kosten.

Stattdessen werden die Werte einmal beim Start entschluesselt und danach
synchron gelesen. Alle 12 Zugriffe auf process.env.PAYPAL* im Modul
laufen jetzt ueber einen einzigen Zugriffspunkt.

WERTE KOMMEN NIE ZURUECK

Es gibt keinen Weg, ein gespeichertes Geheimnis wieder auszulesen. Die
Anzeige zeigt nur: ob etwas da ist, woher es stammt, wie lang es ist,
wann es zuletzt geaendert wurde. Das Formular leert sich nach dem
Speichern selbst -- ein Secret soll nicht stehen bleiben, wenn jemand
anders auf den Bildschirm schaut.

Ein leeres Feld bedeutet "nicht anfassen", nicht "loeschen". Sonst wuerde
das Nachtragen eines einzelnen Werts alle anderen leeren.

Nach jeder Aenderung wird das gemerkte PayPal-Zugangstoken vergessen --
sonst liefe die naechste Zahlung noch ueber die alten Zugangsdaten oder
scheiterte mit "invalid client".

"Verbindung testen" meldet sich probeweise bei PayPal an. Erst das
beweist, dass die Werte stimmen: "gesetzt" heisst nur, dass etwas
dasteht. Es fliesst dabei kein Geld.

GEPRUEFT: 23 Pruefungen auf dem Server, alle bestanden. Darunter die
wichtigsten Verneinungen: In der Datenbank steht kein Klartext, auch
kein Teil davon. Der Stand enthaelt das Geheimnis nicht. Ein fremder
Schluessel wird abgewiesen. Und: Ein gewechselter ENCRYPTION_KEY bringt
den Dienst NICHT zum Stehen -- der Wert gilt dann als nicht vorhanden,
die Seite laeuft weiter.

Verwaltung 69, Gestaltung 0 Befunde, Designsystem 5 -- unveraendert gruen.

Versionsstempel und Cache-Name auf v10.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 16:51:56 +02:00
DogFatherGit 5043642951 WIP: PayPal-Einstellungen ueber die Verwaltung (Test folgt auf dem Server) 2026-08-23 16:50:18 +02:00
DogFatherGitandClaude Opus 5 692d702ebf Einrichtungsskript fuer PayPal -- Filipe tippt nur die Werte
Auf Wunsch, die Werte selbst einzutragen, zuerst geprueft statt vermutet:

  sudo erlaubt claudian: apt, apt-get, systemctl, docker, docker-compose
  /home/dogiintern/        -> Keine Berechtigung
  .../server-internal/.env -> Keine Berechtigung

Der Zugriff fehlt also technisch, und das ist die Trennung, die Filipe am
08.08.2026 bewusst so eingerichtet hat. Selbst mit Zugriff waere es
falsch: Damit ich die Werte eintrage, muesste das Secret durch den
Chatverlauf wandern und stuende dort dauerhaft.

Also alles abnehmen, was NICHT das Eintippen ist:

paypal-einrichten.sh fragt die vier Werte nacheinander ab, zeigt zu jedem
an, ob schon etwas hinterlegt ist (ENTER = behalten), legt vorher eine
Sicherung an, setzt die Rechte auf 600, startet den Dienst neu und meldet
den Stand.

DREI DINGE, DIE DAS SKRIPT RICHTIG MACHT

Das Secret wird mit "read -s" eingelesen -- es erscheint weder auf dem
Bildschirm noch in der Bash-History. Auch die Abschlussmeldung zeigt nur
die LAENGE, nie den Wert.

Geschrieben wird mit awk und dem Wert in einer Variablen, NICHT mit
"sed -i". Ein PayPal-Secret kann &, \, $, / und Anfuehrungszeichen
enthalten; sed liest davon mehrere als Befehl und haette den Wert still
zerstoert. Der Fehler waere erst bei der ersten echten Zahlung
aufgefallen, mit der Meldung "invalid client" -- und dann sucht man an
der falschen Stelle.

Gegengeprueft mit dem Secret  A&B/C\D$E"F~G : zeichengenau in der Datei
angekommen, umliegende Zeilen unveraendert, vorhandener Schluessel
ersetzt statt ein zweites Mal angehaengt.

Laeuft der Dienst nach dem Neustart nicht, nennt das Skript den Pfad der
Sicherung und den fertigen Befehl zum Zurueckholen -- in dem Moment will
niemand erst suchen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 16:43:32 +02:00
DogFatherGit 244b275dce PayPal-Anleitung: zuerst pruefen, was schon da ist
Client ID und Secret teilt sich der Webdesign-Bereich mit dem
DogiCrew-Supporter-Abo -- laeuft das live, sind zwei der vier Werte
schon eingetragen und es fehlt nur die Webhook-Kennung.

Neuer Schritt 0 mit Befehlen, die nur ANZEIGEN, ob ein Wert gesetzt ist,
ohne ihn auszugeben. Verhindert ausserdem, dass eine zweite App fuer
dasselbe Konto entsteht: Das funktioniert zwar, macht aber jede spaetere
Fehlersuche doppelt muehsam, und beim Erneuern eines Secrets braeche
womoeglich der andere Bereich weg.
2026-08-23 16:33:32 +02:00
DogFatherGitandClaude Opus 5 321e378f9b Zahlungen: Routen, Zustimmung nach § 356 Abs. 4 und Einrichtungsanleitung
Das PayPal-Modul und die Tabellen standen seit dem 22.08.2026 -- es
fehlten die Wege dorthin. Jetzt vollstaendig.

DREI DINGE, DIE HIER ANDERS SIND ALS BEI EINEM UEBLICHEN BEZAHLKNOPF

1. DIE ZUSTIMMUNG IST TEIL DER ZAHLUNG, kein Haekchen daneben.

   Die 30-%-Anzahlung wird faellig, BEVOR die Widerrufsfrist ablaeuft.
   Damit trotzdem sofort begonnen werden darf, verlangt § 356 Abs. 4 BGB
   die ausdrueckliche Zustimmung UND die Bestaetigung, dass der
   Verbraucher dadurch sein Widerrufsrecht verliert. Die Beweislast fuer
   beides liegt beim Unternehmer.

   Gespeichert wird deshalb nicht "hat zugestimmt", sondern der
   WORTLAUT, den der Kunde gesehen hat, samt Fassung und Zeitstempel.
   Im Streit zaehlt nicht DASS, sondern WOZU jemand zugestimmt hat --
   und Texte aendern sich ueber die Jahre.

   Der Wortlaut steht auf dem SERVER, nicht im Browser: Was als Nachweis
   gespeichert wird, muss das sein, was der Server kennt, sonst koennte
   man ihm einen beliebigen Text unterschieben.

   Geschaeftskunden werden gar nicht erst gefragt. Ein Unternehmer hat
   kein Widerrufsrecht; ihn eine Verzichtserklaerung unterschreiben zu
   lassen waere sinnlos und wuerde nur Misstrauen wecken.

2. NICHT EINGERICHTET IST EIN ZUSTAND, KEIN FEHLER.

   Ohne Zugangsdaten sagt die Seite das freundlich und nennt den Weg
   ueber das Postfach. Ein Knopf, der eine technische Fehlermeldung
   wirft, sieht nach einer kaputten Seite aus -- und niemand bezahlt gern
   auf einer kaputten Seite.

3. DER WEBHOOK IST DIE WAHRHEIT, nicht die Rueckkehr des Browsers.

   Der Kunde kann das Fenster schliessen, bevor er zurueckgeleitet wird.
   Die Zahlung ist dann trotzdem erfolgt. Beide Wege schreiben ueber
   DIESELBE Funktion -- zwei getrennte Fassungen wuerden frueher oder
   spaeter auseinanderlaufen und unterschiedliche Felder setzen.

   Doppelte Zustellung ist bei PayPal normal. Die Merkliste verhindert
   die Doppelverbuchung, und "verarbeitet" wird erst NACH der Auswertung
   gesetzt: Bricht der Server dazwischen ab, steht die Meldung als
   empfangen-aber-offen da und faellt auf, statt spurlos als "schon
   behandelt" zu gelten.

Ohne gueltige Signatur wird nichts verarbeitet -- sonst koennte jeder
eine Zahlung als bezahlt melden.

GEPRUEFT: 26 Pruefungen auf dem Server, alle bestanden. Bewusst OHNE
PayPal-Zugangsdaten, weil genau das der heutige Zustand ist. Geprueft
wird vor allem, was NICHT passieren darf: kein Bezahlvorgang ohne
Zustimmung, keine Zustimmungsfrage an Geschaeftskunden, keine fremde
Zahlung sichtbar, keine Doppelverbuchung, kein Geldfluss ohne
Zugangsdaten -- und dass ein gescheiterter Versuch die Zahlung
unangetastet auf "offen" laesst.

ANLEITUNG: PAYPAL-EINRICHTEN.md, Schritt fuer Schritt mit den genauen
Klicks. Sie nennt ausdruecklich die drei Fallen, die man sonst erst
spaeter merkt: der Sandbox-Schalter (Zugangsdaten ohne echtes Geld),
"Select all" bei den Webhook-Ereignissen (erzeugt Rauschen, in dem echte
Probleme untergehen) und Client-ID und Secret aus verschiedenen Apps.

Das Secret gehoert nach Bitwarden -- die Anleitung sagt das an drei
Stellen und bietet an, dass Filipe es selbst eintraegt, ohne es mir zu
zeigen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 16:24:51 +02:00
DogFatherGit eede01ca70 WIP: Zahlungsrouten (Test folgt auf dem Server) 2026-08-23 16:23:12 +02:00
DogFatherGitandClaude Opus 5 be3599239d Designsystem: eine Quelle je Farbe, Radien auf eine Skala
Diese Runde ging eine Ebene tiefer als "sieht es gut aus": Ist das
System, aus dem das Aussehen entsteht, ueberhaupt eines?

DIE MESSUNG VORWEG

  Markenfarben ausgeschrieben im Quelltext:  155 Stellen
    davon Blau 74, Gold 33, Lila 22, Gruen 16, Rot 10
  verschiedene Eckenradien:                   12
    darunter 5px, 7px, 9px, 11px

Die Farben gab es laengst als Variablen. Sie standen trotzdem ueberall
ausgeschrieben da, weil halbdurchsichtige Toene -- Raender, Leuchten,
sanfte Flaechen -- einen Alphawert brauchen, und das mit einer fertigen
Farbvariablen nicht geht: rgba(127, 208, 232, .16).

Die Folge waere beim ersten Farbwechsel sichtbar geworden: Man findet
150 Stellen, uebersieht fuenf, und die Seite hat danach zwei Blautoene,
die sich um eine Nuance unterscheiden. Genau das ist der Unterschied
zwischen einer gewachsenen und einer gestalteten Oberflaeche.

WAS GEAENDERT WURDE

Kanalvariablen (--wd-blau-rgb: 127 208 232) neben den fertigen Farben.
Damit schreibt man rgb(var(--wd-blau-rgb) / .16), und eine Aenderung
wirkt ueberall. 164 Stellen umgestellt, verteilt ueber CSS und neun
Seiten.

Auch die abgeleiteten Farben zeigen jetzt auf die Kanaele statt eigene
Werte zu fuehren -- und die vier Prozessfarben (--wd-p1 bis p4) sind
keine eigenen Farben mehr, sondern Rollen: --wd-p1: var(--wd-blau).
Sonst haette man vier weitere Stellen, die beim naechsten Farbwechsel
stillschweigend zurueckbleiben.

Ergebnis: Jede Markenfarbe hat genau EINE Quelle. Ausgeschriebene
Markenfarben im Quelltext: 0.

Radien auf vier Stufen plus Pillenform: 6 / 10 / 14 / 20 / 999. Kein
Wert wurde um mehr als 2px verschoben, optisch aendert sich also nichts
Spuerbares -- 12 verschiedene Werte sind auf 3 tatsaechlich verwendete
zusammengeschmolzen. Zwischenwerte wie 7px oder 11px entstehen nicht aus
Absicht, sondern weil man beim Bauen einer neuen Kachel nicht nachschaut,
was nebenan schon gilt.

DER BEWEIS, DASS ES EIN SYSTEM IST

Neue Pruefung pruef-system.mjs. Sie behauptet nicht, sie STELLT UM: Die
Markenfarbe wird von Babyblau auf kraeftiges Orange gesetzt und dann
nachgezaehlt, ob irgendwo der alte Ton stehen bleibt.

  658 von 1975 sichtbaren Elementen tragen die Markenfarbe
  0 bleiben nach der Umstellung beim alten Ton
  Prozessfarben folgen nachweislich mit
  Radien: 3 Stufen

UND DIE WICHTIGSTE ERKENNTNIS -- ueber das Messen selbst

Der erste Anlauf meldete 110 haengengebliebene Elemente, alle in Kopf-
und Fusszeile. Die naheliegende Erklaerung waere gewesen: "da steht die
Farbe noch ausgeschrieben". Sie war falsch.

Nachgewiesen ueber die DevTools-Schnittstelle: Auf diese Elemente wirkt
die Regel ".wd a:not(.wd-btn) -> var(--wd-blau)", und --wd-blau
resolviert an genau dieser Stelle korrekt zum neuen Orange. Trotzdem
blieb die berechnete Textfarbe alt -- auch nach erzwungener
Neuberechnung.

Der Unterschied: Kopf- und Fusszeile werden von wd-core.js per innerHTML
eingefuegt. Chromium erneuert solche Teilbaeume nicht zuverlaessig, wenn
man eine Variable NACHTRAEGLICH umstellt. Laedt man dieselbe Seite mit
bereits geaenderter Variable, sind sie orange -- gegengeprueft mit einem
Minimalbeispiel, in dem derselbe Mechanismus einwandfrei funktioniert.

Das Pruefverfahren wurde deshalb umgestellt: Die Variable wird VOR dem
Laden ueberschrieben. Das entspricht genau dem, was eine Aenderung in der
CSS-Datei bewirkt -- also dem Fall, um den es wirklich geht.

Haette ich der ersten Messung geglaubt, haette ich 110 einwandfreie
Stellen "repariert" und dabei das gerade aufgebaute System wieder
zerlegt. Es ist der siebte Fehlalarm in eigener Messtechnik innerhalb
von zwei Runden -- und der subtilste.

ALLE PRUEFUNGEN GRUEN: Gestaltung 0 Befunde (13 Seiten x 5 Sprachen
gegen WCAG 2.2 AA), Anfragedetail 20, Verwaltung 69, Portal 32,
Widerruf 58, Textkodierung 55 Kombinationen, Designsystem 5.

Versionsstempel und Cache-Name auf v9.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 13:05:12 +02:00
DogFatherGitandClaude Opus 5 3ca9f0b0cd Gestaltung: WCAG-Messung ueber alle Seiten, alle Befunde behoben
"Sieht gut aus" ist Geschmack und laesst sich nicht pruefen. Handwerkliche
Qualitaet laesst sich pruefen -- und bei einer WEBDESIGN-Seite ist sie
zugleich das Verkaufsargument: Wer selbst schludert, kann schlecht
Sorgfalt verkaufen.

Neues Werkzeug pruef-design.mjs misst 13 Seiten x 5 Sprachen auf
Handy-Breite gegen WCAG 2.2 AA -- denselben Standard, auf den auch das
Barrierefreiheitsstaerkungsgesetz verweist. Der Browser rechnet, es wird
nichts geschaetzt.

ERGEBNIS: 0 Befunde. Kontrast, Klickflaechen, Tastaturfokus,
Ueberschriftenstruktur, Bildalternativen, Feldbeschriftungen,
lang-Attribut, Schriftgroessen.

ECHTE BEFUNDE, DIE BEHOBEN WURDEN

1. 23 Schriftgroessen unter 12px (bis hinunter zu 10,9px), verteilt ueber
   CSS und sieben Seiten. Betroffen waren durchweg gesperrte
   Grossbuchstaben-Beschriftungen -- also genau die Textsorte, die klein
   ohnehin am schlechtesten lesbar ist. Alle auf mindestens 12px, die
   gesperrten auf 12,8px. Das ist zugleich die Dauervorgabe
   "augenschonend" ernst genommen.

2. Ueberschriftensprung h2 -> h4 in der Fusszeile, auf ALLEN 13 Seiten in
   allen 5 Sprachen (42 Stellen). Wer sich mit einem Screenreader durch
   die Ueberschriften bewegt, hoert dadurch eine Gliederung, die es nicht
   gibt, und vermutet uebersprungene Abschnitte. Jetzt h2 mit
   Gestaltungsklasse: Die Ebene sagt etwas ueber die STRUKTUR, die
   Groesse ueber die GEWICHTUNG -- zwei verschiedene Dinge.

3. code-Auszeichnung auf der Rechtsseite rutschte durch relative
   Groessenangabe auf 11,2px. Jetzt mit Untergrenze abgesichert.

SECHS FEHLALARME IM WERKZEUG -- ALLE GEFUNDEN UND BESEITIGT

Der erste Lauf meldete 302 Fundstellen. Fast alle waren falsch. Haette
ich sie "repariert", haette ich funktionierende Gestaltung zerstoert.
Jeder Fehlalarm hatte eine eigene, nicht offensichtliche Ursache:

  * Farbverlaeufe: getComputedStyle().backgroundColor liefert bei einer
    reinen Verlaufsflaeche "rgba(0,0,0,0)". Die Suche lief an der hellen
    Knopfflaeche VORBEI bis zum dunklen Seitengrund und verglich dunkle
    Schrift mit dunklem Grund -- Ergebnis 1:1 fuer 60 einwandfreie
    Knoepfe. Jetzt werden die Farbstopps ausgelesen und der
    unguenstigste Fall gerechnet.

  * MEHRERE Hintergrundebenen: Die Karten nutzen den bekannten Kniff
    "Flaeche padding-box, Rahmenverlauf border-box". Der helle
    Rahmenverlauf ist im Textbereich gar nicht sichtbar. Alle Ebenen
    zusammenzurechnen ergab 820 Meldungen mit Werten wie "3,45:1" fuer
    Text, der in Wahrheit ueber 7:1 liegt. CSS malt die ERSTE Ebene oben
    -- nur die zaehlt. Das Trennen an Kommas muss dabei die Kommas
    INNERHALB der Verlaufsklammern respektieren.

  * Verlaufsschrift (background-clip:text): Dort ist die Schriftfarbe
    durchsichtig und der Verlauf IST der Text. Farbe gegen Hintergrund zu
    vergleichen ergibt zwangslaeufig 1:1.

  * Verlaufsstopps mit wenig Deckkraft (9 % und 4 % Gold in den
    Hinweiskaesten) wurden verworfen statt auf den Untergrund gerechnet.
    Uebrig blieb "nicht messbar" fuer 72 Textstellen. Ein Pruefwerkzeug,
    das bei allem passt, prueft nichts.

  * Laufende Ueberblendungen: getComputedStyle liefert waehrend einer
    Ueberblendung den ZWISCHENWERT. Nach blur() verblasst der Fokusring
    ueber 0,16 s -- misst man sofort, sieht man ihn noch, und "vorher"
    ist identisch mit "nachher". Das Codefeld der Verwaltung wurde
    dadurch als "ohne sichtbaren Fokus" gemeldet. Nachgewiesen mit
    el.matches(":focus") === false bei gleichzeitig sichtbarem Schatten.
    Loesung: Ueberblendungen fuer die Messung abschalten.

  * Ausgeblendete Abschnitte: Eine Ueberschrift in einem versteckten
    Bereich hat selbst weiterhin display:block. Im Portal wurden dadurch
    drei h1 gezaehlt, obwohl immer nur eine sichtbar ist. Jetzt
    getClientRects().

Dazu zwei bekannte Muster, die das Werkzeug jetzt kennt: der Sprunglink
(absichtlich aus dem Bild geschoben) und die winzigen Auswahlfelder
hinter den Kacheln (die Kachel IST die Klickflaeche).

ALLE UEBRIGEN PRUEFUNGEN WEITERHIN GRUEN nach den Aenderungen:
Anfragedetail 20, Verwaltung 69, Portal 32, Widerruf 58, Textkodierung
55 Seiten-Sprach-Kombinationen.

Versionsstempel und Cache-Name auf v8.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 12:49:29 +02:00
DogFatherGitandClaude Opus 5 14ad108c87 Rechtliches: elektronische Widerrufsfunktion (§ 356a BGB), Impressum, AGB,
Widerrufsbelehrung und Datenschutz

RECHERCHIERT, NICHT AUS DEM GEDAECHTNIS

Der Webdesign-Bereich hatte KEINE eigene Rechtsseite -- obwohl dort
Vertraege geschlossen und Zahlungen ausgeloest werden. Die Fusszeile
verwies auf die Kontaktseite des Universe, die ein Content-Projekt
beschreibt, kein Dienstleistungsgeschaeft.

Die Web-Recherche hat eine Pflicht zutage gefoerdert, die seit zwei
Monaten gilt und die die Seite nicht erfuellt hat:

  § 356a BGB -- elektronische Widerrufsfunktion, in Kraft seit
  19.06.2026. Sie gilt fuer ALLE Fernabsatzvertraege, die ueber eine
  Online-Benutzeroberflaeche geschlossen werden, nicht nur fuer
  Finanzdienstleistungen.

WARUM DAS HIER GILT: Im Kundenportal nimmt der Kunde Zusatzangebote per
Klick VERBINDLICH an ("Damit nimmst du das Zusatzangebot verbindlich
an"). Das ist ein Vertragsschluss ueber eine Online-Benutzeroberflaeche.
Fuer die geplanten PayPal-Zahlungen gilt dasselbe.

Der Anbieter sitzt in Luxemburg. Fuer Verbraucher mit gewoehnlichem
Aufenthalt in Deutschland gilt nach Art. 6 Rom-I-VO trotzdem das
deutsche zwingende Verbraucherrecht, weil die Seite sich erkennbar an den
deutschsprachigen Markt richtet. Deshalb wurde der STRENGERE Standard
umgesetzt, nicht der bequemere.

WAS GEBAUT WURDE

webdesign/widerruf.html -- die Widerrufsfunktion, genau nach dem
Wortlaut der Vorschrift:
  * Beschriftung "Vertrag widerrufen" (gesetzlich vorgegeben)
  * ZWEITE Schaltflaeche "Widerruf bestaetigen" (ebenfalls vorgegeben --
    ein einstufiges Formular wuerde die Vorschrift nicht erfuellen)
  * unverzuegliche Eingangsbestaetigung mit Nummer, ZEITPUNKT (nicht nur
    Datum), Empfaenger und dem Wortlaut der Erklaerung
  * Link in der Fusszeile JEDER Seite -- "staendig verfuegbar,
    hervorgehoben platziert, leicht zugaenglich" heisst nicht "in den AGB
    versteckt"

Drei Entscheidungen, die unmittelbar aus dem Sinn der Vorschrift folgen:

  KEINE ANMELDUNG. Die Seite ist von der Zugangswand ausgenommen. Wer
  seinen Code verlegt hat, wuerde sonst seine Frist verlieren --
  Fristverlust durch eine selbstgebaute Huerde ist der schlimmste
  denkbare Fall.

  KEINE MENGENBEGRENZUNG. Ueberall sonst richtig, hier ein
  Rechtsverlust: Wer in der letzten Stunde seiner Frist wegen eines
  hakenden Netzes dreimal klickt, darf nicht abgewiesen werden.

  KEIN ZWISCHENSPEICHER. Beim Anfrageformular ein Segen, hier eine
  Falle: Auf einem geteilten Rechner laege der halb ausgefuellte Widerruf
  des einen im Browser des naechsten.

webdesign/rechtliches.html -- Impressum, AGB, Widerrufsbelehrung samt
Muster-Widerrufsformular, Datenschutzerklaerung. Alle Betreiberangaben
sind aus den bestehenden Seiten UEBERNOMMEN, nichts erfunden: Anschrift
in Mondercange, Kleinunternehmerregelung nach Art. 57 TVA-Gesetz LU und
Richtlinie (EU) 2020/285, netcup/Cloudflare, PayPal Luxemburg.

Migration 0014: wd_widerrufe (der dauerhafte Datentraeger und der
Nachweis, wird nie geleert) und wd_zustimmungen. Letztere speichert nicht
nur den Haken, sondern den WORTLAUT, den der Kunde gesehen hat -- die
Beweislast fuer die Zustimmung zum vorzeitigen Leistungsbeginn liegt beim
Unternehmer (§ 356 Abs. 4 BGB), und im Streit zaehlt, WAS bestaetigt
wurde. Bewusst OHNE Fremdschluessel: CASCADE wuerde den Nachweis mit dem
Kunden mitloeschen, RESTRICT wuerde eine DSGVO-Loeschung blockieren.

Druckansicht: Die Eingangsbestaetigung ist ein Rechtsnachweis. Auf Papier
schwarz auf weiss, ohne Navigation und Knoepfe -- ein Nachweis, den
niemand ausdruckt, weil er eine halbe Patrone kostet, erfuellt seinen
Zweck nicht.

ZWEI ECHTE FEHLER GEFUNDEN

1. Die Pflicht-Sternchen verschwanden nach der Uebersetzung. Ursache:
   data-i18n stand auf dem <label> selbst und ersetzte dessen ganzen
   Inhalt -- samt <span class="wd-pflicht">. Das Anfrageformular macht es
   laengst richtig (data-i18n auf einem INNEREN span); die neue Seite
   hatte das Muster nicht uebernommen. Aufgefallen ist es nur, weil der
   Test die Pflichtfelder GEZAEHLT hat statt sie vorauszusetzen.

2. window.WD.sprache() gibt es nicht, die Funktion heisst getSprache().
   Dadurch brach der Uebergang zur zweiten Stufe stumm ab.

GEPRUEFT: 58 Pruefungen, alle bestanden. Darunter die gesetzlich
vorgegebenen Beschriftungen in allen FUENF Sprachen, "kein Grund noetig",
genau drei Pflichtfelder, Datum UND Uhrzeit in der Bestaetigung, und die
Druckansicht.

WAS FILIPE NOCH PRUEFEN LASSEN MUSS: Diese Texte sind sorgfaeltig
recherchiert, aber ich bin keine Rechtsanwaeltin. Vor dem oeffentlichen
Start gehoert das Ganze einmal zu einer im luxemburgischen Recht
qualifizierten Fachperson -- besonders die grenzueberschreitende Lage
(Sitz LU, Kunden in DE/CH/FR/PT) und die Frage, ob die Seite in
Franzoesisch und Portugiesisch aktiv verkaufen soll. Dann muessen die
Rechtstexte auch dorthin uebersetzt werden, und zwar fachlich.

Versionsstempel und Cache-Name auf v7.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 12:29:09 +02:00
DogFatherGitandClaude Opus 5 98fc04c666 Verwaltung: Projektstand aendern, Wuensche beziffern, im Projekt antworten
Drei Server-Funktionen existierten seit Tagen und hatten KEINE Oberflaeche.
Erst im Vergleich "was kann der Server" gegen "was ruft die Seite auf"
ist es aufgefallen:

  POST /admin/projekte/:id           projektAendern
  POST /admin/aenderungen/:id/beziffern
  POST /admin/projekt-nachricht

Jede davon schliesst einen Kreis, der bisher offen war.

1. DIE SCHMERZHAFTESTE LUECKE: DER STAND

Der Kunde sieht in seinem Portal GANZ OBEN die Phasenleiste und den
"naechsten Schritt". Beides liess sich nirgends aendern. Ein Projekt blieb
also fuer immer im Briefing stehen, egal wie weit es wirklich war -- und
der prominenteste Text im ganzen Kundenportal war dauerhaft leer.

Jetzt: sieben Phasen als Knoepfe (plus Pausiert/Abgebrochen daneben, denn
das sind keine Phasen, sondern Zustaende), "wer ist am Zug", naechster
Schritt, Richttermin, Zahlungsstand, Portfolio-Freigabe.

Phase und "wer ist am Zug" speichern SOFORT ohne Speichern-Knopf: Es ist
ein Klick auf genau einen Wert, ein zweiter Klick waere Zeremonie. Die
Textfelder haben einen Knopf, weil man beim Tippen zwischendurch nicht
speichern will.

2. AENDERUNGSWUENSCHE

Der Kunde konnte Ideen einreichen, seit es das Portal gibt. Beziffern ging
serverseitig auch -- nur gab es keine Oberflaeche. Der Kreis war offen: Er
schickt etwas los und hoert nie wieder davon.

Jetzt stehen alle Wuensche im Projekt, offene mit Feldern fuer Preis,
Dauer und einer kurzen Erklaerung. Ohne Preis geht nichts raus (geprueft)
-- ohne Preis kann der Kunde nicht entscheiden, und ein "Angebot" ohne
Zahl ist keins.

3. NACHRICHTEN ZUM PROJEKT

Getrennt vom allgemeinen Postfach, weil sie zum Projekt gehoeren und im
Portal auch dort erscheinen. Sie hier nicht zu haben hiess: Der Kunde
schreibt im Projekt, und ich kann ihm nur woanders antworten.

AUFBAU

Neuer Endpunkt /admin/projekte/:id/alles liefert Projekt, Aufgaben,
Fortschritt, Aenderungswuensche und Nachrichten in EINER Antwort. Fuenf
Anfragen hintereinander wuerden die Ansicht sichtbar ruckelnd aufbauen.

Dieselbe Aufteilung wie in der Anfrageansicht -- links die Arbeit
(Aufgaben), rechts das Steuern. Wer zwischen beiden Ansichten wechselt,
muss nicht umdenken.

GEPRUEFT: 69 Pruefungen, Computer und Handy, alle bestanden.

Die neuen Pruefungen schauen nicht darauf, ob sich ein Knopf einfaerbt --
das beweist nichts. Sie schneiden mit, was die Seite TATSAECHLICH an den
Server schickt: {"status":"entwicklung"}, {"restBezahlt":true},
{"preisEuro":250,"dauer":"3 Tage"}. Und sie pruefen den Fall, in dem
nichts rausgehen darf: Ohne Preis bleibt die Mitschrift leer.

Ein Fehler im Test selbst behoben: Die Nachrichtenzaehlung war
document-weit und zaehlte die Nachrichten der geschlossenen Projektansicht
mit -- geschlossen heisst nicht aus dem Dokument entfernt. Jetzt auf den
Postfach-Verlauf eingegrenzt.

Versionsstempel und Cache-Name auf v6.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 12:08:57 +02:00
DogFatherGitandClaude Opus 5 469a6c195f Portal: Aufgabenliste und Postfach fuer den Kunden
Schritt 4 von 4 -- damit sind beide Wuensche vom 22.08.2026 vollstaendig:
"wenn sie drauf druecken sollen sie auch sehen was ich schon gemacht habe
von dem was im plan war" und "die kunden sollen mir auch nachrichten
hinterlassen koennen".

AUFGABENLISTE IM PROJEKT

Bisher sah der Kunde nur die Phase ("Design . 3/7") -- eine Zahl ohne
Inhalt. Sie beantwortet die eigentliche Frage nicht: WAS ist denn fertig?

Die Liste steht GANZ OBEN, direkt nach dem Projektkopf, noch vor
Zahlungen und Dateien. Die sind wichtig, aber sie sind nicht der Grund
fuer den Klick.

Zwei Entscheidungen praegen die Ansicht:

1. Offene Kundenpunkte stehen in einem EIGENEN Kasten ganz oben ("Das
   brauche ich noch von dir"), nicht nur farblich markiert irgendwo
   mittendrin. Wer eine lange Liste sieht, liest sie als Bericht ueber
   fremde Arbeit und ueberliest seinen eigenen Teil -- genau daraus
   entstehen die meisten Verzoegerungen. Ist nichts offen, steht das
   auch da: "Von dir wird gerade nichts gebraucht." Eine gute Nachricht
   darf ausgesprochen werden.

2. Der Kunde hakt seine eigenen Punkte selbst ab. Nur diese haben einen
   Knopf; bei fremden Punkten ist das Zeichen reine Anzeige. Ein Knopf,
   der nichts tut, laesst die Seite kaputt wirken. Geprueft: 4 Knoepfe
   bei 2 offenen eigenen Punkten (sie erscheinen zweimal), 5 feste.

Grosse Zahl statt Prozent: "2 von 6 erledigt" ist greifbar, "33 %" ist
eine Rechnung, die niemand fuehlt. Der Balken traegt die vier
Prozessfarben -- dieselben wie auf der Ablaufseite und in der Verwaltung.

Der Ton ist bewusst gewaehlt. Der Kunde liest die Liste, wenn er unsicher
ist -- also im Zweifel schon angespannt. "Wir warten auf dich" waere
Druck, "Das brauche ich noch von dir" ist eine Bitte.

POSTFACH AUF DER UEBERSICHT

Bewusst auf der Uebersicht, nicht im Projekt: Wer kein Projekt hat -- oder
eine Frage, die zu keinem gehoert -- konnte vorher gar nicht schreiben und
musste zur E-Mail greifen. Damit war der Verlauf weg, sobald man ihn
brauchte.

Niedrigschwellig: kein Betreff, kein Pflichtfeld, der Projektbezug ist
freiwillig. Wer glaubt, eine Nachricht muesse eine "richtige" Anfrage
sein, schreibt gar nicht erst -- und genau die kurzen Fragen sollen hier
landen. Wird nachgeladen, damit die Uebersicht sofort dasteht.

Projekt- und Aufgabendaten werden gleichzeitig angefordert statt
nacheinander -- sonst waere die Wartezeit die Summe beider Anfragen. Die
Aufgabenliste darf dabei fehlschlagen, ohne die Seite mitzureissen: Wer
wegen einer leeren Liste seine Zahlungen nicht mehr saehe, waere
schlechter dran als vorher.

EIN SICHTBARER FEHLER GEFUNDEN

Auf dem Screenshot stand "Ideen &amp;amp; Aenderungswuensche". Ursache ist
eine Falle mit zwei Wegen: Texte ueber data-i18n landen als HTML in der
Seite, dort ist "&amp;" richtig. Derselbe Text im JavaScript laeuft aber
durch die Absicherung und wird ein zweites Mal kodiert. Derselbe Baustein
ist also je nach Verwendungsort richtig oder falsch.

Das kann man nicht im Kopf behalten -- deshalb neu pruef-texte.mjs mit
ZWEI Pruefungen:

  Anzeige:   alle 11 Seiten x 5 Sprachen = 55 Kombinationen, sichtbarer
             Text darf keine Kodierungsreste enthalten.
  Quelltext: welcher Baustein mit "&amp;" wird irgendwo abgesichert
             eingesetzt?

Beide sind noetig. Gegengeprueft: Die Anzeigepruefung allein haette den
Fehler NICHT gefunden, weil die Ideen-Karte erst nach einer Anmeldung
erscheint. Die Quelltextpruefung findet ihn ohne Anzeige.

Auch die Quelltextpruefung selbst wurde zweimal gegengeprueft: Zuerst
meldete sie zusaetzlich po_senden ("Senden", voellig harmlos) -- ihre
Blockerkennung lief bis zum naechsten Schluessel und schluckte dabei
einen Kommentar mit "&". Jetzt zaehlt sie geschweifte Klammern. Ein
Fehlalarm im Pruefwerkzeug ist fast so schaedlich wie ein uebersehener
Fehler: Man gewoehnt sich daran, ihn wegzusehen.

GEPRUEFT

32 Pruefungen im Portal (Computer und Handy), alle bestanden. Darunter:
114 Texte in allen fuenf Sprachen vollstaendig, Kundenpunkte stehen vor
der langen Liste, Aufgaben vor den Zahlungen, Abhaken meldet den
richtigen Punkt, alle Kaestchen mindestens 24 px, keine Fehler im
Protokoll. Dazu 55 Seiten-Sprach-Kombinationen ohne Kodierungsreste.

Ein Fehler im Test selbst gefunden und behoben: Die Adressabfrage prueft
jetzt den PFAD statt der ganzen Adresse. Der API-Server heisst
"postfach.dogfather-universe.com" -- ein includes("/postfach") traf den
HOSTNAMEN und damit jede Anfrage, auch die Uebersicht.

Versionsstempel und Cache-Name auf v5.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 10:46:09 +02:00
DogFatherGitandClaude Opus 5 b7c333fca3 Verwaltung: Aufgabenlisten abhaken und Postfach
Schritt 3 von 4 zu den zwei Wuenschen vom 22.08.2026. Die Datenbank und
die Server-Routen standen schon; das hier ist die Seite, auf der ICH
arbeite. Was hier passiert, sieht der Kunde unmittelbar in seinem Portal
(Schritt 4).

ZWEI NEUE REITER

"Projekte" -- bisher gab es Projekte nur als Zahl neben dem Kunden
("3 Proj."), man konnte kein einzelnes oeffnen. Man denkt aber in
Projekten, nicht in Kunden. Neuer Endpunkt projekteListe liefert die
flache Liste samt Aufgabenzahlen; die einzeln nachzufragen haette bei
zwanzig Projekten einundzwanzig Anfragen bedeutet.

"Postfach" -- mit Abzeichen fuer Ungelesenes. Steht nichts an, ist das
Abzeichen GANZ weg statt eine 0 zu zeigen: Eine Null, die man taeglich
sieht, wird zu Rauschen, und irgendwann uebersieht man auch die 3.

Die Reiterumschaltung war vorher ein Ja/Nein zwischen zwei Ansichten. Mit
vier Reitern haette jede neue Ansicht eine weitere "hidden = ..."-Zeile
gebraucht -- und beim naechsten Reiter vergisst man eine, dann liegen zwei
Ansichten uebereinander. Jetzt eine Zuordnung Reiter -> Kasten, die sich
selbst aufraeumt.

AUFGABENLISTE

Vier Abschnitte in den vier Prozessfarben -- dieselben wie im Portal und
auf der Ablaufseite. Das ist der Punkt: Eine Farbe bedeutet ueberall
dasselbe, sonst waere sie Deko.

Ein Klick aufs Kaestchen schaltet weiter: offen -> in Arbeit -> erledigt.
Drei Zustaende ueber einen Knopf statt eines Auswahlfeldes -- beim
Abarbeiten einer Liste zaehlt jeder gesparte Klick.

Punkte, die auf den KUNDEN warten, sind golden hinterlegt und tragen eine
Marke. Ohne diese Unterscheidung liest man die Liste als reinen
Fortschrittsbericht und uebersieht, dass man selbst gar nicht am Zug ist.
Der goldene Grund verschwindet, sobald der Punkt erledigt ist -- er
braucht dann keine Aufmerksamkeit mehr.

Abhaken faerbt die Zeile sofort um, ohne auf den Server zu warten. Bei
zehn Punkten hintereinander waere ein Neuaufbau nach jedem Klick zaeh, und
man verliert die Stelle. Geht es schief, wird die Liste neu geholt und der
Zustand ist wieder ehrlich.

POSTFACH

Links Verlaeufe, rechts das Gespraech. Nicht aus Nachahmung, sondern weil
man ein Gespraech abarbeitet und nicht eine Zeile: Man will sehen, was
vorher besprochen wurde, waehrend man antwortet. Kunde links, eigene
Nachrichten rechts -- man erkennt den Absender an der Seite, bevor man den
Namen liest.

Der Schalter "Nur interne Notiz" faerbt das ganze Schreibfeld um. Ein
Haekchen allein uebersieht man, und eine interne Bemerkung, die beim
Kunden landet, ist der peinlichste Fehler, den dieses System machen kann.
Interne Notizen im Verlauf sind gestrichelt umrandet und golden -- sie
duerfen nicht wie etwas Gesendetes aussehen.

Strg+Enter sendet, Enter allein nicht: In einem mehrzeiligen Feld will man
Absaetze machen koennen, ohne dass die halbe Nachricht rausgeht.

EIN FEHLER, DEN DIE TESTS NICHT GEFUNDEN HABEN

Der erste Lauf meldete 37 von 37 gruen -- und jede Aufgabenzeile war
sichtbar falsch herum gebaut: Kaestchen, dann die kleinen Knoepfe, Titel
ganz rechts. Aufgefallen ist es erst am Screenshot.

Ursache: Das Raster platziert zuerst alle Elemente mit FESTGELEGTER Zeile
und erst danach die freien. Kaestchen und Knopfgruppe hatten beide
"grid-row: 1 / span 2" und wurden deshalb zusammen nach vorne gesetzt; der
Titel landete als letzter in der dritten Spalte. Jetzt bekommt jedes Kind
Zeile UND Spalte ausdruecklich.

Die eigentliche Lehre steckt im Test: Alle 37 Pruefungen betrafen
Bestandteile (Farben, Anzahl, Durchgestrichenes), keine einzige die
ANORDNUNG. Wer eine Anordnung baut, muss die Anordnung pruefen. Drei neue
Pruefungen vergleichen jetzt die tatsaechlichen Bildschirmpositionen --
und sie wurden gegengeprueft: Mit dem alten Zustand schlagen sie fehl,
mit dem neuen nicht. Ein Test, der nie fehlschlaegt, ist wertlos.

GEPRUEFT: 43 Pruefungen, Computer und Handy, alle bestanden. Darunter:
alle Touch-Ziele mindestens 24 px, interne Notiz sieht anders aus als eine
gesendete, Verlauf springt ans Ende, keine Fehler im Protokoll.

Versionsstempel und Cache-Name auf v4.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 23:30:11 +02:00
DogFatherGitandClaude Opus 5 9186d4cc1a Verwaltung uebersichtlicher, Bildlaufleiste dunkel
Rueckmeldung 22.08.2026 zur Anfrage-Detailansicht: "das ganze soll viel
uebersichtlicher sein und nicht so durcheinander" und "die leiste rechts
zum hoch und runter, kannst du die mal geil aussehen lassen anstatt
einfach so kake weiss."

1. DAS DURCHEINANDER HATTE EINE URSACHE

Die Kaesten standen in "columns: 2" -- Zeitungsspalten. Die fuellen sich
von selbst: erst Spalte eins bis oben voll, dann Spalte zwei. Wo ein
Kasten landet, haengt allein davon ab, wie lang die vor ihm sind. Bei
einer Anfrage stand "Interne Notizen" oben rechts, bei der naechsten
unten links. Es gab schlicht keine Ordnung, der man haette folgen koennen.

Jetzt ein festes Raster mit einer Aussage:
  LINKS  = was der Kunde geschickt hat   (lesen)
  RECHTS = was du damit machst           (handeln)

Immer gleich. Nach der zweiten Anfrage weiss man, wo man hinschaut, ohne
zu suchen. Rechts ist schmaler (Knoepfe brauchen weniger Platz als
laufender Text) und klebt beim Scrollen mit -- die Handlungen sind der
Grund, warum man die Ansicht oeffnet, sie duerfen nicht aus dem Bild
wandern.

Reihenfolge rechts korrigiert: "Anfrage uebernehmen" steht jetzt oben.
Es ist der Knopf, den man bei einer neuen Anfrage druecken WILL -- er
stand unter den Notizen und damit ausserhalb des sichtbaren Bereichs.

2. "KEINE ANGABE" WAR DIE HALBE ANSICHT

Jede fehlende Angabe bekam eine eigene Zeile. Der Kasten "Umfang"
enthielt zweimal nichts und war trotzdem so gross wie einer mit Inhalt.

Leere Felder sind aber keine Information, sondern deren Fehlen. Sie
stehen jetzt als EINE leise Zeile am Fuss: "Ohne Angabe: Bereiche,
Funktionen". Aus sechs Zeilen wird eine. Weggelassen werden sie nicht --
man muss sehen, wonach gefragt wurde und was unbeantwortet blieb, genau
daraus entstehen die Rueckfragen.

Neu darueber: "Auf einen Blick" mit Paket, Budget, Wunschtermin und
Alter. Die vier Fragen, die man immer zuerst hat, standen vorher auf drei
Kaesten verteilt. Das Alter als "vor 3 Tagen" statt als Datum -- die
Frage ist nie "welcher Tag war das", sondern "wie lange liegt das schon
hier", und die beantwortet ein Datum erst nach Kopfrechnen.

3. BILDLAUFLEISTE -- und ein Fehler, der fast durchgegangen waere

Auf einer durchgehend dunklen Seite ist eine weisse Bildlaufleiste der
einzige grelle Streifen im Bild. Sie zieht den Blick dorthin, wo nichts
Wichtiges steht, und blendet. Verstoesst gegen die Dauervorgabe
"augenschonend".

Zwei Wege noetig, weil kein Browser beide versteht: color-scheme: dark
fuer Firefox/Safari (wirkt zusaetzlich auf Auswahl- und Datumsfelder, die
sonst weiss aufblitzen), ::-webkit-scrollbar fuer Chrome/Edge.
scrollbar-width/-color bewusst NICHT gesetzt -- sobald es dasteht,
ignoriert Chrome die feineren ::-webkit-Regeln.

Beim ersten Versuch stand dort nur ".wd ::-webkit-scrollbar" -- MIT
Leerzeichen. Das trifft nur Elemente INNERHALB der Seite, nicht den body
selbst. Alle Messungen sahen gut aus; an der einen Leiste, ueber die sich
jemand beschwert hatte, haette sich nichts geaendert. Jetzt beide
Fassungen, mit einem Warnhinweis im CSS.

GEPRUEFT

Neue Datei pruef-detail.mjs: echter Browser, 1440x900 und 390x844, mit
einer absichtlich sehr knappen Anfrage -- genau die sah vorher schlecht
aus. 20 Pruefungen, alle bestanden: Spalten nebeneinander bzw. auf dem
Handy untereinander, rechte schmaler als linke, keine Ueberlappung, kein
waagerechtes Schieben, nichts ragt aus dem Fenster, keine einzelne
"keine Angabe"-Zeile mehr, keine Fehler im Protokoll.

Die Bildlaufleiste liess sich nur in einem ECHTEN Browserfenster pruefen:
Headless-Chromium blendet Leisten grundsaetzlich ueber dem Inhalt ein und
meldet deshalb immer 0 px Breite. Mit sichtbarem Fenster gemessen: 12 px,
alle sechs Regeln vom Browser akzeptiert.

Versionsstempel auf allen 11 Seiten und Cache-Name des Service Workers
auf v3 -- sonst liefert der eigene Zwischenspeicher beim ersten Laden
weiter die alte Fassung aus.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 23:05:47 +02:00
DogFatherGit 60c1e7be94 Test: Absturz beim Beenden vermeiden (better-sqlite3) 2026-08-22 22:57:59 +02:00
DogFatherGit 38bc741660 Aufraeumen: versehentlich mitcommittete Dateien entfernt
Ein 'git add -A' hat 13 Bild-Sicherungskopien (.bak-*, zusammen 2,6 MB),
den lokalen Testserver und eine erzeugte package-lock.json mitgenommen.

Die package-lock.json war der schaedlichste Teil: Auf dem Server existiert
eine eigene, dort erzeugte Fassung. Eine versionierte Datei gleichen Namens
laesst 'git pull' abbrechen -- der Deploy stand sofort still.

Alle drei Muster stehen jetzt in .gitignore.
2026-08-22 22:57:26 +02:00
DogFatherGit 4ce51c127e WIP: Aufgaben- und Postfach-Routen (Test folgt auf dem Server) 2026-08-22 22:56:59 +02:00
DogFatherGitandClaude Opus 5 548ec6bc00 Aufgabenlisten und allgemeines Postfach: Datenbank und Vorlagen
Erster von vier Schritten fuer zwei Wuensche vom 22.08.2026:
"wenn sie drauf druecken sollen sie auch sehen was ich schon gemacht habe
von dem was im plan war" und "die kunden sollen mir auch nachrichten
hinterlassen koennen".

DATENBANK (3 neue Tabellen, jetzt 18 insgesamt, Fremdschluessel geprueft)

wd_aufgaben -- die Punkte je Projekt. Drei Entscheidungen darin:
  * "wer_dran" ist eine eigene Spalte, kein Text. Manche Punkte warten
    auf den KUNDEN ("Texte liefern"), und genau die muessen ihm ins Auge
    springen. Ohne diese Unterscheidung liest er die Liste als reinen
    Fortschrittsbericht und uebersieht seinen eigenen Teil -- der
    haeufigste Grund fuer Verzoegerungen ueberhaupt.
  * "nicht_enthalten" bildet ab, was NICHT zum Umfang gehoert. Das ist
    die haeufigste Streitfrage in jedem Projekt ("ich dachte, das ist
    dabei"). Vorher sichtbar aufgeschrieben kostet es nichts, hinterher
    kostet es Geld oder den Kunden.
  * "kategorie" nutzt dieselben vier Abschnitte wie die Farben im Portal.
    Damit bedeutet eine Farbe ueberall dasselbe statt nur huebsch zu sein.

wd_postfach -- Nachrichten OHNE Projektbezug. Bewusst eine EIGENE Tabelle:
wd_nachrichten hat projekt_id als NOT NULL, und SQLite kann das nicht
lockern, ohne die ganze Tabelle zu kopieren -- ein unnoetiges Risiko bei
echten Kundennachrichten. Die neue Tabelle hat ohnehin andere
Anforderungen (Anhaenge, Lesestatus in BEIDE Richtungen, optionaler Bezug
auf eine Aufgabe). Zwei klar getrennte Tabellen sind ehrlicher als eine,
die beides halb kann.

VORLAGEN (56 Punkte ueber 5 Pakete, davon 20 beim Kunden)

Bei jedem Projekt fuenfzehn Punkte von Hand einzutippen fuehrt
zuverlaessig dazu, dass es irgendwann niemand mehr macht -- und dann
steht der Kunde wieder vor der leeren Liste, die der Ausloeser war.

Die Punkte sind in der Sprache formuliert, in der ein KUNDE denkt:
"Aufbau der Seiten festlegen" statt "Informationsarchitektur", "Auf dem
Handy durchtesten" statt "Responsive QA". Er liest diese Liste -- sie
muss ihm etwas sagen, nicht mir.

Die Vorlagen wandern beim ersten Start in die Datenbank und sind ab dann
dort aenderbar. Bereits vorhandene werden NIE ueberschrieben: sonst waeren
von Hand angepasste Vorlagen nach dem naechsten Neustart weg, und niemand
kaeme auf die Idee, dass der Neustart schuld war.

Naechste Schritte: Server-Routen, Verwaltung, Portal.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 22:48:39 +02:00
DogFatherGitandClaude Opus 5 71b9fc09c1 Anfrage-Detail: zentriertes Fenster statt seitlicher Leiste
Rueckmeldung 22.08.2026 mit Bildschirmfoto: "ich will das nicht so ich
will es viel viel viel besser und uebersichtlicher und schoener zentriert
in der mitte."

Berechtigt, und aus mehreren Gruenden. Eine seitlich eingeschobene Leiste
ist fuer so viel Inhalt das falsche Format:
  * Der Blick springt beim Oeffnen nach rechts.
  * Die Liste dahinter bleibt halb sichtbar und lenkt ab.
  * Alles muss sich in eine schmale Spalte quetschen -- auf dem
    Bildschirmfoto stand "keine Angabe" dadurch achtmal untereinander,
    und die Abwesenheit von Information nahm mehr Platz ein als die
    Information selbst.

Jetzt: mittig, 940px breit, zweispaltig ab Tablet. Der Blick bleibt, wo
er ist, und zusammengehoerende Angaben stehen nebeneinander.

Weitere Entscheidungen:
* Mauerwerk-Umbruch (columns) statt Raster. Die Abschnitte sind
  unterschiedlich hoch; ein Raster haette grosse Luecken gerissen.
* Jeder Abschnitt bekommt eine eigene Flaeche statt nur einer Trennlinie.
* Die Kopfzeile klebt beim Scrollen oben fest. Bei einer langen Anfrage
  weiss man sonst nach dem Scrollen nicht mehr, wessen Daten man liest.
* "keine Angabe" wird kleiner und blasser dargestellt. Es ist die
  ABWESENHEIT einer Information und darf nicht aussehen wie eine.
* Die Oeffnen-Animation skaliert dezent aus der Mitte statt von rechts
  hereinzufahren, und ist bei prefers-reduced-motion ganz aus.

9/9 Tests weiterhin gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 22:28:09 +02:00
DogFatherGitandClaude Opus 5 2539fdea8d Zahlenkreise repariert und vier Prozessfarben durchgaengig
"wieso sehen die zahlen immer noch so scheisse aus?" -- zu Recht, und die
Ursache war nicht Geschmack, sondern ein Fehler. Nachgemessen sass die
Ziffer 15px NEBEN der Kreismitte.

Ursache: ".po-leer-punkt span" (fuer den Beschreibungstext) trifft auch
den Zahlen-Span und ist spezifischer (Klasse + Element) als
".po-leer-nr" (nur Klasse). Sie erzwang display:block, 14,4px Schrift und
23px Zeilenhoehe -- exakt die gemessenen Werte. Meine eigene
"line-height: 1"-Regel kam gar nicht zum Zug.

Das ist derselbe Spezifitaets-Fehler wie zuvor beim Hauptknopf (blaue
Schrift auf blauem Grund) und bei den Namen auf der Zugangswand. Dreimal
dieselbe Falle, deshalb steht die Begruendung jetzt ausfuehrlich im Code.

Behoben ueber :not(.po-leer-nr) an beiden Textregeln. Nachgemessen:
  Versatz waagerecht  -15px -> 0px
  Versatz senkrecht    -8px -> -1,3px
  display             block -> grid
  Schrift/Zeile   14,4/23px -> 18,4/18,4px

Zweite Rueckmeldung: "es soll 4 farben geben wie 4 kategorien zum
prozess". Sehr gute Idee -- sie macht das System erst schluessig. Die
sieben Phasen sind in Wahrheit vier Abschnitte, und die vier
Merkmalskacheln trugen ohnehin schon vier Farben. Jetzt bedeuten diese
Farben ueberall dasselbe:

  1  Blau   Start        Briefing, Angebot
  2  Lila   Gestaltung   Design
  3  Gruen  Umsetzung    Entwicklung, Tests
  4  Gold   Abschluss    Abnahme, Uebergabe

Die Phasenleiste faerbt erledigte Abschnitte in IHRER Farbe statt
pauschal gruen -- man sieht dadurch, wie weit man ist, nicht nur DASS
etwas fertig ist. Die Vorschau laeuft in denselben Farben durch. Die
Legende nennt die vier Abschnitte beim Namen, damit Farbe nicht geraten
werden muss: Farbe allein ist nie Information.

45/45 Handy-Abnahme, i18n vollstaendig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 22:26:04 +02:00
DogFatherGitandClaude Opus 5 c597753abf Portal: Zahlenkreise, Typografie und eine echte Farblogik fuer Phasen
Drei Rueckmeldungen vom 22.08.2026, alle drei berechtigt.

1) "die kisten sollen auch eine spezielle farbe bekommen wenn sie fertig
   sind" -- das war inhaltlich der wichtigste Punkt. Vorher sahen
   erledigte und gerade laufende Phase fast gleich blau aus. Damit ging
   die einzige Aussage verloren, die die Leiste ueberhaupt traegt:
   naemlich WO man steht. Jetzt drei klar getrennte Zustaende:
     erledigt     gruen, ruhig
     laeuft grad  leuchtendes Blau mit langsamem Puls
     kommt noch   gedaempft
   Die Vorschau erklaert die Logik gleich mit: ihre Leiste wandert von
   blau nach gruen, statt nur an- und auszugehen.
   Dazu eine Legende in Worten. Farbe allein ist nie Information -- wer
   sie nicht unterscheiden kann, liest hier trotzdem, was sie bedeutet.

2) "die zahlen sollen besser im kreis sein" -- vorher ein flacher Kreis
   mit Zahl. Jetzt ein doppelter Ring: aussen ein Farbverlauf als Rand,
   innen die dunkle Flaeche, dazu ein weicher Schein. Derselbe Kniff wie
   bei den Karten (Verlauf ueber border-box); der Kreis wirkt dadurch
   plastisch statt aufgemalt. Jede der vier Kacheln hat ihre eigene
   Farbe -- vier gleiche Kacheln wirken wie eine Aufzaehlung, vier
   unterscheidbare wie ein System.

3) "schrift soll spezieller sein" -- die Kachel-Ueberschrift war so gross
   wie der Text darunter und ging unter. Jetzt groesser, in der
   Headline-Schrift, enger gesetzt mit leicht negativer Laufweite. Die
   Ueberschrift "Gleich geht es los" bekommt einen Farbverlauf.

45/45 Handy-Abnahme, i18n vollstaendig, prefers-reduced-motion beachtet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 22:14:00 +02:00
DogFatherGitandClaude Opus 5 ad58ab99d3 Leeres Portal: zeigen statt erzaehlen
Rueckmeldung 22.08.2026 mit Bildschirmfoto: "dass muss noch viel
spezieller, krasser und geiler sein, nicht so einfach".

Der Kern des Problems war nicht die Optik, sondern die Haltung: Die Seite
BESCHRIEB, was hier bald stehen wird ("Eine Leiste zeigt dir, in welcher
Phase dein Projekt ist"). Beschreibungen sind schwach -- man muss sie
lesen und sich dann etwas vorstellen.

Jetzt steht dort eine echte, als Vorschau gekennzeichnete Projektkarte:
Projektnummer, Titel, eine Phasenleiste, die langsam durchlaeuft, die
Marke "Dogfather ist dran" und ein naechster Schritt. Man sieht in zwei
Sekunden, was drei Saetze nicht erklaeren.

Die Karte ist bewusst als Vorschau erkennbar -- gestrichelter Rand,
Etikett oben rechts, gedaempfte Schrift, Nummer P-0000-0000. Sie darf nie
mit einem echten Projekt verwechselt werden.

Dazu ein sehr langsamer Lichtstreifen, der einmal durchwandert. Er sagt
ohne Worte "hier passiert gleich etwas", ohne zu blinken oder zu zappeln
-- augenschonend bleibt Dauervorgabe.

Bei prefers-reduced-motion laeuft nichts, aber die Phasenleiste zeigt
trotzdem drei erledigte Phasen. Ohne das staende dort eine leere graue
Reihe und die Vorschau erklaerte gar nichts mehr -- ein abgeschaltetes
Element muss trotzdem noch seine Aussage transportieren.

45/45 Handy-Abnahme, i18n vollstaendig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 22:07:06 +02:00
DogFatherGitandClaude Opus 5 96458b58aa Anfrage mit einem Klick zu Kunde und Projektraum machen
Der wichtigste Handgriff im ganzen Bereich -- und der Weg, den man bei
JEDEM echten Kunden geht. Bisher haette man Name, E-Mail, Paket und
Wunschtermin von Hand in die Kundenmaske abgetippt: vier Gelegenheiten
fuer einen Tippfehler, und einer davon ist spaeter nicht mehr
korrigierbar, weil die E-Mail gleichzeitig der Anmeldename ist.

Jetzt steht in der Anfrage-Detailansicht eine Uebernahme mit Vorschau
(Kunde, E-Mail, Projekttitel, Sprache) und einem optionalen Preisfeld,
das die 30 % Anzahlung beim Tippen mitrechnet. Ein Klick legt an:
  * den Kundenzugang, freigeschaltet, in der Sprache der Anfrage
  * das Projekt, verknuepft mit der Anfrage, mit Wunschtermin und
    "Briefing-Termin vereinbaren" als erstem sichtbaren Schritt
  * die Anfrage wechselt automatisch auf "angenommen"
Danach springt die Ansicht auf "Kunden" und zeigt sofort den
Einladungslink -- den einzigen Weg, wie der Kunde an sein Passwort kommt.

Die beiden Schritte laufen bewusst nacheinander, nicht parallel: das
Projekt braucht die Kunden-Kennung. Schlaegt der zweite fehl, existiert
der Kunde trotzdem schon. Genau das sagt die Fehlermeldung dann auch
ausdruecklich -- sonst versucht man es blind noch einmal und scheitert an
der doppelten E-Mail-Adresse, ohne zu verstehen warum.

Ist aus einer Anfrage bereits ein Kunde geworden, erscheint statt der
Uebernahme ein Hinweis darauf. Zweimal denselben Kunden anzulegen soll
gar nicht erst angeboten werden.

Dabei denselben Anfuehrungszeichen-Fehler wie schon einmal gemacht und
behoben: das gerade " in „Kunden" beendet die JavaScript-Zeichenkette.
Typografisch richtig ist ohnehin das schliessende " -- beides in einem Zug.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 21:48:07 +02:00
DogFatherGitandClaude Opus 5 77674cb801 Kennzahlen der Verwaltung: aus stillen Zahlen werden Werkzeuge
Rueckmeldung 22.08.2026: "mach es noch krasser noch detaillierter noch
viel besser und perfekter und auch die kacheln viel geiler."

Die Kacheln waren eine Reihe stiller Zahlen. Zahlen, die man nur anschauen
kann, sind Dekoration. Jetzt:

* Jede Kachel FILTERT die Liste darunter beim Antippen. Als echter
  <button>, nicht als div mit Klickzuhoerer -- sonst ist sie mit der
  Tastatur nicht erreichbar und ein Screenreader kuendigt sie nicht als
  Bedienelement an.
* Jede Kachel sagt, was zu TUN ist, nicht nur wie viele es sind:
  "warten auf dich" / "du bist dran" / "Kunde ist dran" /
  "Projekt anlegen".
* Neue Kachel: der aelteste unbearbeitete Vorgang in Tagen. Das ist die
  ehrlichste Kennzahl ueberhaupt -- sie sagt nicht, wie viel man
  geschafft hat, sondern wie lange jemand schon auf Antwort wartet.
  Genau daran misst ein Kunde Zuverlaessigkeit. Ab sieben Tagen rot.
  Diese eine Kachel filtert bewusst NICHT und ist deshalb auch kein
  Knopf: sie zeigt einen Zustand, keinen Status.
* Eine Null wird gedaempft dargestellt. Sonst konkurriert "0 abgelehnt"
  optisch mit "3 neu" -- und genau die Drei ist die, auf die man schauen
  soll. Was nichts zu tun gibt, soll auch nicht leuchten.
* Gold bleibt ausschliesslich dem echten Handlungsbedarf vorbehalten.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 21:44:37 +02:00
DogFatherGitandClaude Opus 5 d93625439a Verwaltung: mehr Information pro Zeile statt leerer Flaeche
Rueckmeldung 22.08.2026 mit Bildschirmfoto: die Anfragenliste wirkte blass
und leer. Zu Recht -- sie zeigte Nummer, Name, Paket, Status, Datum. Das
ist korrekt, beantwortet aber nicht die Fragen, die man beim Draufschauen
WIRKLICH hat.

Jetzt steht in jeder Zeile:
* E-Mail direkt sichtbar (vorher musste man die Anfrage dafuer oeffnen)
* Paket, Budgetrahmen, Wunschtermin als eigene Marken
* Sprache, falls es NICHT Deutsch ist -- dann antwortet man auch in der
  richtigen Sprache
* ob daraus schon ein Kunde geworden ist

Statt eines Datums steht dort "vor 3 Tagen". Beim Datum muss man selbst
rechnen, wie lange jemand schon wartet -- und genau das ist die Frage,
die zaehlt. Ab sieben Tagen ohne Bearbeitung wird die Angabe rot: eine
Anfrage, die eine Woche liegt, ist ein verlorener Kunde.

Ein farbiger Streifen links codiert den Status zusaetzlich zur Textmarke.
Farbe ALLEIN waere fuer farbfehlsichtige Menschen keine Information --
zusammen mit dem Text ist sie ein schneller Anker beim Ueberfliegen.

Der leere Zustand stellt nicht mehr nur fest, dass nichts da ist, sondern
erklaert, WO Anfragen herkommen, und legt den Link zum Formular gleich
daneben.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 21:42:15 +02:00
DogFatherGitandClaude Opus 5 097e80b845 Projekte anlegen und ein leerer Zustand, der fuehrt statt zu enttaeuschen
Rueckmeldung 22.08.2026 mit Bildschirmfoto aus dem eigenen Testzugang:
"die seite soll jetzt schon bitte richtig krass sein und hoch profissionel,
auch sehr detailliert."

Zwei Luecken, beide geschlossen:

1) Projekte liessen sich nur ueber die Schnittstelle anlegen. Jetzt in der
   Verwaltung: pro Kunde ein "+ Projekt" mit Titel, Paket, Preis,
   Richttermin und naechstem Schritt. Bewusst als Einblendung direkt bei
   der Kundenzeile statt als eigene Seite -- man legt ein Projekt IMMER
   fuer einen bestimmten Kunden an, nie im luftleeren Raum. Der Kundenname
   steht deshalb gross im Formular, damit es nicht versehentlich dem
   Falschen angehaengt wird.
   Die 30 % Anzahlung wird beim Tippen live mitgerechnet. Sie wird zwar
   serverseitig berechnet, aber wer sie beim Eintippen sieht, merkt eine
   falsche Null sofort -- und nicht erst, wenn der Kunde ueberweisen soll.

2) Der leere Zustand im Portal war ein einziger Satz. Das ist eine
   verpasste Gelegenheit: Wer dort zum ersten Mal landet, hat gerade sein
   Passwort gesetzt und weiss noch nicht, was ihn erwartet -- genau dann
   entscheidet sich, ob die Seite souveraen wirkt oder unfertig. Jetzt
   zeigt er in vier Punkten, WAS gleich hier stehen wird (Projektstand,
   wer am Zug ist, Dateien und Nachrichten, Ideen jederzeit) und schliesst
   mit der Zusicherung, dass nichts zu tun ist. Fuenfsprachig.

45/45 Handy-Abnahme, i18n vollstaendig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 21:40:27 +02:00
DogFatherGitandClaude Opus 5 74091590b8 Verwaltung: nur noch EINMAL den Code eingeben
Rueckmeldung 22.08.2026: "ich hab auf der verwaltungs seite 2 mal dass ich
den zugangscode eingeben muss und vanvan auch, ich will es nur einmal
eingeben muessen." Berechtigt.

Die Zugangswand hat serverseitig laengst geprueft, WER da ist -- Dogfather
oder VanVan, laut Masterplan S.4 beide gleichberechtigte
Volladministratoren. Ein zweites Passwort danach bringt keinen
zusaetzlichen Schutz, es kostet nur jedes Mal Zeit.

Warum es nicht einfach "Cookie mitschicken" ist: Die Verwaltungsdaten
liegen hinter postfach.dogfather-universe.com, einem ANDEREN Rechnernamen.
Das Sitzungs-Cookie gilt dort nicht -- so sind Cookies gebaut, und das ist
gut so.

Loesung: Die Zugangswand stellt unter /webdesign/api-ausweis einen
kurzlebigen, signierten Ausweis aus, den die interne API anerkennt.
  - 15 Minuten gueltig, die Seite holt bei Bedarf still einen neuen
  - traegt bereich="wd-admin", damit ein Sitzungs-Token der Zugangswand
    hier NICHT durchgeht und umgekehrt
  - nur mit gueltiger Zugangssitzung zu bekommen
  - gilt AUSSCHLIESSLICH fuer die /webdesign-Endpunkte. Postfach,
    Bewerbungen, Supporter und Teamverwaltung bleiben unberuehrt

Fehlt WEBDESIGN_API_SECRET auf einer der beiden Seiten, gilt kein Ausweis
und die Verwaltung fragt wie bisher nach dem Team-Code. Ein fehlender
Konfigurationswert darf niemals eine Tuer oeffnen -- nur eine schliessen.
Genau das prueft der letzte Testfall.

Bei einem 401 wird EINMAL still ein neuer Ausweis geholt und die Anfrage
wiederholt, statt jemanden mitten im Arbeiten rauszuwerfen.

10/10 Tests, darunter: gefaelschte Signatur, fremdes Geheimnis,
abgelaufen, falscher Bereich, erfundene Rolle.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 21:36:11 +02:00
DogFatherGitandClaude Opus 5 2542a2598e Kunden und Projekte in der Verwaltung anlegen, inklusive Testzugang
Wunsch 22.08.2026: "ich will einen provisorischen zugang fuer mich".
Der richtige Weg dahin war ohnehin ueberfaellig -- Kundenzugaenge
entstehen ausschliesslich hier, es gibt bewusst keine Selbstregistrierung.

Neu:
* Kundenzugang anlegen (Name, E-Mail, Firma, Sprache, sofort freischalten
  ja/nein) mit Einladungslink
* "Testzugang fuer mich" -- ein Klick, legt einen als Test erkennbaren
  Zugang mit Datumsstempel an. Bewusst NICHT die echte Geschaeftsadresse:
  die E-Mail ist gleichzeitig der Anmeldename und laesst sich aus gutem
  Grund nicht mehr aendern, ein Test wuerde also spaeter mit einem echten
  Kundenkonto kollidieren.
* Zugaenge auflisten, freischalten, sperren, neuen Einladungslink erzeugen
* Projekte anlegen und aendern, Aenderungswuensche beziffern, Nachrichten

Entscheidungen:
* Der Einladungslink geht EINMAL im Klartext raus, direkt beim Anlegen.
  In der Datenbank liegt nur sein Hash. Wer ihn verliert, bekommt einen
  neuen -- das ist sicherer, als ihn dauerhaft abrufbar zu halten. Die
  Oberflaeche sagt das auch klar dazu, sonst klickt man ihn weg und
  wundert sich.
* Ein neuer Link entwertet alle offenen alten. Sonst sammeln sich mehrere
  gueltige Generalschluessel fuer dasselbe Konto an.
* "Sofort freischalten" ist eine bewusste Handlung. Ohne Haekchen wird der
  Zugang angelegt, kommt aber noch nicht hinein -- Masterplan S.12
  verlangt, dass Projektkauf oder Betreuung VOR der Freischaltung geprueft
  werden.
* Sperren beendet laufende Sitzungen sofort, nicht erst nach Ablauf.
* Die 30 % Anzahlung werden aus dem Preis BERECHNET, nicht eingetippt.
  Ein Tippfehler in der Anzahlung faellt sonst erst beim Geldeingang auf.
* Preise kommen als Euro herein und werden sofort in Cent umgerechnet
  (Math.round, damit 49.99 nicht zu 4998 wird). Ab da nie wieder Komma.
* Vier klar unterscheidbare Zustaende in der Liste. Wichtig vor allem
  "Einladung offen": freigeschaltet, aber noch kein Passwort gesetzt --
  der Kunde war also noch nie drin.
* Kopieren faellt auf Markieren zurueck, wenn die Zwischenablage
  blockiert ist. Eine Fehlermeldung waere dort nutzlos.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 21:23:15 +02:00
DogFatherGitandClaude Opus 5 1b432f456f Kundenportal: Oberflaeche (Masterplan S.12)
Vier Ansichten in einer Seite: Anmeldung, Passwort festlegen ueber den
Einladungslink, Uebersicht, Projektdetail. 71 Textbausteine in fuenf
Sprachen.

Korrektur an einer frueheren Entscheidung: Ich hatte das Portal als "nur
Deutsch" eingestuft wie die Verwaltung. Denkfehler -- die Verwaltung
nutzen Dogfather und VanVan, das PORTAL nutzen Kunden, und die koennen
Franzosen oder Portugiesen sein. Jetzt fuenfsprachig, und die Sprache des
Kunden wird bei der Anmeldung automatisch uebernommen.

Gestaltungsentscheidungen:
* Eine Phasenleiste zeigt auf einen Blick, wo das Projekt steht. Das ist
  die haeufigste Frage ueberhaupt und der Grund, warum Kunden anrufen.
  Bei pausierten oder abgebrochenen Projekten wird KEINE Leiste gezeigt --
  ein Fortschrittsbalken waere dort irrefuehrend.
* Eine Marke sagt, wer gerade am Zug ist ("Wir warten auf dich" /
  "Dogfather ist dran"). Das beendet die haeufigste Unklarheit im
  Projektverlauf.
* Offene Zahlungen stehen ganz oben und in Gold -- das Einzige, was den
  Kunden wirklich zum Handeln auffordert.
* Rueckfrage nur beim ANNEHMEN eines Zusatzangebots, nicht beim Ablehnen.
  Annehmen erzeugt eine Zahlungspflicht, Ablehnen ist folgenlos.
* Abgelaufene Sitzung fuehrt zur Anmeldung mit klarer Ansage statt zu
  einer leeren Seite, die wie "du hast keine Projekte" aussaehe.
* Der Einladungslink wird nach Gebrauch aus der Adresszeile entfernt --
  er ist verbraucht und hat in der Chronik nichts verloren.
* Die Zahlungsknoepfe sagen ehrlich, dass PayPal noch eingerichtet wird,
  statt so zu tun als wuerde etwas passieren.

Pruefskript zweimal geschaerft, beide Male waren es Fehlalarme:
* Die Heuristik fuer Nachschlagetabellen hielt gewoehnliche Woerter wie
  "abnahme" und "uebergeben" fuer Woerterbuch-Schluessel. Jetzt muss ein
  Unterstrich im Namen sein, so wie bei allen echten Schluesseln.
* Eine Ueberschrift gilt jetzt auch als uebersetzt, wenn die Uebersetzung
  INNEN sitzt (fester Teil + dynamischer Teil).

i18n vollstaendig, 45/45 Handy-Abnahme.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 21:17:22 +02:00
DogFatherGitandClaude Opus 5 04a1af1253 Kundenportal: Server-Seite mit strikter Mandantentrennung
Masterplan S.12. Anmeldung, Uebersicht, Projektdetail, Aenderungswuensche,
Zusatzangebote annehmen/ablehnen, Nachrichten, Profil.

DIE ZENTRALE ENTSCHEIDUNG: Die Trennung zwischen Kunden sitzt NICHT in der
Oberflaeche, sondern in jeder einzelnen Abfrage. Ueberall steht
"WHERE id = ? AND kunde_id = ?" statt "WHERE id = ?", wobei die kunde_id
IMMER aus der Sitzung kommt, nie aus der Anfrage. Wer eine fremde
Projektkennung errät, bekommt dadurch "nicht gefunden" statt Daten. Die
Oberflaeche liegt im Browser des Kunden und ist beliebig manipulierbar --
sie kann diese Aufgabe grundsaetzlich nicht uebernehmen.

Weitere bewusste Entscheidungen:
* KEINE Selbstregistrierung. Masterplan S.12 verlangt, dass Projektkauf
  oder Betreuung VOR der Freischaltung geprueft werden. Ein Portal, in das
  sich jeder selbst eintraegt, waere das Gegenteil. Kunden werden in der
  Verwaltung angelegt und bekommen einen Einladungslink.
* Passwoerter mit scrypt (in Node eingebaut, kein Zusatzpaket). Ein
  SHA-256 ueber ein Passwort ist milliardenfach pro Sekunde durchprobierbar;
  scrypt ist absichtlich langsam UND speicherhungrig.
* Mindestanforderung ist LAENGE, nicht Zeichenklassen. "Hund1234!" erfuellt
  jede Klassenregel und ist trotzdem schlecht; "mein blauer stuhl steht
  krumm" erfuellt keine und ist ausgezeichnet.
* Gleiche Fehlermeldung bei unbekannter Adresse und falschem Passwort --
  sonst lassen sich Kundenadressen durchprobieren.
* Die Anmeldesperre haengt an der E-Mail, nicht an der IP. Eine IP-Sperre
  wuerde mehrere Kunden hinter demselben Firmenanschluss gemeinsam
  aussperren -- genau der Fehler, der im Universe am 19.08.2026 auftrat.
* Sperre und Passwortwechsel beenden laufende Sitzungen SOFORT, nicht erst
  nach zwoelf Stunden.
* Ein Zusatzangebot laesst sich nur annehmen, wenn es beziffert ist --
  sonst entstuende eine Zahlungspflicht ohne Preis.

Dazu der wichtigste Test des Bereichs (test-webdesign-portal.mjs): zwei
echte Kunden, und Kunde B versucht systematisch mit Kunde As echten
Kennungen an dessen Projekt, Nachrichten, Dateien und Zusatzangebote zu
kommen. Prueft ausserdem, dass interne Notizen und interne Arbeitsdateien
auch dem BERECHTIGTEN Kunden verborgen bleiben.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 19:12:56 +02:00
DogFatherGitandClaude Opus 5 ae9878cb4c Sprungmarken auf breiten Bildschirmen alle in einer Reihe
Rueckmeldung 22.08.2026: "alle neben einander bitte". Vorher passten vier
in die Zeile und die fuenfte rutschte allein darunter -- das sieht aus wie
ein Versehen, nicht wie eine Gestaltung.

Ab 700px teilen sich jetzt alle fuenf den Platz zu gleichen Teilen
("flex: 1 1 0" statt "auto"). Mit "auto" waere "Preise & Zahlung" schmal
und "Inhalte & Zusammenarbeit" breit gewesen -- ungleiche Kacheln in einer
Reihe, also genau das, was hier schon einmal bemaengelt wurde.
"min-width: 0" ist dabei noetig, weil Flex-Elemente sonst nicht unter ihre
Inhaltsbreite schrumpfen und die Reihe trotzdem umbrechen wuerde.

Nachgemessen bei 700 / 900 / 1280 / 1440px: 5 Knoepfe, 1 Reihe, alle
gleich breit UND gleich hoch. Unter 700px bleibt der Umbruch -- fuenf
Spalten waeren auf einem Handy unlesbar schmal.

45/45 Handy-Abnahme weiterhin sauber.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 19:08:47 +02:00
DogFatherGitandClaude Opus 5 9f74167f84 Sprungmarken brechen um statt zu scrollen, keine Silbentrennung in Ueberschriften
Rueckmeldung 22.08.2026: "ich will nicht dass man die hin und her
schieben muss man soll die alle sehen aber nicht schieben muessen."

Richtig, und aus zwei Gruenden: Eine Wischleiste verbirgt, DASS es noch
mehr gibt -- was rechts aus dem Bild ragt, existiert fuer die meisten
Menschen schlicht nicht. Dazu kam ein haesslicher Scrollbalken quer ueber
die Seite. Jetzt brechen die Knoepfe um, alle fuenf sind auf einen Blick
da, auch bei 320px.

Der Text in den Knoepfen darf dabei mitbrechen (white-space: normal) --
ohne das sprengt ein langer Name wie "Buchhaltungs- &
Steuerverwaltungsseiten" auf schmalen Bildschirmen die Zeile und der
waagerechte Ueberlauf waere durch die Hintertuer zurueck.

Dabei mitgefunden: "hyphens: auto" auf Ueberschriften. Auf dem Handy
stand dadurch "Alles, was vorher ge-klaert sein sollte". Der Browser
trennt damit nach Silben, auch wenn ueberhaupt kein Platzproblem
besteht. In Fliesstext ist das ein Gewinn, in grossen Ueberschriften
sieht es billig aus -- und genau die sind das Erste, was jemand sieht.
overflow-wrap: break-word bleibt und faengt echte Ueberlaeufe weiterhin ab.

Pruefskript: die Sprungmarken-Leisten waren als "absichtlich scrollbar"
von der Ueberlaufpruefung ausgenommen. Diese Ausnahme ist raus -- sonst
wuerde ein zurueckkehrender Ueberlauf dort nie auffallen. 45/45 weiterhin
sauber.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 19:03:30 +02:00
DogFatherGitandClaude Opus 5 de4a90362e Verwaltungsbereich fuer Projektanfragen
Masterplan S.13. Anfragen ansehen, filtern, durchsuchen, Status aendern,
interne Notizen, archivieren. 9/9 Sicherheitstests.

Bewusste Entscheidungen:

* NUR AUF DEUTSCH. Der oeffentliche Teil laeuft in fuenf Sprachen, weil
  dort Kunden landen. Hier landen ausschliesslich Dogfather und VanVan --
  fuenf Sprachen waeren fuenffache Pflege und fuenffache Fehlerflaeche
  ohne einen einzigen Nutzer, der sie braucht. Genau wie der bestehende
  interne Bereich des Universe.

* Anmeldung ueber die BESTEHENDE Team-Anmeldung (/auth/login) statt einer
  zweiten eigenen. Zwei Anmeldungen fuer dieselben zwei Personen waeren
  doppelte Pflege und ein zweiter Ort, an dem ein Zugang vergessen werden
  kann. Der Code der Zugangswand gilt hier ausdruecklich NICHT --
  die Verwaltung steckt hinter zwei getrennten Tueren.

* Sitzungstoken im sessionStorage, nicht localStorage: es verschwindet
  beim Schliessen, dieselbe Regel wie an der Zugangswand. Der Test prueft
  das ausdruecklich.

* Bei abgelaufener Sitzung geht es zurueck zur Anmeldung statt zu einer
  leeren Liste. Eine leere Liste sieht aus wie "keine Anfragen" und ist
  damit eine stille Falschaussage.

* Gold nur beim Status "neu" -- also genau dort, wo wirklich etwas zu tun
  ist. Wuerde alles leuchten, leuchtet nichts.

* Archivieren statt Loeschen, und die Oberflaeche sagt das auch dazu.

Pruefskript: kennt jetzt bewusst einsprachige Seiten (verwaltung, portal)
und prueft dort nur die Struktur, statt ein fehlendes Woerterbuch zu
melden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 18:54:58 +02:00
DogFatherGitandClaude Opus 5 eaeaec6b56 Anfrageformular fertig - vier Schritte, Zwischenspeicherung, echte Absendung
Masterplan S.10 "Projektanfrage ohne Informationsverlust" umgesetzt.
End-to-End gegen die echte Datenbank belegt: Anfrage A-2608-0002 liegt drin.
38/38 Browsertests, 45/45 Handy-Abnahme.

Aufbau: 4 Schritte + Zusammenfassung + Dankeseite.
- Zwischenspeicherung nach jedem Tastendruck. Geschlossener Tab, leerer
  Akku oder versehentliches Zurueck kosten keine Arbeit. Entwuerfe
  verfallen nach 30 Tagen -- ein halbes Jahr alter Entwurf verwirrt mehr
  als er hilft.
- Folgefragen erscheinen nur passend zur Auswahl und werden beim
  Zurueckwechseln NICHT mitgeschickt, sonst landen alte Shop-Antworten in
  einer Onepager-Anfrage.
- Die Zusammenfassung wird aus den Feldern gelesen, nicht aus einem
  nebenher gepflegten Objekt -- sie kann dadurch nie etwas anderes
  behaupten als das, was tatsaechlich abgeschickt wird.
- Telefon wird zur Pflicht, sobald "Per Telefon" gewaehlt ist. Ein Wunsch,
  der nicht erfuellbar ist, ist schlimmer als eine Pflichtangabe.

Drei Fehler, die erst der Test gefunden hat:
1. Der "Zurueck"-Knopf stand auch auf Schritt 1 da. Ursache: das
   hidden-Attribut wird nur ueber "display:none" umgesetzt und verliert
   gegen jedes eigene display -- und Knoepfe sind inline-flex. Zentral
   behoben ueber ".wd [hidden] { display:none !important }".
2. Nach erfolgreichem Absenden legte zeigeSeite() den geloeschten Entwurf
   sofort wieder an. Beim naechsten Besuch haette eine bereits gesendete
   Anfrage erneut im Formular gestanden.
3. Der Server meldet bei Spamverdacht bewusst Erfolg ohne Nummer. Ein
   echter Mensch, der zufaellig sehr schnell war, haette eine Dankeseite
   mit "—" gesehen und auf eine Antwort gewartet, die nie kommt. Jetzt
   ein ehrlicher Hinweis mit zweitem Weg, und der Entwurf bleibt erhalten.

Pruefskripte geschaerft:
- i18n-Pruefung liest jetzt auch assets/js/wd-<seite>.js, sonst meldet sie
  reihenweise "unbenutzt" fuer Schluessel, die aus dem Seitenskript kommen.
- Handy-Abnahme ueberspringt absichtlich unsichtbare Eingabefelder
  (Auswahlkacheln, Honigtopf). Fehlalarme sind das Ende jeder Pruefung,
  weil man sie irgendwann pauschal ignoriert.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 18:47:45 +02:00
DogFatherGitandClaude Opus 5 8ace955740 Service Worker lieferte alte Stilvorlagen aus - Korrekturen blieben unsichtbar
Gemeldet 22.08.2026 mit Bildschirmfoto: "immer noch so" -- das Logo im
Marken-Intro stand weiterhin hochkant gestreckt da, obwohl die Korrektur
(height:auto gegen die height-Attribute) laengst live war.

Ursache war NICHT die Korrektur, sondern mein eigener Service Worker. Er
lief fuer /assets/ als "stale-while-revalidate": die gespeicherte Fassung
geht sofort raus, im Hintergrund wird eine frische geholt. Das ist schnell
-- bedeutet aber, dass jede Aenderung an CSS oder JavaScript beim ersten
Aufruf unsichtbar bleibt und erst beim zweiten wirkt. Ein behobener Fehler
sieht dadurch aus wie ein nicht behobener. Die unangenehmste Sorte.

Nachgemessen ist das Logo jetzt 160x161px bei einem natuerlichen
Verhaeltnis von 472x476 -- also unverzerrt. Vorher erzwang das Attribut
height="476" bei 160px Breite ein Verhaeltnis von 0,34 statt 0,99, genau
das lange Gesicht auf dem Bildschirmfoto.

Behoben:
- Stilvorlagen und Skripte laufen jetzt "Netz zuerst", Zwischenspeicher
  nur als Notfallnetz. Richtigkeit vor Millisekunden.
- Bilder und Symbole bleiben beim schnellen Weg -- die aendern sich
  praktisch nie und bekaemen sonst einen neuen Dateinamen.
- CACHE_NAME auf v2 gesetzt, damit der alte Speicher beim naechsten Start
  vollstaendig weggeworfen wird.
- Versionsnummer an allen CSS/JS-Verweisen hochgesetzt, damit es sich auch
  ohne Service Worker sofort selbst heilt.

Ausserdem: Woerterbuch fuer das Anfrageformular, 101 Textbausteine in fuenf
Sprachen (vier Schritte, Zusammenfassung, Fehlermeldungen).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 18:34:15 +02:00
DogFatherGitandClaude Opus 5 29a054c359 Bilder waren hochkant gestreckt und ungleich hoch, Abstaende halbiert
Rueckmeldung 22.08.2026 per Bildschirmfoto: "die sollen alle gleich
aussehen und nicht einer groesser oder kleiner" und "ich will nicht soooo
viel abstand".

1) Verzerrte, ungleich hohe Projektbilder
   Nachgemessen: 576px breit, aber 750px bzw. 696px hoch -- also hochkant
   gestreckt und unterschiedlich, obwohl im CSS sauber "aspect-ratio:
   16/10" stand. Ursache: die width/height-Attribute im HTML (die dort
   bewusst stehen, damit der Browser vor dem Laden den Platz reserviert
   und die Seite nicht springt) wirken wie eine CSS-Hoehe und schlagen
   aspect-ratio. Fix zentral ueber ".wd img[width][height] { height:
   auto }" statt in jeder einzelnen Regel -- so kann es bei einem neuen
   Bild nicht vergessen werden.
   Jetzt beide 576x360, Karten beide 853px hoch.

2) Zu viel Leerraum
   .wd-abschnitt hatte 100,8px oben UND unten, also gut 200px zwischen
   zwei Abschnitten. Halbiert auf 57,6px, Hero von 78svh auf 68svh.
   Seitenlaenge dadurch 8798px -> 7089px bei gleichem Inhalt.

3) Zwei neue Dauerpruefungen in pruefe-webdesign-handy.mjs, damit genau
   diese beiden Fehlerarten nicht wieder per Bildschirmfoto auffallen
   muessen:
   - verzerrte Bilder (gewuenschtes vs. tatsaechlich gerendertes
     Seitenverhaeltnis, 2 % Toleranz)
   - ungleich hohe Karten innerhalb EINER Rasterzeile

40/40 Abnahme weiterhin sauber.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 18:26:07 +02:00
DogFatherGitandClaude Opus 5 356bb974b8 Knopf war unlesbar, und der Code wird jetzt bei jedem Schliessen neu verlangt
Zwei Rueckmeldungen vom 22.08.2026, beide behoben und mit Tests abgesichert.

1) "das sieht nicht gut aus" (Bildschirmfoto des Hauptknopfs)
   Ursache: ".wd a" ist Klasse+Element und damit spezifischer als die reine
   Knopfklasse ".wd-btn--haupt". Die Textfarbe des Knopfs wurde dadurch
   ueberstimmt -- hellblaue Schrift auf hellblauem Grund, praktisch
   unlesbar. Derselbe Spezifitaetsfehler wie zuvor bei den Namen auf der
   Zugangswand. Fix: ".wd a:not(.wd-btn)" plus zweistufig geschriebene
   Knopfregeln, damit das nicht wieder passieren kann.

2) "das sieht lang gezogen aus"
   Die Pillenform (border-radius 999px) laesst breite Knoepfe
   auseinandergezogen wirken, weil der Radius optisch mit der Breite
   mitwaechst. Jetzt fester Radius von 14px -- gleiche Form bei jeder
   Breite, ruhiger und hochwertiger.

3) "ich will das ich jedes mal den code gefragt werde wenn man die seite
   zu macht"
   Das Sitzungs-Cookie allein reicht dafuer nicht: Browser stellen genau
   solche Cookies beim Wiederherstellen von Tabs zurueck ("Dort
   fortfahren, wo du aufgehoert hast"), man landet dann ohne Codeabfrage
   wieder mitten in der Seite. Zusaetzlich jetzt eine Sitzungsmarke im
   sessionStorage, die beim echten Schliessen verschwindet. Fehlt sie bei
   vorhandenem Cookie, wird die Sitzung serverseitig beendet und zur
   Zugangswand geleitet. Token-Notbremse von 24 auf 8 Stunden gesenkt.
   13/13 Tests im echten Browser, inklusive Schutz vor Endlosschleife.

DEPLOY.md: zwei Checkouts auf dem Server dokumentiert (/home/dogiweb und
/home/dogiintern) und der Vorfall, dass /webdesign nach dem Pull kurz ohne
Zugangsschutz erreichbar war, weil der Dienstneustart fehlte.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 18:20:34 +02:00
DogFatherGitandClaude Opus 5 4b3ec450d5 Webdesign-Bereich: Fundament, oeffentliche Seiten, Zugangsschutz, PayPal
Umsetzung des "Website Masterplan" (16 Seiten) unter /webdesign.

Zugangsschutz mit EIGENER Schranke (server/webdesign-gate.js) statt gate.js:
gate.js laesst seit dem oeffentlichen Start am 21.08.2026 jeden durch, weil
die Pruefung auf SITE_PUBLIC_LAUNCH_AT als allererste Zeile steht. Haette man
/webdesign dahintergehaengt, waere der ausdruecklich nicht-oeffentliche Bereich
inklusive Preisen und spaeteren Kundendaten ab der ersten Sekunde fuer jeden
lesbar gewesen. Eigenes Sitzungs-Cookie, bereich="webdesign" im Token, damit
ein gueltiges Universe-Cookie hier NICHT gilt. 37/37 Tests.

Sieben oeffentliche Seiten in fuenf Sprachen (de, de-CH mit echtem Dialekt, en,
fr, pt). Preise, Zeitrahmen, Paketnamen und die 30-%-Regel stehen an genau
EINER Stelle in wd-core.js -- der Masterplan verlangt "ueberall
widerspruchsfrei", und vier Kopien laufen bei der ersten Preisaenderung
auseinander.

Als App installierbar auf Handy und PC. Der Service Worker speichert bewusst
KEINE HTML-Seite zwischen: nach dem Abmelden wuerden sonst geschuetzte Seiten
weiter ausgeliefert, ohne dass der Server je gefragt wird. 18/18 Tests.

Handy-Abnahme ueber alle Seiten in fuenf Breiten (320-1440) und fuenf Sprachen:
40/40. Der Test fand 35 echte Fehler (Touch-Ziele unter 44px), behoben im
Designsystem statt einzeln pro Seite.

PayPal (Wunsch 22.08.2026 "sofort auf meinem paypal"): Orders API mit
intent=CAPTURE, also sofortiger Einzug statt blosser Reservierung. Gebuehr und
Nettobetrag getrennt gespeichert. Betraege durchgehend als Ganzzahl in Cent.
Gefaelschte Webhooks werden abgewiesen. Fail closed solange Zugangsdaten
fehlen. 28/28 Tests gegen einen nachgebauten PayPal-Server.

Datenbank: 15 Tabellen mit Praefix wd_, fachlich vollstaendig vom Universe
getrennt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 18:06:52 +02:00
DogFatherGitandClaude Opus 5 2010b5ec33 Live benutzte Hintergrundbilder + Stimmen-Avatare in die Ablage aufgenommen
Beim Abgleich zwischen Rechner und Netcup-Server am 22.08.2026 aufgefallen:
20 Bilddateien wurden live von mehreren Seiten benutzt (bewerben-modi.html,
links.html, medien-casper.html, medien-hasidog.html, supporter.html,
stimmen.html u.a.), lagen aber in keiner Git-Ablage -- nur auf diesem
Rechner und auf dem Server. Waeren beide verloren gegangen, waeren diese
Seiten ohne Hintergrund/Avatare dagestanden.

Vor dem Hinzufuegen jede Datei per Pruefsumme gegen den Server abgeglichen
-- alle identisch, kein abweichender Stand wird hier verdeckt.

dogfather-universe-app.ico ist aktuell nirgends verlinkt (vermutlich ein
nie eingebauter Favicon-Entwurf), trotzdem mit gesichert statt verworfen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 15:47:38 +02:00
DogFatherGitandClaude Opus 5 541a91d9e2 DEPLOY.md korrigiert: echte Seite laeuft auf Netcup, nicht auf Cloudflare
Die Anleitung nannte als Deploy-Weg weiterhin "npx wrangler deploy". Das ist
seit dem oeffentlichen Start (21.08.2026) falsch und aktiv gefaehrlich: der
Befehl laeuft fehlerfrei durch und meldet Erfolg, erreicht aber nur noch
...workers.dev. Die echte Domain wird von Caddy auf dem Netcup-Server
ausgeliefert (dogiweb.service, localhost:4100). Genau darauf bin ich heute
beim Sprachfenster-Fix hereingefallen -- Deploy "erfolgreich", auf dem Handy
aber unveraendert kaputt.

Neu dokumentiert: der einzige gueltige Weg (commit -> push gitea master:main
-> git pull auf dem Server), die Zweig-Falle master/main, die Pflichtpruefung
gegen die echte Domain statt gegen die Deploy-Meldung, warum die
wrangler-Fehlermeldung "externally managed DNS records" erwuenscht ist, und
als offener Punkt: mehrere live benutzte Hintergrundbilder liegen in keiner
Git-Ablage.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 15:27:08 +02:00
DogFatherGitandClaude Opus 5 e5d55137d4 Zwei Server-Reparaturen zurueckgeholt, die es nur auf dem Server gab
Beim Abgleich der beiden Staende (Rechner vs. Netcup-Server) gefunden --
beide Aenderungen liefen bereits live, waren aber nirgends gespeichert und
haetten beim naechsten Deploy vom Rechner aus verloren gehen koennen:

1. gate.html: autofocus im Dogi-Feld entfernt (21.08.2026, Rueckmeldung von
   Diene -- der Cursor sprang nach dem Laden zurueck nach oben, die eigene
   Kachel scrollte dabei aus dem Bild).
2. verwaltung.html: "Oeffnen"-Knoepfe wurden auf dem Handy vom
   overflow:hidden der Karte abgeschnitten (22.08.2026); jetzt
   overflow:visible plus etwas mehr Unterabstand.

Inhalt unveraendert vom Server uebernommen, nur hier nachgetragen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 15:24:35 +02:00
DogFatherGitandClaude Opus 5 3f9997456a Handy: Sprachfenster (DE) klappte halb aus dem Bild auf
Nutzer-Report per Foto: beim Tippen auf "DE" im Handy-Menue erschien das
Sprachfenster halb links ausserhalb des Bildschirms, man konnte keine
Sprache mehr lesen oder treffen.

Ursache: Die Handy-Regel, die alle Aufklapp-Fenster auf statische Position
und volle Breite umstellt, hat den Sprachschalter nicht erreicht -- seine
Desktop-Regel (.sprach-schalter .nav-dropdown, 0-2-0) ist spezifischer,
und eine @media-Abfrage erhoeht die Spezifitaet nicht. Dadurch lief die
Desktop-Animation "sprach-dropdown-in" weiter, die per fill-mode:both
dauerhaft transform: translateX(-50%) setzt. Auf dem Desktop korrekt
(schmales Fenster mittig unter dem Knopf), im Handy-Menue aber toedlich:
das Fenster ist dort bildschirmbreit und wurde um seine halbe Breite nach
links geschoben (gemessen live: x = -148px bei 390px Viewport).

Fix: dieselben Handy-Regeln erneut mit ausreichender Spezifitaet fuer den
Sprachschalter, samt Kommentar zur Begruendung.

Geprueft mit Playwright (echter Browser): 390x844, 360x640, Querformat
844x390 und Tablet 768x1024 -> Fenster jeweils vollstaendig im Bild, alle
5 Sprachen sichtbar; Desktop 1920 unveraendert schmal und zentriert;
Sprachwechsel (English) funktioniert weiterhin.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 11:27:22 +02:00
DogFatherGitandClaude Sonnet 5 adb1042915 Namenskorrektur Cigdem (statt Cidgem) auch lokal + in allen 5 Sprachen nachgezogen
War beim Server-Deploy schon als Konflikt aufgefallen (Diene/das Team hatte
die richtige Schreibweise bereits korrigiert) -- hier fehlte die Korrektur
noch in i18n-zeitreise.js (alle 5 Sprachen) sowie in der HTML-Fallback-
Version, weil mein ursprünglicher Fix von heute Nachmittag ("Cidem" ->
"Cidgem") selbst schon falsch war.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 23:39:29 +02:00
DogFatherGitandClaude Sonnet 5 1f0ec994a9 Drei Handy-Nachbesserungen: Trio-Bild hinter dem Text, auffälliger Menü-Knopf, abgeschnittener Öffnen-Knopf
Nutzer-Report mit drei Bildern vom Handy:

1. HERO-BILD SASS NICHT MEHR HINTER DEM TEXT (index.html)
   .hero-artwork hat "inset:0" -- füllt also die GESAMTE Höhe von
   .hero-cinematic, und diese Sektion umschließt nicht nur die Überschrift,
   sondern auch die 3 Welt-Kacheln darunter. Auf dem Handy wird die Sektion
   durch den 4-zeilig umbrechenden Titel + Kacheln riesig (~1570px bei
   390px Breite) -- das Trio-Bild (16:9, contain-skaliert an die Breite)
   wurde dadurch nur ~220px hoch und mittig in dieser riesigen Fläche
   zentriert, landete also als schmaler Streifen zwischen den Buttons und
   den Welt-Kacheln statt hinter der Überschrift.
   Fix: eigenes festes Seitenverhältnis (4:3) für die Bildbox auf dem
   Handy, von OBEN verankert statt zentriert, background-size auf "cover"
   -- Verlauf eigens für die neue, kürzere Box abgestimmt (die
   Desktop-Version wäre zu abrupt gewesen). Über 6 Breiten (320-768px)
   mit Playwright gegengeprüft.

2. MENÜ-KNOPF ZU UNAUFFÄLLIG (main.js + main.css, alle Seiten)
   "da soll auch menü stehen und die 3 striche" + "die kiste soll auch
   leicht eine andere farbe haben so dass sie auffällt, eine leicht blau
   tönung". Sichtbares "Menü" neben den drei Strichen ergänzt (neuer i18n-
   Schlüssel nav_toggle_label, alle 5 Sprachen) + dezente Babyblau-Tönung
   (8% Deckkraft, passend zur Markenfarbe --accent). Dabei einen
   unabhängigen, zweiten Bug gefunden und mitbehoben: durch die neue
   Knopfbreite/-höhe wurde ein Rechenfehler im geschlossenen Mobilmenü
   sichtbar -- "translateY(-110%)" reicht nur, wenn das Panel mindestens
   10x so hoch ist wie der Kopfbereich; war das nicht der Fall, ragte die
   Unterkante (der Sprachschalter) ein paar Pixel sichtbar ins Bild.
   Robusterer Ersatz: -100% + fester 200px-Puffer, unabhängig von Panel-
   und Kopfbereichshöhe. Über 3 Breiten gegengeprüft (Panel unsichtbar UND
   öffnet weiterhin normal).

3. "ÖFFNEN"-KNOPF AUF DER ZUGANGSSEITE ABGESCHNITTEN (gate.html)
   .gate-card trägt "flex: 1 1 220px" für die Desktop-Reihe (220px als
   BREITE gedacht). Sobald @media max-width:480px auf flex-direction:column
   umschaltet, gilt dieselbe Zahl als HÖHE -- die Karte wurde auf genau
   220px Höhe gequetscht. overflow:hidden setzt zusätzlich das normale
   Mindestmaß (min-height:auto) auf 0 herunter, wodurch nichts das
   Zusammenquetschen verhinderte -- der "Öffnen"-Knopf wurde unten
   abgeschnitten. Fix: flex:none in genau diesem Media-Block, Breite bleibt
   weiterhin über width:100%/max-width geregelt. An 4 Breiten x 3 Kacheln
   (Dogi/VanVan/Diene) gegengeprüft: Karten jetzt 258px statt 220px hoch,
   Knopf komplett sichtbar.

Vollständiger Regressionslauf: alle 34 Seiten bei 390px (kein Überlauf,
Menü-Knopf überall erreichbar und beschriftet), Desktop bei 1920px
unverändert (Hamburger weiterhin nur unter der bestehenden 1650px-
Schwelle sichtbar).

Cache-Busting-Version auf 20260821t erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 23:37:14 +02:00
DogFatherGitandClaude Sonnet 5 57966e24d0 Automatischer öffentlicher Start um 21 Uhr + Countdown auf der Zugangsseite
Nutzer-Wunsch 21.08.2026: "ich will dass du auch ein countdown zu der
seite hinzufügst wo wir den zugangscode eingeben müssen. den um 21h heute
abend geht die seite live. kannst du das sogar so anpassen dass es
automatisch läuft?"

server/gate.js:
- Neue Einstellung SITE_PUBLIC_LAUNCH_AT (ISO-Zeitstempel mit Zeitzone).
  Ab diesem Moment lässt gateMiddleware ausnahmslos jeden durch -- ganz
  ohne Neustart oder manuellen Eingriff, weil jede Anfrage die aktuelle
  Serverzeit live neu prüft. Die Freischaltung "passiert" also von selbst
  in der Sekunde, in der die Uhrzeit erreicht wird. Vorher bleiben die
  Zugangscodes unverändert nötig, damit das Team schon vorher rein kann.
  Fail-safe statt fail-open geprüft: ein kaputter/unparsbarer Zeitwert
  (z.B. Tippfehler in der .env) lässt die Schranke aktiv, statt die Seite
  versehentlich für alle zu öffnen.
- Neuer öffentlicher Endpunkt GET /gate-launch-info (immer erreichbar,
  auch ohne gültige Sitzung) liefert launchAt/isLive/serverTime für die
  Countdown-Anzeige im Frontend.

gate.html:
- Neue Countdown-Box zwischen Titel-Karte und den Zugangscode-Kacheln
  (bleibt unsichtbar, solange kein Starttermin konfiguriert ist). Rechnet
  auf der SERVERZEIT statt der eigenen Uhr (einmaliger Zeit-Abgleich beim
  Laden), damit eine falsch gehende Besucher-Uhr weder zu früh noch zu
  spät zählt. Bei Erreichen von Null folgt ein letzter Abgleich mit dem
  Server, bevor automatisch zur Zielseite weitergeleitet wird -- kein
  Klick, kein Neuladen nötig.
- Zugangscode-Kacheln (Dogi/VanVan/Diene) bleiben während des Countdowns
  unverändert nutzbar.

Getestet: 18 Middleware-Tests (inkl. Fail-safe bei kaputtem Zeitwert,
weiterhin funktionierender Zugangscode vor dem Start) + 10 Playwright-
Tests der Countdown-Oberfläche (Anzeige, Format, automatischer Sprung bei
Ablauf, sofortige Weiterleitung falls schon live, stiller Fallback bei
Netzwerkfehler). Zusätzlich alle 32 echten Seiten auf PC-Installierbarkeit
geprüft (Manifest, Icons, Service Worker, Install-Knopf, echter
Install-Klick-Ablauf simuliert) -- keine Probleme gefunden.

Cache-Busting-Version auf 20260821s erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 19:25:12 +02:00
DogFatherGitandClaude Opus 5 41faf22614 Handy: vollständige Prüfung von Inhalten, Knöpfen und allen Systemen
Nachtrag auf Nachfrage ("hast du alles geprüft oder nur die menü
leisten?") -- vorher hatte ich nur Navigation, Seitenbreite, Tippflächen
und Hoch/Querformat geprüft. Jetzt zusätzlich Bilder, Texte, Knöpfe und
die kompletten Abläufe.

Geprüft über alle 35 Seiten bei 393px:
- Bilder: kein einziges kaputtes Bild, keine fehlenden alt-Texte
- Dateien: keine 404er
- Knöpfe/Links: alle beschriftet (keine namenlosen Bedienelemente)
- Text: nichts wird abgeschnitten. Die zunächst gemeldeten "Überläufe"
  bei .btn/.card waren Fehlalarme -- sie stammen von den dekorativen
  Leucht-Ebenen (::before mit negativem inset), echter Text ragt
  nirgends heraus (einzeln gegengeprüft).

Zwei echte Funde behoben:
1. Marken-Unterzeile war mit 8,96px zu klein zum Lesen (hatte ich beim
   Handy-Fix selbst so verkleinert). Jetzt 11px -- Platz ist da, seit der
   Menü-Knopf eigenständig rechts sitzt. Über acht Breiten gegengeprüft,
   Kopfleiste bleibt überall stabil.
2. "Aktiv"-Ankreuzfelder in der Verwaltung waren 22px. Jetzt 26px, die
   ganze Beschriftungszeile ist 44px hoch und schaltet mit um.

Abläufe am Handy durchgespielt (echte Fingertipps, Fake-Backend):
- Registrierung über alle drei Schritte inkl. falschem Code
- Login mit falschem und richtigem Zugangscode
- Abmelden auf supporter.html
- Stimme einreichen inkl. Profilbild-Auswahl
- Verwaltung: Team-Mitglied anlegen, Stimme freigeben, alle Bereiche
  sichtbar, kein Überlauf

Cache-Busting-Version auf 20260821r erhöht.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-21 18:33:52 +02:00
DogFatherGitandClaude Opus 5 6910112d97 Stimmen: nur noch die zwei Originalfiguren + eigenes Bild hochladen
Nutzer-Wunsch 21.08.2026: "benutz da bitte nur die originalen husky und
hasen. mach nur die zwei. und die auswahl wo die leute selbst ein bild
rein setzen können."

- Auswahl von acht auf zwei reduziert (DogFather-Husky, HasiDog). Die
  übrigen Bilddateien bleiben liegen, falls sie je zurücksollen -- es
  genügt, die Zeile in data-stimmen-avatare.js und die Id in
  ERLAUBTE_AVATARE wieder zu ergänzen.
- Neue Kachel "eigenes Bild" (gestrichelter Rand + Plus), die den
  Dateidialog öffnet, das Bild sofort hochlädt und als Vorschau in der
  Kachel zeigt.

Bereits freigegebene Stimmen mit einer der entfernten Figuren zeigen
wieder den Anfangsbuchstaben statt eines kaputten Bildes -- die
Auflösung unbekannter Ids liefert null, das war schon so vorgesehen.

Sicherheit des öffentlichen Uploads (bisher war Hochladen bewusst nur
der Verwaltung erlaubt):
- Gleiche multer-Härtung wie der Verwaltungs-Upload: nur JPG/PNG/WebP,
  max. 5 MB, zufälliger UUID-Dateiname (kein Originalname).
- Der Server nimmt im Avatar-Feld weiterhin NUR bekannte Ids an oder
  eine Adresse, die exakt auf den eigenen Upload-Ordner zeigt und danach
  nur aus UUID + Bildendung besteht. Gegengetestet: fremde Domains,
  "../"-Ausbruch, .svg/.html, javascript:, angehängte Skripte und http
  statt https werden alle abgelehnt.
- Missbrauchsbremse gegen Vollschreiben der Festplatte: max. 10 Uploads
  pro Stunde und IP.
- Sichtbar wird ein Bild ohnehin erst, wenn die Stimme freigegeben wird.

Mit Playwright end-to-end geprüft (10 Tests) plus 9 Sicherheitsfälle.

Cache-Busting-Version auf 20260821q erhöht.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-21 18:23:17 +02:00
DogFatherGitandClaude Opus 5 e2bd436084 Arbeit der parallelen Session gesichert (Profilbilder für Stimmen, Diene-Zugang)
Wie beim vorherigen Mal lag das nur als Arbeitskopie auf dem Server, nicht
in git. Unverändert übernommen, bevor darauf aufgebaut wird:
- Profilbild-Auswahl für die Stimmen (data-stimmen-avatare.js neu,
  stimmen.js, i18n-stimmen.js, stimmen.html, verwaltung.html, main.css)
- Dritte Zugangs-Kachel für Diene (gate.html, server/gate.js)

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-21 18:17:47 +02:00
DogFatherGitandClaude Opus 5 d0613f86c1 Handy: Navigation war komplett unerreichbar — behoben, plus Tippflächen überall
Nutzer-Report 21.08.2026 mit Screenshot: "auf dem handy kann ich oben
nicht aussehen in welche seite ich will welche kategorie welche sprache
garnichts". Drei echte, per Playwright reproduzierte Fehler:

1. MENÜ-KNOPF AUSSERHALB DES BILDSCHIRMS (Hauptproblem)
   .brand stand auf flex-shrink:0, war bei 390px aber 352px breit. Mit
   Abstand + Knopf brauchte die Leiste 423px bei 367px Platz -- der
   Menü-Knopf landete bei x=396px, also komplett außerhalb. Damit war auf
   dem Handy die GESAMTE Navigation unerreichbar (keine Seiten, keine
   Kategorien, kein Sprachwechsel).
   Fix: Knopf per margin-left:auto immer an die rechte Kante; Marke darf
   unter 620px schrumpfen (inkl. min-width:0, sonst greift flex-shrink
   nicht); unter 400px entfällt die reine Deko-Unterzeile.

2. MENÜ IM QUERFORMAT NICHT ZU ÖFFNEN
   Das zugeklappte Panel wird um -110% SEINER EIGENEN Höhe verschoben. Quer
   (568x320) ist es nur 258px hoch, die Unterkante lag dadurch bei y=36px --
   also unsichtbar genau über dem Menü-Knopf (y=11..55) und hat jede
   Berührung abgefangen. Fix: pointer-events:none im geschlossenen Zustand.

3. MENÜPUNKTE AUF KURZEN BILDSCHIRMEN ZUSAMMENGEQUETSCHT
   .nav-links ist ein Flex-Container fester Höhe; passte der Inhalt nicht,
   schrumpfte Flexbox die Einträge (iPhone SE: 59px -> 29px, Knöpfe 21px).
   Fix: flex-shrink:0 auf die Kinder, der Bereich scrollt stattdessen
   (overflow-y:auto war bereits gesetzt).

Zusätzlich: Fußzeilen- und Impressum/AGB-Links auf Handys als echte
Tippziele (44px statt 17-20px) -- 18 dicht stehende Links, mit dem Finger
vorher kaum zu treffen. Menü-Trennlinien begradigt (folgten dem
Desktop-Pillenradius und sahen aus wie Schüsseln).

Ergebnis: alle 31 Seiten ohne Überlauf, Menü auf 320-768px, hoch UND quer
nutzbar, kleinster Menüpunkt 46px. Verbleibende kleine Ziele sind reine
Fließtext-Links im Satz (dürfen laut Standard klein bleiben).
Desktop per Regressionstest unverändert geprüft.

Cache-Busting-Version auf 20260821p erhöht.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-21 18:13:03 +02:00
DogFatherGitandClaude Opus 5 5a5912636d Registrierung: E-Mail-Bestätigung per Einmalcode vor dem Zugangscode
Nutzer-Wunsch 21.08.2026: "bei der ersten registrierung sollen die eine
email bekommen mit einem einmaligen code damit wir auch wissen dass
email stimmt und danach wenn sie den code eingegeben haben sollen die
erst ihren zugangscode auswählen/eintippen können."

Damit wird eine echte Lücke geschlossen: registerSupporter() hat bisher
`email_verified = 1` gesetzt, OHNE dass irgendetwas geprüft wurde -- man
konnte sich mit einer fremden oder erfundenen Adresse registrieren und
kam sofort rein.

Neuer Ablauf in drei Schritten:
1. Name/TikTok/E-Mail -> Konto wird als UNBESTÄTIGT angelegt
   (email_verified = 0, noch kein Zugangscode). Es kommt bewusst KEIN
   Session-Token zurück -- eingeloggt ist man hier noch nicht.
2. Einmalcode aus der E-Mail eingeben -> verify-email bestätigt und
   loggt ein. Die Willkommens-Mail wandert hierher, sie ging vorher an
   eine noch ungeprüfte Adresse.
3. Erst jetzt den eigenen Zugangscode festlegen.

Serverseitig abgesichert: setSupporterAccessCode() lehnt ab, solange die
E-Mail nicht bestätigt ist -- der Schritt ist damit nicht nur im
Formular versteckt, sondern auch per Direktaufruf nicht überspringbar.

Wiederverwendet wird die bereits vorhandene Mechanik (issueAuthCode/
verifyAuthCode mit Zweck "verify_email", sendVerifyEmailCode, die Panels
#panel-code und #panel-neuer-code) -- der "Code vergessen?"-Weg nutzt
dieselbe Code-Eingabe und bleibt unverändert; eine neue Variable
codeZweck unterscheidet, welcher Endpunkt aufgerufen wird.

Mit Playwright end-to-end geprüft (14 Tests): Reihenfolge der Aufrufe,
kein Token vor der Bestätigung, falscher Code kommt nicht weiter,
Zugangscode-Feld erst im letzten Schritt, "Code vergessen?" unberührt.

Cache-Busting-Version auf 20260821o erhöht.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-21 17:48:08 +02:00
DogFatherGitandClaude Opus 5 5d0ea11570 Arbeit der parallelen Session in git gesichert (war nur auf dem Server)
Diese Änderungen liefen bereits live auf dem Server, lagen dort aber
ausschließlich als nicht eingecheckte Arbeitskopie — bei jedem Deploy
(stash/pull/pop) und bei jedem Serverproblem wären sie verloren gewesen.
Deshalb hier unverändert in git übernommen, bevor darauf aufgebaut wird.

Enthalten (nicht von mir gebaut, nur gesichert):
- Supporter: eigener fester Zugangscode statt Einmalcode-Login
  (Migration 0009, lib/crypto.js scrypt-Hash, routes/supporter.js,
  abonnieren.html, supporter.html, i18n-abonnieren/-supporter)
- Stimmen: Profilbilder (Migration 0010, routes/testimonials.js)
- Event-Bild-Upload: voller Pfad statt relativem (routes/events.js)
- Neue/überarbeitete Hintergrundbilder für viele Seiten
- Teilen-Funktion (streamplan.js, i18n-index/-streamplan, main.css)

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-21 17:35:14 +02:00
DogFatherGitandClaude Sonnet 5 4b294f6c6b Startseite: die vier Personen-Kacheln unter "Team Dogi" entfernt
Nutzer-Wunsch 21.08.2026: "die 4 kacheln da sollen da weg bitte."
Überschrift, Beschreibungstext und der Knopf zum Modi-Team bleiben --
dadurch kommt das "Team Dogi"-Hintergrundbild dieser Sektion jetzt
unverdeckt zur Geltung. Der zugehörige Render-Code ist ebenfalls raus;
data-modis.js/data-scouts.js bleiben eingebunden, weil die
Statistik-Zeile weiter oben sie weiterhin auszählt (geprüft: zeigt
weiterhin korrekt "8+"). Die Profile selbst gibt es unverändert
vollständig auf team-modis.html.

Cache-Busting-Version auf 20260821n erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 16:53:56 +02:00
DogFatherGitandClaude Sonnet 5 d0f3edb079 bewerben.html: Überschrift-Umbruch verbessert (kein einsames letztes Wort mehr)
Nutzer-Wunsch 21.08.2026: "Weg" stand auf manchen Bildschirmbreiten
allein in einer eigenen Zeile, sah unruhig aus. Non-breaking-Space
zwischen den letzten zwei Wörtern in allen 5 Sprachen -- bricht die
Zeile jetzt nie mehr genau zwischen ihnen um.

Cache-Busting-Version auf 20260821m erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 16:51:00 +02:00
DogFatherGitandClaude Sonnet 5 76f70ffcf2 Zeitreise: Namen "Cidem" zu "Cidgem" korrigiert (Creator Cup)
Nutzer-Wunsch 21.08.2026: Tippfehler beim Namen des Kooperationspartners
beim Creator Cup korrigiert, in allen 5 Sprachen plus dem
HTML-Fallbacktext.

Cache-Busting-Version auf 20260821l erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 12:58:29 +02:00
DogFatherGitandClaude Sonnet 5 971c5ea3f6 Bugfix: Team-Foto-Upload lieferte relativen statt vollen Pfad zurück
Gleicher Fehler wie in routes/events.js/uploadEventImage (dort gerade
direkt auf dem Server gefunden und gefixt, siehe git stash auf
dogiintern): die Verwaltung und die echte Website laufen auf
dogfather-universe.com (dogiweb), hochgeladene Fotos liegen aber unter
postfach.dogfather-universe.com (dogiintern). Ein relativer Pfad wurde
vom Browser also gegen die falsche Domain aufgelöst -> kaputtes
Bild-Symbol für jedes über die Team-Verwaltung hochgeladene Foto.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 04:37:04 +02:00
DogFatherGitandClaude Sonnet 5 81f55d67db Team-Verwaltung Runde 2: bestehende Mitglieder importieren + Kategorien/Seitentitel editierbar
Nutzer-Wunsch 21.08.2026: "wenn ich eine änderung mache ist es auch für
jeden für die die schon da sind und die neuen" + "ich will ich titel
und namen von kategorien und seiten ändern können in der verwaltung".

Backend (braucht Filipes manuellen Deploy):
- Migration 0008: team_members bekommt intro/bioHtml/extraCta-Spalten
  -- volle Feld-Parität mit den von Hand gepflegten Profilen (Diene/
  Patrick/Bananenstift/Marina nutzen diese Felder).
- routes/team.js: neue importLegacyMember()-Funktion -- übernimmt ein
  bestehendes Profil 1:1 in die Datenbank, OHNE erneut zu übersetzen
  (die vorhandenen, von Hand geschriebenen Übersetzungen bleiben
  erhalten). Idempotent: mehrfacher Import erzeugt keine Duplikate.
- routes/site-texts.js (neu): admin-editierbare Kategorie-Namen und
  Seitentitel, generischer key->{de,...}-Override über app_settings
  (wie "Event des Jahres"), fester Schlüssel-Katalog aus
  Sicherheitsgründen. Leeres Feld setzt auf den Standardtext zurück.
- 27 Tests in isolierter Testumgebung geprüft (kein better-sqlite3
  lokal kompilierbar).

Frontend:
- window.dogiTeamZusammenfuehren() (main.js): admin-Mitglieder
  ÜBERSCHREIBEN jetzt gleichnamige statische Einträge (per slug) statt
  sie zu duplizieren -- eine Bearbeitung wirkt dadurch für alle.
- window.dogiSiteTexteLaden() + applyTranslations() erweitert um
  data-site-text-key -- Kategorie-Seitentitel (team-modis.html/
  team-scouts.html/creator.html/manager.html) sind jetzt live editierbar.
- Verwaltung: "📥 Bestehende Mitglieder importieren"-Knopf (holt VanVan/
  Diene/Funny/Miss/Marina/Ghost/Patrick/Bananenstift aus den data-*.js-
  Dateien, VanVan bewusst ausgenommen -- eigene Sonderkarte + Seite),
  erweitertes Formular (Intro/ausführliche Vorstellung/zweiter Button,
  eingeklappt unter "Erweitert"), neue Sektion "Kategorien &
  Seitentitel" (4 Karten, sofort wirksam auf Website UND Verwaltung).

Beim Testen einen echten UX-Bug gefunden und gefixt: die "Gespeichert"-
Meldung nach dem Speichern eines Mitglieds wurde von der direkt
anschließenden Formular-Zurücksetzung sofort wieder überschrieben und
war nie sichtbar.

Cache-Busting-Version auf 20260821k erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 03:47:51 +02:00
DogFatherGitandClaude Sonnet 5 7060a4a6ae Verwaltungsseite perfektioniert: Team-Verwaltung für Modis/Scouts/Creator/Manager
Nutzer-Wunsch 21.08.2026: "überall wo Modis sind oder Scouts oder
Manager, oder Creator, will ich dass ich die easy über meine
Verwaltungsseite hinzufüge und die automatisch in der Website
hinzugefügt werden ... auch mit Fotos und TikTok Link."

Backend (server-internal, braucht Filipes manuellen Deploy):
- Neue Tabelle team_members (Migration 0007) für alle vier Kategorien
  gemeinsam -- ergänzt, überschreibt NIE die von Hand gepflegten
  Einträge in data-modis.js/data-scouts.js/data-creator.js.
- routes/team.js: öffentliches Lesen (nur aktive Mitglieder, optional
  nach Kategorie gefiltert), TEAM_MANAGE-geschütztes Anlegen/Bearbeiten/
  Löschen, Foto-Upload (gleiches Muster wie "Event des Jahres"),
  automatische Übersetzung von Rolle/Bio/Zitat wie bei anderen
  admin-gepflegten Texten. Slug global eindeutig (mit automatischer
  Kollisionsauflösung), TikTok-Kurzname wird zu voller URL ergänzt.
  27 Tests in isolierter Testumgebung geprüft (kein better-sqlite3
  lokal kompilierbar).

Frontend:
- window.dogiTeamLaden() in main.js: holt admin-gepflegte Mitglieder
  einer (oder aller) Kategorien und bringt sie in exakt dieselbe Form
  wie die bestehenden data-*.js-Einträge.
- team-scouts.html/team-modis.html/creator.html hängen das Ergebnis
  einfach an ihre bestehenden Arrays an und rendern erneut -- exakt
  derselbe Look wie die schon bestehenden Profile (Foto/Avatar-Initiale,
  Rolle, Zitat, TikTok-Button). creator.html hatte bisher ein "return"
  bei leerem CREATORS-Array, wodurch admin-Creator NIE geladen worden
  wären -- gefixt. team-modis.html berücksichtigt Rang/Rang-Bezeichnung
  für die Pyramide.
- Neue Sektion "Weitere Manager" auf manager.html, komplett unsichtbar
  bis der erste Manager angelegt wird (data-manager.js neu, wie
  data-creator.js aktuell leer).
- profil.html sucht jetzt kategorieübergreifend auch in admin-gepflegten
  Profilen, inkl. korrektem Theme/Zurück-Link auch für Manager.

Verwaltung: neue Sektion "Team verwalten" (TEAM_MANAGE-Berechtigung) --
ein Formular für alle vier Kategorien mit Foto-Sofort-Upload,
TikTok-Feld, Bio/Zitat, bedingten Rang-Feldern (nur Modi), Kategorie-
Filterleiste und Bearbeiten/Löschen pro Eintrag.

Beim Testen mit Playwright einen echten Syntaxfehler gefunden und
gefixt (ASCII-Anführungszeichen statt schließendem „" in einem
Statustext), der das GESAMTE Verwaltungs-Skript und damit die komplette
Seite lahmgelegt hätte.

Cache-Busting-Version auf 20260821j erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 03:06:21 +02:00
DogFatherGitandClaude Sonnet 5 8ead1b750b KRITISCH: echten Ursprung der "rohen Übersetzungs-Schlüssel"-Bugs gefunden und behoben
Beide heutigen Meldungen ("pr_quote_open...", "ts_profil_ansehen") waren
KEIN Caching-Problem, sondern ein echter Bug in main.js: nach Klick auf
einen internen Link und dann "Zurück" (oder generell jede zweite Seite
innerhalb einer Browser-Sitzung) wurde die seiteneigene i18n-*.js-Datei
nie erneut ausgeführt -> window.I18N_PAGE blieb auf null -> jede
Übersetzung fiel auf den rohen Schlüsselnamen zurück.

Ursache: skripteUebernehmen() erkennt i18n-Dateien am Muster ".js$"
(String-Ende). Seit die Cache-Busting-Version ("?v=...") an jede
Skript-URL angehängt wird, endet der echte src-Wert aber nie mehr auf
".js", sondern auf ".js?v=...". Der Test schlug seitdem für JEDE
i18n-Datei fehl und sie wurde wie eine normale, schon geladene Datei
behandelt und beim nächsten Seitenwechsel übersprungen.

Mit Playwright reproduziert (Klick auf Profil-Kachel + Zurück-Button)
und nach dem Fix erneut verifiziert -- betraf praktisch jede Seite mit
eigenem i18n-*.js beim Navigieren per Klick, nicht nur team-scouts.html.

Cache-Busting-Version auf 20260821i erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 02:12:24 +02:00
DogFatherGitandClaude Sonnet 5 fbd11b7be4 "Was gerade läuft"-Aktionen-Teaser von der Startseite entfernt
Nutzer-Wunsch 21.08.2026: Überschrift + Button "Alle Aktionen & Projekte"
auf index.html weg. data-aktionen.js-Script-Tag (nur noch dafür gebraucht)
und die zugehörigen JS-Renderzeilen ebenfalls entfernt, dazugehörige
i18n-Keys (idx_aktionen_h2/idx_aktionen_btn) aus i18n-index.js aufgeräumt.
aktion-detail.html und data-aktionen.js selbst bleiben unangetastet.

Cache-Busting-Version auf 20260821h erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 02:04:43 +02:00
DogFatherGitandClaude Sonnet 5 ca5793e79f Patricks Profil-Abschluss um "Scout von Dogfather" ergänzt (wie Bananenstift)
Nutzer-Wunsch 21.08.2026: Patricks Bio endete nur mit "- euer Patrick",
Bananenstift hat zusätzlich eine zweite Zeile "Scout von Dogfather".
Für Konsistenz zwischen beiden Scout-Profilen in allen 5 Sprachen ergänzt.

Cache-Busting-Version auf 20260821g erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 01:53:57 +02:00
DogFatherGitandClaude Sonnet 5 c7904ccd62 TikTok-Buttons auf Patrick- und Bananenstift-Scout-Profilen ergänzt
Nutzer-Wunsch 21.08.2026: unter dem Namen/Rolle-Text je ein TikTok-Button
(@patrick180585 bzw. @bananenstift009). Beide nutzen den bereits
bestehenden social-Mechanismus in profil.html (social.tiktok -> echter
.btn-tiktok-Button), kein neuer Code nötig.

Cache-Busting-Version auf 20260821f erhöht (data-scouts.js ist .js und
damit von Cloudflares erzwungenem Edge-Cache betroffen).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 01:49:00 +02:00
DogFatherGitandClaude Sonnet 5 ac01a74d08 Zeitreise: neue Station "Erstes Fan-Treffen: Team TiliDog" (25. Juli 2026)
Nutzer-Wunsch 21.08.2026: erstes Fan-Treffen von Dogi & Tili (Team
TiliDog) in Wuppertal ergänzt -- gleichzeitig das erste persönliche
Aufeinandertreffen von DogFather und Tili. Als Meilenstein markiert und
chronologisch korrekt zwischen "Juli 2026 — Start als Manager" und
"27. Juli bis 2. August 2026 — Creator Cup" einsortiert.

Cache-Busting-Version auf 20260821e erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 01:39:47 +02:00
DogFatherGitandClaude Sonnet 5 360f19a52d Echtes Foto von Filipe in der Intro-Sektion auf streamer.html ergänzt
Nutzer-Wunsch 21.08.2026: "füge das foto was ich am ende mitschicke
neben den text im screen1". Sektion von einspaltigem Fließtext auf
grid-2 umgebaut, Foto (assets/img/streamer-filipe-portrait.jpg, aus
Pictures/Filipe, auf 1000x1500 verkleinert) rechts daneben mit
frame-gold-Rahmen für Abwechslung zur Casper-Sektion (frame-lila).

Cache-Busting-Version auf 20260821d erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 01:29:03 +02:00
DogFatherGitandClaude Sonnet 5 6106cf9f03 Modi-Gang TikTok-Button von streamer.html nach team-modis.html verschoben
Der Button gehört thematisch besser zum Modi-Team als zur Casper-Sektion
auf streamer.html (Nutzer-Wunsch 21.08.2026: "den knopf da weg bitte und
in die modie seite zu den modis hinzufügen und perfekt in die seite
anpassen"). Jetzt im Hero von team-modis.html, unter dem Lead-Text,
zentriert im bestehenden btn-row-Muster der Seite.

Cache-Busting-Version auf 20260821c erhöht.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 01:24:43 +02:00
DogFatherGitandClaude Sonnet 5 ed9b373a74 Husky-Bild auf streamer.html an Texthöhe angepasst statt fixem Mini-Deckel
Vorheriger max-height-Deckel (460px) fürs sehr hochkantige
streamer-mascot.jpg (900x2812) war zu knapp und ließ eine sichtbare
Lücke neben dem längeren Text ("die größe vom bild vom husky soll dem
text rechts nebendran angepasst werden"). Deckel auf 720px erhöht
(orientiert an der typischen Texthöhe in diesem Abschnitt), Grid-Stretch-
Ansatz ausprobiert und wegen Rückkopplung mit dem extremen
Seitenverhältnis verworfen (siehe Kommentare in main.css/streamer.html).

Cache-Busting-Version auf 20260821b erhöht (Cloudflare cached CSS/JS
sonst bis zu 4h am Edge).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-21 01:21:31 +02:00
DogFatherGitandClaude Opus 5 1f87b54351 Nav umbenannt/umsortiert + Stimmen-Kachel und Zitat-Karten aufgewertet
Nutzer-Wuensche 20.08.2026 (2. Runde):
- Kategorie "DogFather" -> "Filipe", Kategorie "HasiDog" -> "Dogi&Hasi".
- Unterpunkt "Streamer" (streamer.html) aus "Filipe" heraus nach
  "Dogi&Hasi" verschoben, dort GANZ NACH OBEN (ueber "HasiDog") und in
  "DogFather" umbenannt.
- Unterpunkt "HasiDog-Welt" -> "HasiDog".
- Fusszeile zieht mit: Spaltenueberschrift nutzt jetzt dieselbe
  Kategorie-Bezeichnung wie die Nav ("Filipe" / "Dogi&Hasi & Manager"),
  streamer.html steht dort ebenfalls in der Dogi&Hasi-Spalte ganz oben.
  Die internen Schluesselnamen (nav_dogfather...) bleiben bewusst
  unveraendert -- reine Bezeichner, ein Umbenennen waere nur Fehlerquelle.

- "die kachel soll noch spezieller und geiler aussehen": Einreich-Kachel
  auf stimmen.html deutlich aufgewertet -- wandernder Farbverlauf-Rahmen
  (Zwei-Ebenen-/background-position-Technik, KEIN rotierender Ring: siehe
  dokumentierte Projekt-Lektion zu Verzerrungen auf eckigen Flaechen),
  schwebende Herz-Partikel, Medaillon statt nacktem Emoji,
  Farbverlauf-Ueberschrift, aufleuchtende Eingabefelder.
- "und auch die die danach kommen ... sollen viel geiler sein": freigegebene
  Stimmen sind jetzt echte Praesentationskarten -- farbige Akzentlinie oben,
  grosses Anfuehrungszeichen als Wasserzeichen, Avatar-Medaillon mit
  Anfangsbuchstabe, TikTok-Handle als eigener Chip, sanftes Anheben beim
  Ueberfahren.

Alles augenschonend gehalten (gedeckte Toene, langsame Bewegungen,
vollstaendiger Stillstand bei prefers-reduced-motion) und per Playwright
visuell geprueft, keine JS-Fehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-21 01:02:36 +02:00
DogFatherGitandClaude Opus 5 5146cef164 TikTok-Knoepfe fuer HasiDog + Modi-Gang, Zeitreise-Reihenfolge, Bildgroesse
Nutzer-Wuensche 20.08.2026:
- hasidog.html: TikTok-Knopf direkt unter der grossen Ueberschrift,
  Account @hasidog0804 (alle 5 Sprachen uebersetzt).
- streamer.html: TikTok-Knopf unter "Casper - mein treuer Begleiter",
  Account @dogis.modi.gang (alle 5 Sprachen uebersetzt).
- zeitreise.html: die beiden letzten Karten getauscht -- der Website-Start
  (21.08.2026, konkretes Datum) steht jetzt VOR der Teddy-Kooperation
  ("Datum folgt"), damit die Zeitleiste durchgehend chronologisch bleibt.
- streamer.html Bildgroesse gefixt ("der soll bissl kleiner sein und nicht
  so riesig"): streamer-mascot.jpg ist 900x2812 px und lief mit
  aspect-ratio:auto + height:auto in voller natuerlicher Hoehe -- rund
  dreimal so hoch wie die Textspalte daneben. Neue Klasse
  .collage-photo-kompakt deckelt die Hoehe auf 460px und laesst den Rahmen
  eng am Bild sitzen (width:fit-content), die ganze Figur bleibt sichtbar.
  Regel steht bewusst NACH ".collage-photo img" -- gleiche Spezifitaet,
  die spaetere gewinnt, sonst haette width:100%/height:100% sie aufgehoben.

Alles per Playwright verifiziert (beide Knoepfe mit korrekten Links,
Bildhoehe 460px statt ~1780px, neue Zeitreise-Reihenfolge), keine JS-Fehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 23:46:04 +02:00
DogFatherGitandClaude Opus 5 399260b805 "Stimmen" verwandelt: Fans reichen selbst Testimonials ein
Nutzer-Wunsch 20.08.2026: "eine richtig geile kachel ... wo die leute mit
namen und tiktok namen mir eine nachricht schreiben können die ich in die
verwaltung kriege, und dann kann ich die ausgewählten über die
verwaltungsseite auf die website hinzufügen ... das wird mega persoenlich
zu den fans, bitte wirklich krass geil speziell."

- Neue, augenschonend gestaltete Einreich-Kachel auf stimmen.html: Name,
  TikTok-Name (optional), Nachricht -- landet NIE automatisch oeffentlich,
  sondern immer erst als Entwurf in der Verwaltung.
  Warmer Babyblau/Rosé-Farbverlauf, wandernder Lichtschein, pulsierendes
  Herz -- ersetzt die 3 ewigen "Hier steht bald..."-Platzhalterkarten.
- Neue Verwaltungs-Sektion "💬 Stimmen verwalten": Warteschlange (offene
  zuerst), Freigeben/Ablehnen/Löschen pro Eintrag.
- Freigegebene Stimmen erscheinen automatisch in einer neuen, spezielleren
  Zitat-Kartenoptik (großes Anführungszeichen, Name + TikTok-Chip) --
  Abschnitt bleibt komplett unsichtbar, solange keine einzige freigegeben
  wurde.
- Backend: neue Tabelle `testimonials` (Migration 0006), routes/
  testimonials.js (oeffentliches Einreichen + Lesen freigegebener,
  admin-Warteschlange + Status/Loeschen mit neuer TESTIMONIALS_MANAGE-
  Berechtigung). 18 automatisierte Tests gegen eine Fake-DB bestanden.
- Alles per Playwright visuell durchgespielt: leerer Zustand, Einreichen
  -> Erfolgsmeldung, freigegebene Stimmen-Anzeige, Verwaltungs-Warteschlange
  mit allen Aktionen.

Backend-Teil (server-internal/, cloudflare-worker/migrations/) noch ohne
Deploy-Zugriff -- Dogi muss ihn manuell auf dogiintern ausrollen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 23:37:31 +02:00
DogFatherGitandClaude Opus 5 519e209bce Zweite Live-Uhr fuer "spezielles Live-Event" + mehr Farbe im Radar
Nutzer-Wunsch 20.08.2026: "wuensch mir nur noch bissl farbe und sehr
wichtig ist dass ich auch spezielle live events da auch eintragen kann
in der verwaltungsseite so dass da eine zweite uhr erscheint wo die zeit
dann fuer dieses spezielle event laeuft."

- Neue Verwaltungs-Sektion "Spezielles Live-Event": Titel (Deutsch, wird
  automatisch uebersetzt), echtes Datum+Uhrzeit, optionaler Link, Aktiv-
  Schalter -- nur mit Titel + Zukunftsdatum aktivierbar.
- Zweite Uhr auf streamplan.html (Magenta/Violett statt Cyan/Gold, direkt
  neben der bestehenden), nur sichtbar wenn ein Event aktiviert ist und
  das Zieldatum noch nicht vorbei ist. Echter Countdown inkl. Tage (z.B.
  "2T 03:14:59"), "Jetzt"-Zeiger zeigt wie bei Uhr 1 die tatsaechliche
  Uhrzeit, der magentafarbene Fixpunkt markiert die Tageszeit des Events.
  Zieldatum wird als UTC gespeichert -- jede besuchende Person sieht den
  exakt richtigen Countdown in ihrer eigenen Zeitzone.
- "Bissl Farbe": bunter Farbverlauf (Cyan/Violett/Gold) auf dem aeusseren
  Ring statt reinem Grauton, kraeftigerer Sweep-Farbschein.
- Backend: neue routes/special-event.js (getSpecialEventPublic nur bei
  aktiv+zukuenftig, getSpecialEventAdmin fuer Entwuerfe, saveSpecialEvent
  mit Validierung), 13 automatisierte Tests gegen eine Fake-DB bestanden.
- Bugfix unterwegs gefunden (Playwright-Screenshot, wiederholtes Muster
  auf dieser Seite): .stream-radar-wrap blieb trotz [hidden]-Attribut
  sichtbar (display:block ueberschreibt die eingebaute [hidden]-Regel bei
  gleicher Spezifitaet) -- explizite Regel ergaenzt, per Playwright erneut
  verifiziert (display:none bestaetigt).

Backend-Teil (server-internal/) noch ohne Deploy-Zugriff -- Dogi muss ihn
manuell auf dogiintern ausrollen, sonst bleibt die zweite Uhr unsichtbar.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 23:24:17 +02:00
DogFatherGitandClaude Opus 5 7fdceadb9e Fix: Sprachschalter-Menue nicht mittig wie die Kategorie-Menues
Nutzer-Report per Screenshot-Vergleich: "Medien"-Kategorie ist mittig
unter dem Knopf, der Sprachschalter (DE/CH/EN/FR/PT) aber rechtsbuendig --
war bewusst so gebaut, als der Schalter noch ganz am rechten Rand sass.
Seit DogiCrew-/Bewerben-Knopf daneben stehen, ist genug Platz fuer
dieselbe zentrierte Ausrichtung wie ueberall sonst -- macht die Optik
konsistent und behebt nebenbei einen Detail-Fehler: der Verbindungspfeil
zeigte schon immer mittig, waehrend die Box selbst rechts daneben sass.
Per Playwright verifiziert: Dropdown jetzt exakt unter dem Knopf zentriert,
laeuft bei keiner getesteten Breite ueber den Rand.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 23:10:27 +02:00
DogFatherGitandClaude Opus 5 7dadd84cbb Neue Hintergrundbilder: Startseite (Trio) + Streamplan
Nutzer-Wunsch 20.08.2026: Startseiten-Hero (universe-trio.jpg) und
Streamplan-Hero (bg-streamplan.jpg/-mobile.jpg) durch neu bereitgestellte
Bilder ersetzt. Blur-Variante fuer die Startseite neu aus dem frischen
Bild erzeugt (Gaussian Blur, passend zur bestehenden Cover-Verlauf-Technik).
Mobile-Ausschnitt fuer Streamplan mehrfach nachjustiert, damit der
"STREAMPLAN"-Schriftzug im schmalen Hochformat-Crop komplett sichtbar
bleibt. Alte Bilder als .bak-20-08-2026 gesichert (nicht eingecheckt).
Referenzen mit Versions-Stempel (?v=) versehen, damit der neue Stand
sofort sichtbar ist statt bis zu 4 Std im Cache zu haengen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 23:07:33 +02:00
DogFatherGitandClaude Opus 5 19636a25ec Fix: per Klick geoeffnetes Nav-Kategorie-Menue schloss sich nicht von selbst
Nutzer-Report per Screenshot: Klick auf eine Kategorie (z.B. "DogFather")
liess das Untermenue offen stehen, bis irgendwo anders hingeklickt wurde --
verliess man es einfach mit der Maus, blieb es haengen. Jetzt schliesst
sich ein per Klick geoeffnetes Menue automatisch, sobald die Maus die
Kategorie (Knopf + Untermenue) verlaesst -- nur auf echten Maus-Geraeten
(hover:hover + pointer:fine), auf Touch/Tablet aendert sich nichts, dort
gibt es kein "mit der Maus verlassen". Per Playwright verifiziert: Klick
oeffnet, Mausbewegung weg schliesst.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 22:56:49 +02:00
DogFatherGitandClaude Opus 5 a5615331aa Streamplan-Seite: interaktives Live-Radar statt schlichter Textkarten
Nutzer-Wunsch 20.08.2026: "diese seite vom streamplan soll viel geiler
und spezieller sein, ueberrasch mich."

Neues Herzstueck: ein 24-Stunden-Ziffernblatt (reines SVG, keine
Bild-Assets), das den festen 20-Uhr-Termin als dauerhaft leuchtenden
Fixpunkt zeigt (goldener Puls) und einen "Jetzt"-Zeiger in Echtzeit mit
der tatsaechlichen Uhrzeit der besuchenden Person mitbewegt (Babyblau,
DogFather-Markenfarbe). Nutzt denselben /live-status-Endpunkt wie die
Startseite:
- Offline: echter Sekunden-Countdown zum naechsten lokalen 20-Uhr-Termin.
- Live: komplettes Radar schaltet auf Rot/Puls um, zeigt den Stream-Titel
  und einen "Jetzt anschauen"-Knopf direkt zu TikTok.
Sanft rotierender Lichtschein fuer Atmosphaere, komplett augenschonend
(gedeckte Farben, respektiert prefers-reduced-motion vollstaendig).

Bugfix unterwegs gefunden (Playwright-Screenshot, dritte Wiederholung
desselben Musters heute): der "Jetzt anschauen"-Knopf blieb trotz
[hidden]-Attribut sichtbar, weil .btn selbst "display" setzt und damit
gleiche Spezifitaet wie die eingebaute [hidden]-Regel hat -- explizite
Regel ergaenzt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 22:50:49 +02:00
DogFatherGitandClaude Opus 5 b3e191d3ca "Event des Jahres": Aktivieren-Schalter, leer bis befuellt, Detail-Fenster
Nutzer-Wunsch 20.08.2026: "diese zwei kisten sollen leer sein solang wie
ich nichts rein setze, am besten sollen die sogar immer nur erscheinen
wenn ich was rein setze und sie aktiviere ... wen ich drauf druecke dass
dann ein kleines fenster aufgeht wo das bild bissl groesser ist mit einem
groesseren text und beschreibung, und mit einem link ... die kacheln
sollen auch bissl spezieller sein."

- Kein hartkodierter Standardinhalt mehr auf der Startseite -- die Kachel
  UND der ganze Abschnitt bleiben komplett unsichtbar, bis mindestens ein
  Event in der Verwaltung ausgefuellt UND ueber einen neuen "Aktiv"-Schalter
  freigeschaltet ist. Genau 1 aktives Event -> zentrierte Einzelkachel
  statt halbleerem Zwei-Spalten-Raster.
- Klick auf eine Kachel oeffnet jetzt ein Detail-Fenster (groesseres Bild,
  groesserer Titel/Text, optionaler direkter Link) statt sofort
  wegzunavigieren.
- Kacheln bekommen einen goldenen Trophaeen-Akzent + dezenten wandernden
  Lichtschimmer statt der neutralen Standardkarten-Optik.
- Bugfix unterwegs gefunden (Playwright-Screenshot): .jahres-event-card
  blieb trotz [hidden]-Attribut sichtbar (dieselbe Ursache wie der
  frühere .jahres-event-bild-Bug: display:block ueberschreibt die
  eingebaute [hidden]-Regel bei gleicher Spezifitaet) -- explizite
  [hidden]-Regel ergaenzt.
- Backend: server-internal/routes/events.js liefert oeffentlich NUR noch
  aktivierte Slots aus (getEventsPublic), neuer authentifizierter Endpunkt
  getEventsAdmin liefert der Verwaltung auch Entwuerfe zum Vorausfuellen.
  10 automatisierte Tests gegen eine Fake-DB bestanden.

Backend-Teil (server-internal/) noch ohne Deploy-Zugriff -- Dogi muss ihn
manuell auf dogiintern ausrollen, sonst bleibt die Startseite beim alten
Verhalten (immer beide Slots zeigen, kein Aktiv-Schalter in der Verwaltung).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 22:43:28 +02:00
DogFatherGitandClaude Opus 5 208b5ba4ed Fix: gemeinsame Navigationsleiste war auf der Verwaltungsseite ~16% groesser
Endlich gefunden (Screenshot-Vergleich Haupt-/Verwaltungsseite Seite an
Seite, exakt gleiche Fensterbreite): verwaltung.html setzt bewusst
`html { font-size: 18.5px }` fuer ihr eigenes "Jewelen-Tresor"-Design
(statt der normalen 16px) -- das ist eine GLOBALE rem-Basis und vergroesserte
dadurch ungewollt auch die gemeinsame, aus main.css/main.js kommende
Navigationsleiste um denselben Faktor (~16%). Dieselbe Leiste brauchte
dadurch spuerbar mehr Breite als auf jeder anderen Seite und lief bei
Fensterbreiten ueber, bei denen sie ueberall sonst laengst gut passte --
kein Cache-Problem, ein echter CSS-Bug, der die ganze vorherige
Fehlersuche erklaert.

Fix: die Navigationsleiste bekommt in verwaltung.html ihre Masse fest in
Pixel zurueck (exakt die Werte, die bei 16px-Basis herauskaemen) statt in
rem -- dadurch bleibt sie unabhaengig von der Basis-Schriftgroesse dieser
Seite exakt so gross wie ueberall sonst. Lokal verifiziert: beide Seiten
liefern jetzt bei identischer Fensterbreite exakt dieselbe Leisten-Breite
(1560px).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 22:22:45 +02:00
DogFatherGitandClaude Opus 5 fece1dad56 Revert: Hamburger-Schwelle zurueck auf 1650px
Die Erhoehung auf 1900px eben war ein Fehlgriff -- der Nutzer sah dadurch
bei seiner eigentlichen Fensterbreite (die volle Desktop-Nav laengst
gepasst haette, zweifach live bestaetigt) nur noch die schmale Menue-
Ansicht statt der gewohnten vollen Leiste. 1650px war die korrekte,
bereits bestaetigte Schwelle -- das eigentliche Problem war durchgehend
Browser-/CDN-Caching, nicht die Schwelle selbst.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 20:06:18 +02:00
DogFatherGitandClaude Opus 5 0b0e7a2eb9 Nav-Sicherheitsabstand nochmal deutlich vergroessert (Hamburger bis 1900px)
Nutzer meldet weiterhin Ueberlauf trotz zweifach bestaetigtem Live-Test
(exakt seine Fensterbreite 1993x931 gegen den echten Server, 0 Ueberlauf,
mehrfach reproduziert) -- Ursache vermutlich hartnaeckiger lokaler Cache
im jeweiligen Browserprofil, nicht mehr abschliessend ferndiagnostizierbar.
Statt weiter zu diskutieren: Sicherheitsabstand brachial vergroessert,
unabhaengig von der genauen Ursache. Hamburger-Schwelle 1650px -> 1900px --
deckt praktisch jede Laptop-/Desktop-Fensterbreite ab, echte Desktop-Nav
zeigt sich jetzt erst ab sehr breiten Fenstern (>1900px), dort mit viel
Luft (min(1560px, 94vw)).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 20:00:07 +02:00
DogFatherGitandClaude Opus 5 7099d80491 Sichtbarer "App installieren"-Knopf statt versteckter Browser-Funktion
Nutzer-Report: "ich kann sie immer noch nicht runter laden" -- Manifest +
Service Worker reichen technisch fuer Installierbarkeit, aber ohne
sichtbaren Knopf muss man wissen, dass Chrome/Edge das Adressleisten-
Symbol/3-Punkte-Meny dafuer versteckt. Jetzt: echter Knopf im Footer
("Als App installieren", jede Seite) und in der Verwaltung-Session-Leiste
("Als eigene App installieren"), nutzt beforeinstallprompt + prompt() --
loest pro Seite automatisch mit GENAU dem Manifest aus, das diese Seite
selbst verlinkt (index.html -> manifest.json, verwaltung.html ->
manifest-verwaltung.json), kein Sonderfall-Code noetig. Bleibt unsichtbar,
wenn der Browser das nicht unterstuetzt (Safari/iOS) oder die Seite schon
als App laeuft.

Ausserdem: eigener apple-mobile-web-app-title fuer verwaltung.html
("DogiCrew-Verwaltung" statt generisch "DogFather" beim iOS-Home-Bildschirm).

Cache-Busting-Version (?v=) erneut hochgezaehlt (20260820 -> 20260820b),
da main.js sich durch diese Aenderung erneut geaendert hat.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 19:30:42 +02:00
DogFatherGitandClaude Opus 5 4173e43c78 Cache-Busting fuer CSS/JS auf allen Seiten (?v=20260820)
Zusammen mit dem no-cache-Header-Fix in server/index.js: Cloudflare (die
Seite laeuft hinter einem orange-cloud-Proxy) liefert fuer .css/.js einen
eigenen festen Standard-Cache (Browser Cache TTL 4 Std) aus, UNABHAENGIG
vom Origin-Cache-Control -- der no-cache-Header allein reichte deshalb
nicht (per curl bestaetigt: main.css zeigte weiterhin max-age=14400 direkt
nach dem Deploy). Robuste, von Cloudflare-Zoneneinstellungen unabhaengige
Loesung: jede lokale CSS/JS-Referenz auf allen 34 Seiten bekommt einen
Versions-Query-String (?v=20260820) -- fuer Browser/CDN ist das eine neue
URL, alte gecachte Kopien werden dadurch nie mehr faelschlich weiterverwendet.

WICHTIG fuer kuenftige Aenderungen an main.css/main.js: das Datum in ?v=
muss bei der naechsten inhaltlichen Aenderung an einer dieser Dateien
wieder hochgezaehlt werden, sonst greift der Cache-Bust nicht erneut.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 19:26:11 +02:00
DogFatherGitandClaude Opus 5 6dad6a5af5 Fix: Deploys blieben bei Besuchern bis zu 4 Std im Browser-Cache haengen
Nutzer-Report: sah den Nav-Fix und die neue installierbare Verwaltungsseite
trotz erfolgreichem Deploy nicht. Ursache: Express schickt standardmaessig
KEIN Cache-Control mit -- da die Seite hinter Cloudflare (orange-cloud)
liegt, sprang Cloudflare dafuer mit seinem eigenen Standardwert ein
(Browser Cache TTL 4 Std, per curl bestaetigt: max-age=14400). Ein frischer
Deploy war dadurch bis zu 4 Std lang im eigenen Browser-Cache jedes/jeder
Besuchers unsichtbar. Cloudflare respektiert ein vom Origin gesetztes
Cache-Control -- jetzt explizit "no-cache" gesetzt (erzwingt Revalidierung
per ETag bei jedem Laden, kein Performance-Verlust durch schnelle
304-Antworten bei unveraendertem Inhalt).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 19:23:42 +02:00
DogFatherGitandClaude Opus 5 2c5e04e433 Fix: manifest-verwaltung.json wurde von der Zugangsschranke blockiert
Die site-weite Zugangsschranke (server/gate.js) laesst bislang nur den
exakten Pfad /manifest.json unauthentifiziert durch (fuer die
PWA-Installierbarkeit der Haupt-Website noetig) -- das neue
manifest-verwaltung.json fiel dadurch nicht unter die Ausnahme und wurde
zur Login-Seite umgeleitet statt als JSON ausgeliefert zu werden. Neuen
Pfad zur Ausnahmeliste hinzugefuegt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 16:43:23 +02:00
DogFatherGitandClaude Opus 5 aecf6db509 Verwaltungsseite als eigene, separat installierbare App
Nutzer-Wunsch 20.08.2026: "ich will die Verwaltungsseite auch herunter
laden koennen, so dass ich die originale website und die verwaltungsseite
2 mal getrennt installieren kann, auf dem pc und handy."

- Eigenes manifest-verwaltung.json (eigene "id"/"scope" nur fuer
  verwaltung.html, eigener Name "DogiCrew-Verwaltung", eigenes
  Icon-Set) statt des site-weiten manifest.json (scope "/") -- macht sie
  zu einer technisch eigenstaendigen App-Identitaet, installierbar
  parallel zur Haupt-Website, auf Desktop und Handy.
- Neue Icons: bestehendes Husky-Logo mit Violett/Gold-Verlauf statt
  Babyblau (passend zum "Jewelen-Tresor"-Look der Verwaltungsseite),
  damit beide installierten Apps auch optisch klar unterscheidbar sind.
- Kein zweiter Service Worker noetig -- /sw.js laeuft bereits site-weit
  auf Scope "/" und deckt verwaltung.html automatisch mit ab.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 16:42:20 +02:00
DogFatherGitandClaude Opus 5 94182cc6ab Fix: Desktop-Nav lief ab ~1500px Fensterbreite ueber den Rand hinaus
Nutzer-Report per Screenshot: "DogiCrew"-Knopf/"BALD"-Badge rechts
abgeschnitten. Ursache: die Hamburger-Schwelle (1480px) und der
Nav-Bar-Breiten-Deckel (1440px) wurden schon zweimal knapp nachgezogen,
reichten aber nicht mehr, seit die Nav um den "DogiCrew"-Knopf + "BALD"-
Badge gewachsen ist (braucht jetzt real ~1491px). Ergebnis: bei JEDER
Fensterbreite ab ca. 1500px (nicht nur in einer schmalen Uebergangszone)
lief die Nav dauerhaft ~50px ueber den Rand.

Diesmal mit echtem Sicherheitsabstand statt wieder nur knapp behoben:
Schwelle 1480px -> 1650px, Deckel 1440px -> 1560px. Per Playwright ueber
den kompletten Bereich 1024-2560px nachgeprueft, keine Ueberlaeufe mehr.
Header ist site-weit eine gemeinsame Vorlage (main.js/main.css), Fix gilt
damit automatisch fuer alle Seiten.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 16:38:46 +02:00
DogFatherGitandClaude Opus 5 bda3198c76 Neue Funktion: Supporter-Abstimmungen (Verwaltung + DogiCrew-Bereich)
Naechste "Ausbaustufe" aus Dogfather_VanVan_Supporter_Abo.odt Abschnitt 20
(siehe Supporter-Abo-System.md), auf Nutzerwunsch "perfektioniere meine
Verwaltungsseite": Dogi/VanVan koennen in verwaltung.html eine Frage mit
2-6 Antwortoptionen auf Deutsch erstellen, automatische Uebersetzung beim
Speichern (gleiches Muster wie "Event des Jahres"). Jede aktive DogiCrew-
Person sieht die Abstimmung in ihrem Supporter-Bereich, stimmt genau einmal
ab (UNIQUE-Constraint in der DB, nicht nur Anwendungslogik), sieht danach
die Live-Ergebnisse. Admin-Seite zeigt Ergebnisbalken live, kann schliessen/
wiedereroeffnen/loeschen.

- Neue Migration 0005_supporter_polls.sql (supporter_polls,
  supporter_poll_votes), neue Berechtigung POLLS_MANAGE.
- server-internal/routes/polls.js: 30 End-to-End-Tests gegen eine
  Fake-DB bestanden (better-sqlite3 laesst sich lokal nicht kompilieren).
- verwaltung.html: neue "Abstimmungen"-Kiste im bestehenden
  vw-overview-box-Stil (violett/pink Ergebnisbalken).
- supporter.html: neue "Aktuelle Abstimmung"-Karte im bestehenden
  Gold-Look, Optionen -> Stimme -> Ergebnisbalken, alle 5 Sprachen.

Backend-Teil (server-internal/, cloudflare-worker/migrations/) noch ohne
Deploy-Zugriff -- Dogi muss ihn manuell auf dogiintern ausrollen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 16:07:51 +02:00
DogFatherGitandClaude Opus 5 189b72ec73 Vorschau-Umschalter (Theatermasken-Symbol) vor dem oeffentlichen Start deaktiviert
Nutzer-Wunsch 20.08.2026, vor morgigem oeffentlichem Start: "die leute
sollen das ja nicht selbst auswaehlen koennen". Der Schalter erkannte
Dogi/VanVan bisher nur ueber das rein client-seitig lesbare dogi_role-
Cookie (bewusst NICHT das echte HttpOnly-Sicherheits-Cookie) -- technisch
liesse sich dieses Cookie per Browser-Konsole selbst setzen. Echte Daten
waeren dadurch nie einsehbar (nur eigene Beispieldaten-Vorschau), aber
"soll weg fuer die Oeffentlichkeit" heisst hier bewusst ganz weg, nicht
nur sicherer.

Nur der Aufruf beim Seitenaufbau ist auskommentiert, renderVorschauSchalter()
selbst bleibt unangetastet fuer ein spaeteres internes Testen.

Per Playwright verifiziert: Widget erscheint weder bei normalem Besuch
noch mit manuell gesetztem dogi_role-Cookie.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 15:16:01 +02:00
DogFatherGitandClaude Opus 5 f86b96a22f "Event des Jahres" jetzt in der Verwaltung pflegbar (Bild, Text, Link)
Nutzer-Wunsch 20.08.2026: "will ich in der admin seite das selbst
gestalten können, mit bild und text und am besten auch einen link." Auf
Rueckfrage entschieden: Titel/Text nur auf Deutsch eingeben, die anderen
4 Sprachen werden beim Speichern automatisch uebersetzt (MyMemory, 0€,
kein API-Key) -- bewusste Ausnahme von der sonst geltenden "immer echte
Uebersetzung"-Regel, klar dokumentiert und im Admin-UI selbst als Hinweis
sichtbar. de-CH bekommt denselben deutschen Text (Dialekt ist keine von
Uebersetzungs-APIs unterstuetzte Zielsprache).

Backend (server-internal):
- routes/events.js: getEventsPublic (oeffentlich, keine Session),
  saveEvent + uploadEventImage (beide hinter neuer EVENTS_MANAGE-
  Berechtigung, Owner immer erlaubt). Speichert in der bereits
  bestehenden app_settings-Tabelle (2 feste Slots) statt einer neuen
  Tabelle -- es gibt nie mehr als genau 2 Events.
- lib/translate.js: MyMemory-Anbindung mit "fail closed auf Deutsch"
  pro Sprache, falls der Dienst mal nicht antwortet.
- Bild-Upload per multer, zufaelliger Dateiname (crypto.randomUUID,
  verhindert Path-Traversal ueber den Originalnamen komplett), 5 MB
  Limit, nur jpeg/png/webp, Ablage unter /var/lib/dogfather-internal/
  uploads/events (NICHT im Git-Ordner -- uebersteht Deploys), oeffentlich
  ausgeliefert unter /uploads.
- Neue Berechtigung EVENTS_MANAGE im Katalog (Gruppe "Startseite").

Frontend:
- index.html: laedt /events-of-year beim Aufruf, ueberschreibt pro Slot
  Datum/Titel/Text/Bild/Link NUR wenn dort tatsaechlich etwas gespeichert
  ist -- bleibt der Abruf aus oder ist ein Slot leer, bleibt der bisherige
  fest eingebaute Standardinhalt (dieselben zwei echten Events) stehen.
  Kein Blocker, kein sichtbarer Fehler bei Ausfall.
- verwaltung.html: neue Sektion "🏆 Event des Jahres" (nur mit
  EVENTS_MANAGE sichtbar), 2 Karten mit Bild-Upload+Vorschau, Datum,
  Titel, Text, Link, eigenem Speichern-Knopf pro Event.

Ausfuehrlich getestet, weil server-internal wegen fehlender Visual-
Studio-Build-Tools auf dieser Windows-Maschine nicht lokal mit echtem
better-sqlite3 laufen kann: routes/events.js komplett isoliert gegen eine
Fake-DB getestet (11 Szenarien: oeffentlicher Abruf, fehlende Session,
Session ohne Recht, Owner, Rolle MIT EVENTS_MANAGE, alle Validierungen,
Bild-Upload inkl. falscher Dateityp, Abruf des hochgeladenen Bilds).
index.html per Playwright mit echtem Netzwerk-Mocking gegen zwei
Szenarien getestet (API nicht erreichbar -> Standardinhalt bleibt; API
liefert echte Daten -> nur der befuellte Slot wird ueberschrieben, der
leere bleibt Standard). Dabei einen echten CSS-Bug gefunden und behoben
(display:block auf .jahres-event-bild überschrieb die [hidden]-Regel des
Browsers, leeres Bild waere immer sichtbar gewesen). verwaltung.html per
Playwright mit gemocktem Login+API end-to-end getestet: Formular wird
korrekt vorbefuellt, Bild-Upload + Speichern senden die richtigen Daten.

WICHTIG: server-internal laeuft unter einem eigenen Systembenutzer
(dogiintern), auf den ich (claudian) bewusst KEINEN Zugriff habe -- diese
Aenderung kann ich anders als sonst nicht selbst bis auf den Server
bringen. Dogi muss den Deploy-Schritt fuer server-internal selbst
ausfuehren (git pull + npm install + Neustart des dogiintern-Dienstes).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 15:09:54 +02:00
DogFatherGitandClaude Opus 5 3f3ba835a9 Patricks Zitat ergaenzt
Nutzer-Wunsch 20.08.2026: "Wege entstehen dadurch, dass man sie geht."
(alle 5 Sprachen). Ersetzt "Zitat folgt" auf der Scout-Karte.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 14:28:11 +02:00
DogFatherGitandClaude Opus 5 f8f3e65603 Streamer-gesucht-Seite: neues Hero-Bild (Spicy Media, rot)
Nutzer-Wunsch 20.08.2026: bg-bewerben-creator.jpg + -mobile.jpg (bisher
unscharfes Selfie mit pinker Sonnenbrille) ersetzt durch das vom Nutzer
gelieferte Motiv (Spicy-Media-Look, rot, Silhouette + Logo). Alte Version
lokal gesichert (*.jpg.bak-20-08-2026, nicht eingecheckt).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 14:22:01 +02:00
DogFatherGitandClaude Opus 5 f6cbaec132 Zeitreise: Karte "Das Manager-Team wächst" entfernt
Nutzer-Wunsch 20.08.2026. Karte (Juli 2026) samt zugehoeriger i18n-Keys
(zr_ev8_date/_h3/_p, alle 5 Sprachen) entfernt. 16 -> 15 Karten.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 13:16:32 +02:00
DogFatherGitandClaude Opus 5 5c5accd12c Neues Links-Hero-Bild + echtes Community-Foto statt Platzhalter
Nutzer-Wunsch 20.08.2026:
- links.html: neues, vom Nutzer geliefertes "aus dem Universum"-Motiv
  (Weltraum-Szene mit TikTok/Instagram/Snapchat/Discord/Merch-Icons um ein
  "LINKS"-Portal) ersetzt bg-links.jpg + -mobile.jpg. Alte Version lokal
  gesichert (*.jpg.bak-20-08-2026, nicht eingecheckt).
- community.html, Abschnitt "Mehr als Follower": echtes Team-Foto
  (Diene, Ghost, VanVan, Dogi bei einem gemeinsamen Treffen, "TEAM DOGI")
  ersetzt den "Foto folgt"-Platzhalter. Gleiches Muster wie vanvan.html
  (.collage-photo, aspect-ratio:3/4 statt der alten 4/3-Platzhalterbox,
  da das echte Foto Hochformat ist). Jetzt ungenutzte i18n-Keys
  com_bild_platzhalter/com_foto_folgt entfernt.

Per Playwright verifiziert: beide Seiten laden fehlerfrei, keine
fehlgeschlagenen Requests, keine Konsolenfehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 12:54:53 +02:00
DogFatherGitandClaude Opus 5 071f3d0382 Startseite: neue "Event des Jahres"-Kachel unter den 3 Welt-Kacheln
Nutzer-Wunsch 20.08.2026: volle Breite wie die 3 Welt-Kacheln zusammen,
zeigt immer genau zwei Events. Erste Belegung mit zwei bereits auf
zeitreise.html dokumentierten echten Events (28-Stunden-Stream 7. März
2026, Creator Cup 27. Juli - 2. August 2026) statt Platzhaltertext --
Texte sind gekuerzte, inhaltlich unveraenderte Fassungen der bestehenden
Zeitreise-Beschreibungen, beide Karten verlinken auf zeitreise.html. Alle
5 Sprachen gepflegt. Gleiche Karten-/Tag-Optik wie die Welt-Kacheln, damit
es wie ein natuerlicher vierter Baustein wirkt statt wie ein fremdes
Element.

Per Playwright verifiziert: exakt gleiche Breite wie .hero-grid (1180px),
2 Karten, kein horizontales Scrollen auf Mobil, keine Konsolenfehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 12:51:17 +02:00
DogFatherGitandClaude Opus 5 9adebd9bc4 KRITISCH: Server-Quellcode + kompletter Git-Verlauf waren oeffentlich abrufbar
Sicherheits-Audit vor dem geplanten oeffentlichen Start morgen (21.08.2026,
Nutzer-Anfrage: "duerfen die leute keinen zugriff auf veraenderungen haben").
SITE_DIR ist der GESAMTE Repo-Ordner (join(__dirname, "..")), express.static
lieferte daher nicht nur die Website aus, sondern auch:
- server/ (inkl. gate.js, das komplette Sicherheitskonzept im Klartext)
- server-internal/ (Admin-/Supporter-Backend-Quellcode)
- cloudflare-worker/ (altes Backend)
- .git/ (VOLLSTAENDIGE Commit-Historie, rekonstruierbar per Git-Dump)
- CLAUDE.md, DEPLOY.md, wrangler.toml, netlify.toml, gate-worker.js,
  gate.html.bak-07-08-2026 (Alt-Backup-Datei einer frueheren Session)

Live nachgewiesen (mit gueltigem Zugangscode -- morgen faellt die Schranke
fuer ALLE weg): /server/gate.js und /.git/config lieferten HTTP 200.
Ursache: serve-static blockt per Default nur Dateien, deren EIGENER Name
mit einem Punkt beginnt (server/.env -> zufaellig schon 404), aber NICHT
rekursiv -- .git/config wird trotzdem ausgeliefert, weil "config" selbst
nicht mit einem Punkt beginnt, nur der Ordner davor.

Fix: eigene Sperr-Middleware VOR express.static, unabhaengig von
gateMiddleware (bleibt also auch nach dem Entfernen der Zugangsschranke
wirksam). Blockt ganze Ordner (server/, server-internal/,
cloudflare-worker/) + versteckte Ordner/Dateien rekursiv (jedes
Pfadsegment, das mit "." beginnt, ausser .well-known) + eine feste Liste
an Alt-Dateien + jedes *.bak-Muster, damit auch kuenftige Backup-Reste
automatisch mitgeschuetzt sind.

Lokal mit echtem Express-Server verifiziert (gateMiddleware absichtlich
deaktiviert, um exakt den morgigen "oeffentlich"-Zustand zu simulieren):
alle vorher gefundenen Luecken jetzt 404, alle echten Seiten/Assets
(index.html, main.css, main.js, manifest.json, robots.txt, favicon)
weiterhin 200.

Getrennt prooft: server-internal/ (eigener Dienst unter
postfach.dogfather-universe.com, Port 4200) hat sein EIGENES,
unabhaengiges Session-System -- jede /admin/*-Route ist einzeln per
requireTeamSession-Middleware abgesichert (in index.js durchgezaehlt,
keine Ausnahme gefunden), live mit einer unauthentifizierten Anfrage
gegen /admin/users/list bestaetigt (401). Dieser Dienst war nie vom
Website-Gate abhaengig und ist von diesem Fund nicht betroffen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 12:47:36 +02:00
DogFatherGitandClaude Opus 5 1d28d8db2b Kooperation-Seite: Hero-Bild nochmal ausgetauscht (verfeinerte Version)
Nutzer-Wunsch 20.08.2026 (zweite Runde): neues, verfeinertes Motiv (DogFather-
Logo oben, Filipe sitzend mit HasiDog + Husky, "INTERESSE AN EINER
KOOPERATION? JETZT KONTAKT AUFNEHMEN") ersetzt die Version vom selben Tag.
Theme bleibt dogfather/babyblau (passt weiterhin). Alte Version lokal
gesichert (*.jpg.bak-20-08-2026-v2, nicht eingecheckt).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 12:26:59 +02:00
DogFatherGitandClaude Opus 5 7a69f896bc Dienes Profiltext: Alter durch Geburtsmonat ersetzt
Nutzer-Wunsch 20.08.2026: "Ich bin 37 Jahre alt" -> "Ich bin im August 1988
geboren" (und analog in allen 5 Sprachen), Rest des Satzes (verheiratet,
Mutter von zwei Kindern) unveraendert. Betrifft nur bioHtml.de/de-CH/en/
fr/pt in data-modis.js, keine anderen Vorkommen von "37" im Text.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 12:06:48 +02:00
DogFatherGitandClaude Opus 5 bfcf35aafa Kooperation-Seite: neues Hero-Bild + Theme Rot -> Blau
Nutzer-Wunsch 20.08.2026: Hintergrundbild von bewerben-kooperation.html
(bg-bewerben.jpg + -mobile.jpg, beide bisher identisch zum alten roten
Spicy-Media-Motiv "WERDE TEIL VON SPICY MEDIA") ersetzt durch das vom
Nutzer geschickte neue Motiv (HasiDog/DogFather/Husky, "EINE KOOPERATION
WOLLEN? DANN HIER MELDEN", bereits babyblau statt rot). Passend dazu
data-theme von "spicymedia" (Rot/Orange) auf "dogfather" (Babyblau/Lila)
umgestellt -- keine hartkodierten Rot-Werte im HTML, daher genuegt die
Theme-Umstellung fuer Knopf-/Link-/Eyebrow-Farben komplett.

Bild aus PNG-Quelle konvertiert (quality=90, optimize+progressive), Seiten-
verhaeltnis/Aufloesung unveraendert uebernommen (kein Hochskalieren). Alte
Bilder lokal gesichert (*.jpg.bak-20-08-2026, nicht eingecheckt).

Per Playwright verifiziert: data-theme=dogfather, --accent=#8fd9ea, keine
fehlgeschlagenen Requests, keine Konsolenfehler, Formular/Footer sehen im
echten (nicht-fullPage-)Screenshot nach dem Scrollen korrekt dunkel aus --
ein zunaechst gefundener "weisser Kasten" war ein reines Playwright-
fullPage-Screenshot-Artefakt (position:fixed-Hintergrund kachelt beim
kuenstlich verlaengerten Full-Page-Screenshot nicht mit), kein echter Bug,
per echtem gescrolltem Viewport-Screenshot gegengeprueft und ausgeschlossen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 12:04:05 +02:00
DogFatherGitandClaude Opus 5 02f9509042 Zeitreise-Finale-Punkt-Bug + Preis aus dem Abonnieren-Hero-Bild entfernt
1) Nutzer-Report 20.08.2026 ("wieso dieser Punkt da in der Mitte"):
.zeit-node (der leuchtende Zeitleisten-Punkt) ist bei normalen Karten
position:absolute; left:50%; top:50% relativ zu .zeit-ast -- korrekt, weil
die Karte dort nur die halbe Breite einnimmt. Die Finale-Karte
(.zeit-final) wird aber auf fast volle Breite gestreckt und zentriert,
der Knoten landete dadurch mitten im Zitat-Text. Die Verbindungslinie
(::before) wurde dafuer schon frueher ausgeblendet, der Knoten selbst
wurde dabei uebersehen -- jetzt nachgezogen (display:none fuer
.zeit-final .zeit-node).

2) Nutzer-Wunsch 20.08.2026: der Preis "FÜR 4,99 € MONATLICH" stand fest
ins Hero-Bild von abonnieren.html eingebrannt (bg-abonnieren.jpg +
-mobile.jpg, beide Dateien waren identisch) -- per CSS/Text nicht
erreichbar, siehe bereits dokumentierter Fund vom selben Tag. Per Pillow
sauber herausretuschiert (Clone-Stamp aus einem textfreien Bereich
derselben Schaltflaeche, exakt auf die Zeilenhoehe inkl. Ü-Umlautpunkte
skaliert, Nahtstellen weich gezeichnet, mit numpy-Helligkeitsanalyse
zeilenweise gegengeprueft bis keine Text-Reste mehr uebrig waren) --
"ABONNIEREN" bleibt als eigenstaendiger Button stehen, keine sichtbare
Lücke/Leerstelle. Qualitaet/Dateigroesse an das Original angepasst
(quality=90, optimize+progressive, exakt vergleichbare Groesse). Original
lokal gesichert (bg-abonnieren.jpg.bak-20-08-2026, nicht eingecheckt).

Beide Fixes per Playwright verifiziert: Knoten-Punkt display:none bei der
Finale-Karte, Hero-Bild laedt fehlerfrei ohne fehlgeschlagene Requests,
keine Konsolenfehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 11:49:26 +02:00
DogFatherGitandClaude Opus 5 7e369c72ae Zeitreise: echte Daten fuer "Team Dogi entsteht" + "geht online"
Nutzer-Wunsch 20.08.2026:
- "Team Dogi entsteht": "Datum folgt" -> "Anfang 2025" (neuer Key
  zr_ev2_date, alle 5 Sprachen). Bleibt an ihrer Stelle -- liegt weiterhin
  chronologisch vor "Ostern 2025".
- "DOGFATHER UNIVERSE geht online": "2026 - Datum folgt" -> "21. August
  2026, 21 Uhr" (morgen, live). Bleibt ebenfalls an ihrer Stelle.

Per Playwright komplette Reihenfolge erneut gegengeprueft: weiterhin
chronologisch korrekt, keine Konsolenfehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 11:36:47 +02:00
DogFatherGitandClaude Opus 5 895f04c524 Zeitreise: echtes Datum fuer "Start als Content Creator" (24.10.2023)
Nutzer-Wunsch 20.08.2026. Neuer i18n-Key zr_ev1_date (alle 5 Sprachen),
Karte bleibt weiterhin an erster Stelle, da 2023 das fruehste Datum der
gesamten Zeitleiste ist. Per Playwright die komplette Reihenfolge erneut
durchgezaehlt: weiterhin korrekt, keine Konsolenfehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 11:33:55 +02:00
DogFatherGitandClaude Opus 5 f750fe9dfd Zeitreise: echte Daten fuer Buch + Spotify, chronologisch einsortiert
Nutzer-Wunsch 20.08.2026: "Das Buch HasiDog & Casper" (November 2025) und
"HasiDog startet auf Spotify" (1. Januar 2026) hatten bisher "Datum folgt"
und lagen dadurch weit hinten in der Zeitleiste, obwohl beide zeitlich vor
dem 28-Stunden-Stream (7. März 2026) liegen. Neue i18n-Keys zr_ev13_date/
zr_ev14_date (alle 5 Sprachen) ergaenzt, beide Karten an die chronologisch
richtige Stelle verschoben: Ostern 2025 -> November 2025 (Buch) ->
1. Januar 2026 (Spotify) -> 7. März 2026 (28-Stunden-Stream) -> ...

"Kooperation mit VanVan Teddys" bleibt bewusst unveraendert bei "Datum
folgt", da dafuer noch kein Datum genannt wurde.

Per Playwright die komplette Reihenfolge alle 16 Karten durchgezaehlt und
gegengeprueft: chronologisch korrekt, keine Konsolenfehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 11:26:19 +02:00
DogFatherGitandClaude Opus 5 9cb4e177ee Zeitreise: 4 Ereignisse entfernt + einladender "wächst noch"-Hinweis
Nutzer-Wunsch 20.08.2026: vier Zeitleisten-Karten sollten komplett weg
("Ein neues Kapitel beginnt" / Mai 2026, "Die ersten betreuten Creator",
"Weitere Scouts und Manager kommen hinzu", "Das erste Community-Treffen") --
entfernt aus zeitreise.html samt der zugehoerigen, jetzt ungenutzten
i18n-Keys (zr_ev5_*, zr_ev16_*, zr_ev17_*, zr_ev18_*). 20 -> 16 Karten.

Zusaetzlich: gleich am Seitenanfang soll auffallen, dass diese Zeitreise
noch nicht fertig ist. Bewusst NICHT die alte .todo-note-Optik reaktiviert
(die war fuer Entwickler gedacht und wurde am 31.07.2026 sitewide bewusst
unsichtbar gemacht) -- stattdessen ein neuer, fuer Besucher gestalteter
Hinweis (.zeit-baustelle), der zum Wachstums-Baum-Thema der Seite passt
(🌱 "Der Anfang" oben, 🌳 "...wächst weiter" unten): "Diese Zeitreise
wächst noch", babyblauer Glow-Rahmen, leicht wiegendes Setzlings-Icon.
Alle 5 Sprachen gepflegt.

Per Playwright verifiziert: alle 4 Karten wirklich weg (Text-Suche),
16 statt 20 .zeit-ast-Elemente, Hinweisbox sichtbar mit korrektem Text,
keine Konsolenfehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 11:23:38 +02:00
DogFatherGitandClaude Opus 5 c4fa132244 DogiCrew-Sperre: auch die Registrierungsfelder selbst nicht mehr eintippbar
Nutzer-Feedback 20.08.2026 (direkte Nachpruefung): "ich kann immer nur was
rein tippen, soll garnicht möglich sein bitte" -- der Registrieren-Knopf
war gesperrt, aber Name/TikTok/E-Mail-Feld ließen sich weiterhin normal
beschreiben. Jetzt disabled, exakt wie das E-Mail-Feld im Login-Panel
(voriger Commit). Per Playwright verifiziert: echter Tippversuch in allen
drei Feldern hinterlässt keinen Wert mehr.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 11:10:26 +02:00
DogFatherGitandClaude Opus 5 d98133faba DogiCrew-Sperre vervollstaendigt: Login-Einstieg, E-Mail-Feld, Preis
Nutzer-Feedback 20.08.2026 (Nachpruefung des vorherigen Commits): der
Registrieren-Knopf war gesperrt, aber drei Luecken blieben offen --

1. "Schon Supporter? Hier einloggen" fuehrte zu einem VOLL FUNKTIONSFAEHIGEN
   Login-Code-Anfordern-Knopf. E-Mail-Feld jetzt disabled (nicht eintippbar,
   wie gewuenscht) und btn-login-request auf denselben .btn-gold-locked-Stil
   wie der Registrieren-Knopf umgestellt (natives disabled-Attribut).
2. Dritte, bislang uebersehene Luecke: "Mit Google anmelden" waere ein
   weiterer Weg gewesen, trotz gesperrter Knoepfe ein echtes Konto anzulegen,
   falls GOOGLE_CLIENT_ID serverseitig konfiguriert ist. initGoogleConsent()
   wird jetzt nicht mehr aufgerufen, solange die Registrierung gesperrt ist --
   ein Kommentar markiert genau die Stelle zum spaeteren Reaktivieren.
3. Der Preis (4,99 €) durfte laut Nutzer noch nicht sichtbar sein -- war aber
   an zwei Stellen zu sehen: der grossen Preis-Zahl in der Box (jetzt
   "Coming soon…" im selben Gold-Schimmer-Stil wie der gesperrte Knopf) und
   im Fliesstext (ab_text2, alle 5 Sprachen: "Für 4,99 € im Monat" ->
   "Mit deinem/dim/your/ta/sua monatlichen Beitrag", nur die erste Teilphrase
   geaendert, Rest jeder Uebersetzung unangetastet).

Betrifft ausschliesslich den oeffentlichen Anmelde-Einstieg -- bereits
eingeloggte Supporter (Dogi/VanVan als Test-Accounts) sind ueber ihren
gespeicherten Token/supporter.html unveraendert erreichbar.

Bekannter, NICHT in diesem Commit geloester Rest: der grosse Hero-Banner
oben auf der Seite (assets/img/bg-abonnieren(-mobile).jpg) zeigt den Preis
ebenfalls fest ins Bild eingebrannt ("ABONNIEREN FÜR 4,99 € MONATLICH") --
das ist Bildmaterial, keine Text-/CSS-Aenderung, braucht Ruecksprache mit
Dogi bevor daran gearbeitet wird.

Vor dem Commit per Playwright verifiziert: Preis-Box zeigt nur noch
"Coming soon…", Login-E-Mail-Feld nimmt keine Eingabe an, Klick auf den
gesperrten Login-Knopf loest keinen /supporter/login-request-Aufruf aus,
Google-Bereich bleibt hidden, keine Konsolenfehler.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 11:07:24 +02:00
DogFatherGitandClaude Opus 5 5cd473e71a DogiCrew-Registrierung: "Coming Soon"-Sperre + Bald-Badge im Nav-Knopf
Nutzer-Wunsch 20.08.2026: DogiCrew ist "das geilste Bonus" -- der Registrieren-
Knopf soll noch nicht bedienbar sein (Dogis PayPal-Business-Zugangsdaten fehlen
noch, siehe DogFather Website - Offene Punkte.md), aber Besucher sollen ueber
den Nav-Knopf trotzdem ganz normal auf die Seite kommen und die Vorschau sehen
duerfen -- "soll alles bleiben" ausser dem einen Knopf.

- Nav-Knopf (DogiCrew/Abo aktivieren, main.js renderAboButton): neues kleines
  Schimmer-Badge "Bald" direkt in der Pille (main.css .nav-cta-soon, reine
  Wiederverwendung von .btn-silver-text/-spark in kleinerem Massstab). Nur auf
  den drei Zielen, die zur (noch gesperrten) Registrierung fuehren -- NICHT auf
  supporter.html, das ist der echte, bereits funktionierende Bereich fuer
  Dogi/VanVan als Test-Supporter.
- abonnieren.html: Registrieren-Knopf ersetzt durch neue .btn-gold-locked-
  Komponente -- eigener Gold/Schloss-Stil (nicht das schon anderswo auf dieser
  Seite vergebene .btn-silver), natives disabled-Attribut (kein JS noetig,
  disabled-Buttons feuern keine Click-Events -- der bestehende Listener bleibt
  unveraendert und inert). Formularfelder bleiben normal ausfuellbar (Teaser),
  nur der Absende-Knopf ist gesperrt.

Bug waehrend der Umsetzung gefunden UND behoben, nicht nur uebersehen: die
rotierende Conic-Gradient-Randmaske von .btn-silver/.btn-legendary (copy-paste
als erster Versuch) verzieht sich auf diesem sehr langgestreckten 100%-Breite-
Knopf zu einer krummen Schlaufe -- exakt die dokumentierte Lektion in
Projektregeln.md Punkt 14 (03.08.2026, TikTok-Button-Bug), die beim ersten
Entwurf übersehen wurde. Per Playwright-Screenshots über mehrere Animations-
Frames nachgewiesen (nicht nur vermutet) und auch am bereits LIVE laufenden
.btn-silver auf dieser Seite reproduziert, um auszuschliessen, dass es an der
neuen Komponente statt an der Technik selbst liegt. Fix: derselbe sichere
Zwei-Layer-Background-Trick wie bei .abo-google-frame (gleiche Datei) --
Bewegung nur ueber background-position, bleibt geometrieunabhaengig exakt an
der Kontur. Nach dem Fix erneut ueber mehrere Frames verifiziert, sauber.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 10:51:34 +02:00
DogFatherGitandClaude Opus 5 0223fcf9cd Mobil-Menü: schwebende Eck-Knöpfe blockierten Sprachschalter + Bewerben-Button
Nutzer-Report 20.08.2026: "auf dem Handy... passt nicht immer alles". Per echter
Mobil-Emulation (iPhone 15 Pro + Pixel 8, Playwright) nachgestellt statt geraten:
Die schwebenden Eck-Knöpfe (Musik-Widget unten rechts, Vorschau-Umschalter fuer
Dogi/VanVan unten links) sind absichtlich immer sichtbar (position:fixed, direkte
<body>-Kinder). Beim aufgeklappten Mobil-Hamburger-Menü lagen sie dadurch sichtbar
UEBER den untersten Menuepunkten (Sprachschalter "DE" + "Bewerben"-Knopf) und
verdeckten sie -- auf Screenshots klar zu sehen, betrifft jede der ueber 30 Seiten,
da das Menü ueberall gleich ist. Genau die Art Bug, die im Browser (Adressleiste
noch da, man kann drumrum navigieren) kaum auffaellt, aber in der installierten
Vollbild-App voll durchschlaegt.

Fix: :has()-Selektor blendet beide Widgets aus, solange .nav-links.open ist --
kein gemeinsamer Elternknoten mit dem Menue vorhanden, daher CSS statt weiterer
main.js-Logik. Nach dem Fix per Regressionstest ueber 6 Seiten x 2 Geraete erneut
verifiziert: Widgets erscheinen normal wieder, sobald das Menue schliesst, keine
neuen Konsolenfehler, kein horizontales Scrollen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 10:28:22 +02:00
678 changed files with 133670 additions and 878 deletions
+27
View File
@@ -0,0 +1,27 @@
# Zeilenenden festlegen.
#
# Entwickelt wird auf Windows, ausgeliefert wird auf Linux. Windows
# beendet Zeilen mit zwei Zeichen (CR LF), Linux mit einem (LF). Bei
# Textdateien ist das folgenlos -- bei Skripten nicht: Aus der ersten
# Zeile "#!/bin/bash" wird dann "#!/bin/bash\r", und Linux sucht nach
# einem Programm, dessen Name auf ein unsichtbares Zeichen endet. Der
# Fehler lautet dann "bad interpreter" und nennt einen Pfad, der voellig
# richtig aussieht.
#
# Deshalb: Alles, was auf dem Server ausgefuehrt wird, bekommt fest
# Unix-Zeilenenden -- unabhaengig davon, womit es bearbeitet wurde.
*.sh text eol=lf
*.mjs text eol=lf
*.js text eol=lf
*.json text eol=lf
*.sql text eol=lf
*.yml text eol=lf
# Bilder und Schriften nie anfassen.
*.png binary
*.jpg binary
*.webp binary
*.ico binary
*.woff binary
*.woff2 binary
+75
View File
@@ -9,3 +9,78 @@ cloudflare-worker/node_modules/
.env.*
!.env.example
server/node_modules/
# Sicherungskopien von Bildern (entstehen beim Optimieren, gehoeren nicht
# ins Verzeichnis). Am 22.08.2026 sind 13 davon versehentlich durch ein
# "git add -A" mitgewandert -- 2,6 MB, die niemand braucht.
*.bak-*
# Lokaler Testserver.
server/tmp-lokal.mjs
# ⚠️ HIER STAND server/package-lock.json (aufgenommen 26.08.2026).
#
# Ausgeschlossen wurde sie, weil ein "git pull" auf dem Server damals
# abbrach: Git weigert sich, eine unverfolgte Datei zu ueberschreiben --
# unabhaengig davon, ob ihr Inhalt derselbe ist. Der Ausschluss hat den
# Deploy repariert und dabei den Zweck der Datei beseitigt.
#
# Ohne sie darf jede Installation andere Fassungen ziehen: "^4.21.2"
# erlaubt alles bis unter 5.0. Auf dem Server laeuft deshalb express
# 4.22.2, waehrend hier 4.21.2 steht -- geprueft wird also nie genau
# das, was ausgeliefert wird. Faellt so ein Unterschied auf, dann im
# Betrieb.
#
# Nachgemessen: Die Datei hier und die auf dem Server sind Byte fuer
# Byte identisch (SHA-256 a50b028d…). Es gab also nie einen inhaltlichen
# Konflikt, nur einen formalen -- und der loest sich, indem die Datei
# einmal vom Server entfernt und danach aus dem Repo geholt wird.
bild-*.png
# Aufnahmen der Handy-Pruefung (pruef-verwaltung-handy.mjs). Sie
# entstehen bei jedem Lauf neu und sind Zwischenstand, kein Quelltext --
# im Repo waeren sie nur Ballast und wuerden bei jedem Lauf als
# Aenderung erscheinen.
handy-*.png
# Laufprotokolle der Pruefungen -- Ergebnis, kein Quelltext.
# Ein Muster statt einer Liste: Sonst haette jede neue Pruefung ihre
# eigene Zeile gebraucht, und die vergisst man. Genau das ist am
# 05.09.2026 passiert -- vier neue Protokolle standen ploetzlich als
# Aenderung im Arbeitsstand.
pruef-*-lauf.txt
server/pruef-*-lauf.txt
# Ergebnis des Sammellaufs (tools/alles-pruefen.mjs)
gesamtlauf.txt
# Bilder zum Ansehen (server/bild-*.mjs) -- entstehen bei jedem Lauf neu
tagesblick-*.png
chat-*.png
calls-*.png
sicht-*.png
# ---------------------------------------------------------------------
# Bilder der Pruefungen (06.09.2026)
#
# Die Pruefdateien schreiben Bildschirmfotos, um zeigen zu koennen, was
# sie gesehen haben -- 62 Stueck, 54 MB, und bei JEDEM Lauf neu. Sie
# standen bisher im Repo und tauchten dadurch bei jedem Commit als
# Aenderung auf: Man haette sie mitcommittet oder jedes Mal von Hand
# aussortiert. Beides ist Ballast.
#
# GEPRUEFT VOR DEM ENTFERNEN: Keine einzige Pruefung LIEST je ein Bild
# (kein readFileSync, kein existsSync auf .png) -- sie werden nur
# geschrieben und angesehen. Es sind also keine Vergleichsbilder, deren
# Verlust eine Pruefung blind machen wuerde.
#
# NICHT betroffen und bewusst weiter versioniert: alles unter
# assets/ und workspace/assets/ -- dort liegen die QR-Codes und die
# Bilder der Website. Beim ersten Anlauf waeren sie um ein Haar
# mitgegangen, weil ein zu grobes Muster sie eingeschlossen hatte.
pruef-*.png
server/pruef-*.png
abnahme-*.png
server/abnahme-*.png
# Messlaeufe von tools/mess-rueckgabewerte.sh -- Ergebnis eines Laufs,
# kein Quelltext. Gehoert nicht in die Geschichte.
rueckgabewerte-*.txt
+9 -8
View File
@@ -3,9 +3,10 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="assets/img/favicon.png" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/universe-32.png" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/universe-180.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#0b0d10" />
<meta name="theme-color" content="#065f76" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="DOGFATHER UNIVERSE" />
<meta property="og:title" content="Seite nicht gefunden — DOGFATHER UNIVERSE" />
@@ -18,10 +19,10 @@
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<meta name="robots" content="noindex" />
<title>Seite nicht gefunden — DOGFATHER UNIVERSE</title>
<link rel="stylesheet" href="assets/css/main.css" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
<link rel="stylesheet" href="assets/css/main.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260828e" />
</head>
<body>
<div id="site-header"></div>
@@ -41,7 +42,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-404.js"></script>
<script src="assets/js/main.js"></script>
<script src="assets/js/i18n-404.js?v=20260828e"></script>
<script src="assets/js/main.js?v=20260828e"></script>
</body>
</html>
+326 -50
View File
@@ -1,70 +1,346 @@
# DOGFATHER UNIVERSE — Go-Live-Anleitung
## ✅ Status: bereits live, eigene Domain seit 03.08.2026 angeschlossen
## ⚠️ ZUERST LESEN: Die echte Seite läuft auf dem NETCUP-SERVER, nicht auf Cloudflare
Die Website läuft — kostenlos, auf deinem eigenen Cloudflare-Account, unter drei Adressen
gleichzeitig (alle zeigen auf denselben Worker):
Seit dem öffentlichen Start (21.08.2026) wird `dogfather-universe.com` vom **eigenen
Netcup-Server** ausgeliefert. Der alte Cloudflare-Worker existiert zwar noch und lässt sich
auch weiterhin deployen — **er erreicht die echte Domain aber nicht mehr.**
- **Eigene Domain:** https://dogfather-universe.com
- **www-Variante:** https://www.dogfather-universe.com
- **workers.dev (bleibt zusätzlich aktiv):** https://dogfather-universe.dogfather1608.workers.dev
- **Bewerbungs-Postfach-Worker:** https://dogfather-universe-postfach.dogfather1608.workers.dev
Das ist die gefährlichste Stelle im ganzen Projekt: `npx wrangler deploy` läuft ohne Fehler
durch und meldet „Deployed", die Änderung ist danach auf `…workers.dev` sichtbar — und auf
`dogfather-universe.com` passiert **nichts**. Genau so ist es am 22.08.2026 beim Sprachfenster-
Fix passiert (siehe unten). Wer nur die Erfolgsmeldung von Wrangler liest, meldet „ist live",
obwohl Filipe auf dem Handy weiterhin den kaputten Stand sieht.
Domain-Anbindung lief über `routes` mit `custom_domain = true` in `wrangler.toml` — da die Zone
`dogfather-universe.com` schon vorher auf Cloudflare lag, waren DNS + SSL-Zertifikat sofort beim
Deploy automatisch aktiv, kein manueller DNS-Schritt nötig. Alle
`REPLACE-WITH-YOUR-DOMAIN.tld`-Platzhalter im Code sind bereits durch die echte Domain ersetzt
(Social-Media-Vorschaubilder, Sitemap, robots.txt).
| Adresse | Läuft wo | Wird von wo beliefert |
|---|---|---|
| **`dogfather-universe.com`** ← **die echte Seite** | **Netcup**, Caddy → `localhost:4100`, systemd-Dienst `dogiweb.service`, Verzeichnis `/home/dogiweb/dogfather-universe/` | Gitea `git.dogfather-universe.com/DogFatherGit/dogfather-universe`, Zweig `main` |
| `www.dogfather-universe.com` | dasselbe (Caddy nimmt beide Namen) | dasselbe |
| `dogfather-universe.dogfather1608.workers.dev` | Cloudflare Worker (Altbestand) | `npx wrangler deploy` |
**Noch offen:** `assets/img/og-cover.jpg` (1200×630px Social-Preview-Bild) fehlt noch — optional,
nicht blockierend. CORS im Postfach-Worker ist weiterhin bewusst offen (`"*"`) statt auf die
Domain eingeschränkt, siehe TODO-Kommentar in `cloudflare-worker/src/lib/http.js` (eigener,
größerer Umbau nötig, um keine Login-/Zahlungsendpunkte zu riskieren).
Erkennungsmerkmal im Zweifel: `curl -sI https://dogfather-universe.com/ | grep -i via`
→ zeigt `via: 1.1 Caddy`, also Netcup. Käme die Seite von Cloudflare, stünde da kein Caddy.
## Änderungen künftig live schalten
Jede Änderung an den Dateien im Ordner `DogiHompage/` wird erst live, wenn erneut deployed
wird:
## So wird eine Änderung wirklich live (der einzige gültige Weg)
```bash
cd DogiHompage
npx wrangler deploy
cd ~/Documents/Obelix/DogiHompage
# 1. Änderung committen
git add <dateien> && git commit
# 2. In die Ablage schieben (Gitea ist die Quelle der Wahrheit)
git push gitea master:main
# 3. Auf dem Server holen
ssh dogfather-server "cd /home/dogiweb/dogfather-universe && git pull --ff-only origin main"
```
Für Änderungen am Bewerbungs-Postfach-Worker selbst (`cloudflare-worker/src/worker.js`):
Statische Dateien (HTML/CSS/JS/Bilder) sind damit **sofort** live — der Express-Dienst liefert
das Verzeichnis direkt aus, ein Neustart ist dafür **nicht** nötig.
## ⚠️ ES GIBT ZWEI CHECKOUTS AUF DEM SERVER, NICHT EINEN
Am 22.08.2026 beim Ausrollen des Webdesign-Bereichs gefunden — diese Datei
beschrieb vorher nur den ersten und war damit unvollständig:
| Dienst | Checkout | Liefert |
|---|---|---|
| `dogiweb.service` (Port 4100) | `/home/dogiweb/dogfather-universe/` | die öffentliche Website + `server/` |
| `dogiintern.service` (Port 4200) | `/home/dogiintern/dogfather-universe/` | die API unter `postfach.dogfather-universe.com` + `server-internal/` |
**Ein `git pull` in `/home/dogiweb` ändert an `server-internal/` also gar nichts.**
Wer nur dort zieht und danach `dogiintern.service` neu startet, startet den
Dienst mit unverändertem Code neu und wundert sich, warum nichts passiert.
`claudian` hat auf `/home/dogiintern/` **keinen Zugriff** (weder lesend noch
über den begrenzten sudo). Änderungen an `server-internal/` muss deshalb
Filipe selbst ausrollen.
**Der Standard-Fall** (nur Code geändert, keine neuen/geänderten Pakete):
```bash
cd DogiHompage/cloudflare-worker
npx wrangler deploy
sudo -u dogiintern git -C /home/dogiintern/dogfather-universe pull --ff-only origin main
sudo systemctl restart dogiintern.service
```
## Alternative Hosting-Optionen (falls du weg von Cloudflare willst)
Die Seite ist reines statisches HTML/CSS/JS und läuft überall. Configs liegen bereit:
- **Netlify:** `netlify.toml` liegt vor — Ordner `DogiHompage/` auf netlify.com hochladen
oder Repo verbinden.
- **GitHub Pages:** `.nojekyll` liegt vor — Repo pushen, unter **Settings → Pages** aktivieren.
In beiden Fällen bleibt der Bewerbungs-Postfach-Worker unabhängig auf Cloudflare bestehen (er ist
eine reine API, kein Teil des Frontends) — `API_BASE_URL` in `assets/js/forms.js`, `index.html`
und `postfach.html` zeigt bereits dorthin.
## Git-Repo
Bereits eingerichtet (`DogiHompage/.git`), erster Commit vorhanden. Bei Bedarf zu GitHub
pushen:
**Der volle Fall** (auch `package.json`/`package-lock.json` geändert — dann MUSS
`npm ci` mit, sonst laufen Code und Pakete auseinander). Ein Block, bricht bei
jedem Fehler ab, und startet den Dienst erst neu, wenn Pull, Installation und
ein Lade-Test der nativen Module (better-sqlite3) durch sind — schlägt etwas
fehl, läuft der alte Dienst unberührt weiter:
```bash
cd DogiHompage
git remote add origin <repo-url>
git push -u origin main
sudo -u dogiintern git -C /home/dogiintern/dogfather-universe pull --ff-only origin main && \
sudo -u dogiintern bash -c 'cd /home/dogiintern/dogfather-universe/server-internal && npm ci' && \
sudo -u dogiintern node -e "require('/home/dogiintern/dogfather-universe/server-internal/node_modules/better-sqlite3'); console.log('native Module laden: ok')" && \
sudo systemctl restart dogiintern.service && \
sleep 6 && echo "Dienst:" $(systemctl is-active dogiintern.service) && \
curl -s -o /dev/null -w "health nach Neustart: %{http_code}\n" https://postfach.dogfather-universe.com/health
```
## Nach jedem Go-Live-Schritt prüfen
**Wichtig — kein führendes `cd`.** `/home/dogiintern/` steht auf 700; selbst
`dogi` darf per `cd` nicht hinein, nur der Benutzer `dogiintern` über `sudo -u`.
Deshalb `git -C <pfad>` (braucht kein cd) und das `npm ci` in einer
`sudo -u dogiintern bash -c 'cd … && npm ci'`-Shell, die den Wechsel als
dogiintern ausführt. Ein `cd` als erste Zeile scheitert an „Keine Berechtigung".
- [ ] Website lädt: https://dogfather-universe.dogfather1608.workers.dev
- [ ] Bewerbungsformular sendet erfolgreich (im Postfach zeigt sich ein neuer Eintrag)
- [ ] Live-Punkt auf der Startseite reagiert auf den Toggle im Postfach
- [ ] Nach Domain-Wechsel: Link-Vorschau testen, z.B. mit dem
[Facebook Sharing Debugger](https://developers.facebook.com/tools/debug/)
**Zwei Warnungen von `npm ci`, die HARMLOS sind (am 28.08.2026 verifiziert):**
1. `npm warn allow-scripts … better-sqlite3 … (install: prebuild-install || node-gyp rebuild)`
— better-sqlite3 baut sich normalerweise über ein Install-Skript. Die
allowScripts-Sperre auf dem Konto blockiert das, aber `prebuild-install`
lädt ein fertig kompiliertes Binary, das ohne den Build-Schritt funktioniert.
**Kontrolle:** Läuft der Dienst danach (`active`) und steht in den Logs
„… Einstellungen aus der Datenbank geladen", ist better-sqlite3 in Ordnung.
Der `native Module laden: ok`-Schritt im Block prüft genau das vorab.
2. `npm warn deprecated [email protected]` — nur ein Hinweis, kein Fehler.
**`sleep 6`, nicht 2.** Der Dienst braucht nach dem Neustart ein paar Sekunden,
bis er auf Port 4200 hört. Ein zu früher health-Check meldet sonst kurzzeitig
`502` (Caddy erreicht den Dienst noch nicht) und `Dienst: activating`, obwohl
gleich darauf alles läuft. Erst bei anhaltendem 502/activating ist wirklich
etwas kaputt — dann `sudo systemctl status dogiintern.service --no-pager` ansehen.
Erwartete letzte Zeilen: `native Module laden: ok`, `Dienst: active`,
`health nach Neustart: 200`. Kommt stattdessen ein Abbruch VOR dem Neustart,
ist nichts passiert — der alte Dienst läuft weiter, und der Fehler (meist ein
fehlgeschlagener `npm ci`) lässt sich in Ruhe ansehen.
## Server-Code geändert? Dann ist ein Neustart PFLICHT
Statische Dateien sind nach dem Pull sofort live. Server-Code **nicht** —
der läuft weiter mit dem alten Stand, bis der Dienst neu startet.
```bash
ssh dogfather-server "sudo systemctl restart dogiweb.service" # nach Änderungen in server/
ssh dogfather-server "sudo systemctl restart dogiintern.service" # nach Änderungen in server-internal/
```
**Warum das hier besonders steht (Vorfall 22.08.2026):** Nach dem Pull des
Webdesign-Bereichs war `/webdesign/` für einige Minuten **ohne Zugangsschutz
öffentlich erreichbar** (HTTP 200 statt der Umleitung zur Zugangswand). Die
HTML-Dateien waren durch den Pull sofort da — die Schranke in
`server/webdesign-gate.js` lief aber erst nach dem Neustart von
`dogiweb.service`. Bei einem Bereich, der ausdrücklich nicht öffentlich sein
soll, ist genau dieses Zeitfenster der gefährliche Teil eines Deploys.
Merksatz: **Erst neu starten, dann „ist live" melden** — und danach mit
`curl -I` prüfen, dass die Schranke wirklich greift:
```bash
curl -sI https://dogfather-universe.com/webdesign/ | grep -iE "^location|x-robots-tag"
# erwartet: location: /webdesign/zugang.html?next=... und x-robots-tag: noindex, ...
```
**Achtung bei Zweig-Namen:** lokal heißt der Zweig `master`, auf dem Server und in Gitea `main`.
Deshalb `master:main` beim Push. Ein blankes `git push` schiebt sonst nach `gitea/master` —
einen alten, abgehängten Zweig, den niemand ausliefert.
## Abhängigkeiten: `npm ci`, nicht `npm install`
Seit dem 26.08.2026 ist `package-lock.json` versioniert. Auf dem Server gilt
deshalb:
```bash
npm ci # richtig: installiert exakt das, was in der Lock-Datei steht
npm install # falsch: darf neuere Fassungen ziehen und schreibt die Datei um
```
**Warum der Unterschied zählt.** In `package.json` steht `"express": "^4.21.2"` —
das erlaubt alles unter 5.0. Vor dieser Umstellung lief auf dem Server deshalb
express **4.22.2**, während hier 4.21.2 stand. Geprüft wurde also nie ganz das,
was ausgeliefert wurde. Solche Unterschiede fallen nicht beim Deploy auf,
sondern im Betrieb — und dann sucht man den Fehler im eigenen Code.
`npm ci` löscht `node_modules` vorher vollständig und baut streng nach der
Lock-Datei neu auf. Es schreibt sie nie um; passt sie nicht zur `package.json`,
bricht es ab, statt still etwas anderes zu installieren.
**Die Lock-Datei war früher ausgeschlossen** (`.gitignore`), weil ein `git pull`
daran scheiterte: Git überschreibt keine unverfolgte Datei, auch wenn ihr Inhalt
derselbe ist. Das war ein formaler Konflikt, kein inhaltlicher — nachgemessen
waren beide Fassungen Byte für Byte identisch. Wenn dieser Fall irgendwo erneut
auftritt, ist die Lösung, die Datei auf dem Server **einmal** zu entfernen und
danach aus dem Repo zu holen:
```bash
ssh dogfather-server "rm -f /home/dogiweb/dogfather-universe/server/package-lock.json"
ssh dogfather-server "cd /home/dogiweb/dogfather-universe && git pull --ff-only origin main"
```
## ⚠️ Am Webdesign-Bereich geändert? Dann ZWEI Zahlen hochzählen
Die App speichert Dateien zwischen. Nach einer Änderung an `webdesign/`
oder `assets/` müssen **beide** Stellen hoch, sonst bekommen Geräte, die
die Seite schon einmal geöffnet haben, weiterhin den alten Stand:
```
webdesign/sw.js const CACHE_NAME = "dogfather-webdesign-vNN";
assets/js/wd-core.js .register("/webdesign/sw.js?v=NN", …)
```
**Warum zwei und nicht eine.** `CACHE_NAME` wirft den Zwischenspeicher
weg, sobald der Service Worker startet. Die Nummer in der Adresse sorgt
dafür, dass er überhaupt neu geladen wird — und das ist hier nicht
selbstverständlich:
Gemessen am 26.08.2026: Der Server liefert `sw.js` mit
`Cache-Control: no-cache` aus. **Cloudflare ersetzt das durch
`max-age=14400`** — vier Stunden, auch bei `cf-cache-status: MISS`.
Ursache ist eine feste „Browser Cache TTL" in den Cloudflare-Einstellungen.
Ohne die Nummer in der Adresse erreicht jede Änderung am Service Worker
die Geräte also bis zu vier Stunden zu spät. Solange alles läuft, fällt
das nie auf; ist der Stand fehlerhaft, sind es vier Stunden ohne
Reparaturmöglichkeit.
## Was auf dem Server automatisch läuft
Beides über `/etc/cron.d/`, beides als root. Die Skripte liegen im Repo
und werden über den normalen `git pull` aktualisiert — ein eigener
Deploy-Schritt ist nicht nötig.
| Wann | Was | Skript |
|---|---|---|
| täglich 03:15 | Sicherung von Datenbank und Uploads | `server-internal/sicherung.sh` |
| alle 5 Minuten | Wächter über 19 Punkte (inkl. „läuft die Sicherung?") | `server-internal/waechter.mjs` |
**Sicherung.** Nutzt SQLites eigenen `.backup`-Befehl, keine Dateikopie —
die Datenbank ist 778 KB groß, ihr WAL 4,1 MB, eine Kopie der `.db`
allein wäre also weitgehend leer. 14 tägliche Stände, sonntags zusätzlich
ein Wochenstand (8 davon). Jeder Stand wird sofort nach dem Anlegen
geprüft. Wiederherstellung: `server-internal/wiederherstellen.sh`, zeigt
ohne Argument die verfügbaren Stände.
**Wächter.** Prüft sechs Dienste, neun Adressen, Plattenplatz und zwei
Zertifikatslaufzeiten. Meldet per Push, und zwar **nur bei
Zustandswechsel** — nicht alle fünf Minuten dasselbe. Läuft als root und
verschickt selbst; ein Wächter, der über den internen Dienst meldet,
wäre ausgerechnet dann still, wenn dieser das Problem ist.
Meldeweg prüfen, ohne auf eine Störung zu warten:
```bash
sudo /usr/local/bin/node /home/dogiweb/dogfather-universe/server-internal/waechter.mjs --probe
```
⚠️ **Grenze:** Ist der Server als Ganzes weg — Netz, Strom, Hardware —,
meldet auch der Wächter nichts. Dagegen hilft nur eine Überwachung
außerhalb der Maschine.
## Die Verwaltung ist eine eigene App
Seit dem 26.08.2026 hat `webdesign/verwaltung.html` ein **eigenes**
Manifest (`verwaltung.webmanifest`) mit eigener `id` und eigenem Symbol.
Ohne die unterschiedliche `id` hielten Browser beide für dieselbe App,
und die zweite Installation überschriebe die erste.
Das Manifest steht in der Ausnahmeliste von `server/webdesign-gate.js` —
ohne diesen Eintrag antwortet es mit 302 auf die Zugangswand, und der
Browser bietet „App installieren" gar nicht erst an.
## Pflicht-Prüfung nach JEDEM Deploy
Nicht auf die Erfolgsmeldung des Deploy-Befehls verlassen, sondern **die echte Domain fragen**:
```bash
# Kommt meine Änderung wirklich auf der Seite an, die Filipe benutzt?
curl -s https://dogfather-universe.com/assets/css/main.css | grep "<mein Merkmal>"
```
Erst wenn das anschlägt, gilt eine Änderung als erledigt. Bei optischen Änderungen zusätzlich
mit einem echten Browser im Handy-Format nachsehen (Playwright, Viewport 390×844) und einen
Screenshot machen — Text im Quelltext beweist noch nicht, dass es auch richtig aussieht.
## Der Cloudflare-Worker (Altbestand)
Bleibt vorerst bestehen, liefert aber nur noch `…workers.dev` aus. Ein Deploy dorthin ändert
an der echten Seite nichts. Der Versuch, die Domain per `wrangler` wieder anzubinden, schlägt
bewusst fehl:
```
Hostname 'dogfather-universe.com' already has externally managed DNS records
```
Das ist **kein Fehler, den man beheben sollte** — es ist die Schutzwirkung davon, dass die
Domain jetzt auf Netcup zeigt. `routes` mit `custom_domain = true` steht deshalb nur noch aus
historischen Gründen in `wrangler.toml`.
Der Bewerbungs-Postfach-Worker ist davon unabhängig und läuft weiter auf Cloudflare:
`https://dogfather-universe-postfach.dogfather1608.workers.dev` (`API_BASE_URL` in
`assets/js/forms.js`, `index.html`, `postfach.html`).
## Git-Ablage
- **Quelle der Wahrheit:** Gitea, `git.dogfather-universe.com/DogFatherGit/dogfather-universe`,
Zweig `main` (auf dem eigenen Server, kostenlos, in eigener Hand).
- Lokal heißt das Fernziel `gitea`, der Zweig `master`.
- Auf dem Server heißt das Fernziel `origin`, der Zweig `main`.
- `gitea/master` ist ein **alter, abgehängter Zweig** (Stand 44086b0) — nicht benutzen.
- Sicherungszweig `server-stand-vor-abgleich-22-08-2026` auf dem Server: der Stand, bevor die
beiden Fassungen am 22.08.2026 zusammengeführt wurden. Kann irgendwann weg, kostet nichts.
**Nie direkt auf dem Server Dateien bearbeiten, ohne sie danach in Gitea nachzutragen.** Genau
dadurch waren am 22.08.2026 zwei Reparaturen (Autofokus in `gate.html`, abgeschnittene
Öffnen-Knöpfe in `verwaltung.html`) nur auf dem Server vorhanden und wären beim nächsten
Deploy vom Rechner aus überschrieben worden.
## Offene Punkte
- **Server-Neustart steht aus (seit 28.08.2026).** `unattended-upgrades` hat
Kernel (6.12.105) und OpenSSL (3.5.7) eingespielt, aber es läuft noch der alte
Kernel (6.12.100) und die Dienste haben die alte libssl im Speicher.
`/var/run/reboot-required` steht. Kein Notfall (die OpenSSL-CVEs betreffen
PKCS7/CMS/QUIC-Pfade, die hier kaum aktiv sind — Caddys QUIC läuft über Go),
aber der Neustart gehört auf eine ruhige Zeit gelegt, während Filipe erreichbar
ist (Netcup-Konsole griffbereit). Danach prüfen: laufen alle sechs Dienste?
- **Server-internal-Deploy ausstehend (28.08.2026):** In Gitea liegen fertige,
getestete Änderungen an `server-internal/`, die noch nicht live sind
(Anfragebremse für `/submit` + `/testimonials/submit`; `package-lock.json` auf
multer 2.2.0 / node-cron 4.6.0). Ausrollen mit dem **vollen** Deploy-Block oben
(der mit `npm ci`), weil sich die Paketstände geändert haben. Getestet:
multer 2.2.0 fängt den DoS-Fall sauber ab, node-cron 4.x akzeptiert den
bestehenden Aufruf, alle Selbsttests grün.
- ~~Hintergrundbilder liegen nur auf dem Server~~ — **erledigt, nachgeprüft am
26.08.2026.** Alle genannten Dateien (`bg-bewerben-modi.jpg`, `bg-links-seite.jpg`,
`bg-medien-casper.jpg`, `bg-supporter.jpg`, `bg-modis.jpg`) und der Ordner
`assets/img/stimmen-avatare/` (8 Dateien) sind inzwischen versioniert.
`git status --untracked-files=all -- assets/` auf dem Server meldet **null**
unverfolgte Dateien.
Der Eintrag bleibt hier stehen, statt gelöscht zu werden: Eine Liste offener
Punkte, in der Erledigtes ungekennzeichnet steht, wird beim nächsten Mal gar
nicht mehr gelesen. Wer prüft, will sehen, dass geprüft wurde.
- **CORS steht auf `*`** — in `server-internal/index.js` (`app.use(cors())`) und im
Altbestand `cloudflare-worker/src/lib/http.js`.
**Eingeordnet am 26.08.2026, weniger dringend als der alte Hinweis klang:**
Die Antwort enthält *kein* `Access-Control-Allow-Credentials`, und die
Verwaltung weist sich über `Authorization: Bearer …` aus, nicht über ein
Cookie. Ein Browser schickt bei einer fremden Seite deshalb weder Cookies noch
das Token mit — eine fremde Seite erreicht damit nur die ohnehin öffentlichen
Endpunkte (Team, Events, Stimmen). An Kundendaten kommt sie nicht.
Sauberer wäre es trotzdem: eine feste Liste erlaubter Herkünfte
(`dogfather-universe.com`, `vans-diy-bastelbedarf.com`) statt `*`. Das ist
Härtung, keine Reparatur — und ein Eingriff in einen laufenden Dienst, der bei
zu enger Einstellung die Seite lahmlegt. Also als eigener Vorgang, mit
Live-Test danach.
- **express 5 vorbereitet, nicht umgestellt.** `pruef-express5.mjs` (Bestand
durchsuchen) und `server/test-express5.mjs` (echter Server gegen 5.2.1) sind
grün — der Umstieg wäre ohne Codeänderung möglich. Nicht durchgeführt, weil
express 4.22.2 gepflegt wird und `npm audit` null meldet. Ebenfalls offen:
dotenv 16→17, better-sqlite3 11→13 (dort muss die ABI zur Node-Fassung
passen; ein Fehlgriff legt den internen Dienst still).
- ~~Datenschutz-Endpunkte warten auf einen Pull~~ — **erledigt, live geprüft am
28.08.2026.** Alle drei (`/webdesign/admin/datenschutz/{auskunft,vorschau,
loeschen}`) antworten auf der echten Domain mit 401 (vorhanden und geschützt),
nicht mehr 404. Steht abgehakt hier, nicht gelöscht — wer prüft, will sehen,
dass geprüft wurde.
## Alternative Hosting-Optionen (nur als Notfall-Plan)
Die Seite ist reines statisches HTML/CSS/JS und läuft überall — `netlify.toml` und `.nojekyll`
liegen bereit. Relevant nur, falls der Netcup-Server einmal ausfällt.
+281
View File
@@ -0,0 +1,281 @@
# PayPal für den Webdesign-Bereich einrichten
Das ist der einzige Schritt, den ich nicht selbst machen kann: PayPal
lässt Zugangsdaten nur über dein eingeloggtes Konto erzeugen.
Alles andere steht bereits — Bestellungen, Einzug, Webhook mit
Signaturprüfung, Schutz gegen Doppelverbuchung, die gesetzlich nötige
Zustimmung vor der Anzahlung. Sobald die vier Werte eingetragen sind,
funktionieren die Bezahlknöpfe.
**Zeitbedarf:** etwa 20 Minuten. **Kosten:** keine — ein
PayPal-Geschäftskonto ist kostenlos, Gebühren fallen nur pro Zahlung an.
---
## Schritt 0 — Zuerst nachsehen, was schon da ist
**Du hast wahrscheinlich schon die Hälfte.** Das DogiCrew-Supporter-Abo
läuft über dasselbe PayPal-Konto und benutzt dieselbe Client ID und
dasselbe Secret. Wenn das Abo live funktioniert, sind sie bereits da.
So siehst du es — ohne Konsole:
1. Verwaltung öffnen: `https://dogfather-universe.com/webdesign/verwaltung.html`
2. Reiter **Zahlungen**
Oben steht der Stand, bei jedem Feld eine Marke:
| Was dort steht | Bedeutung |
|---|---|
| *hinterlegt (auf dem Server)* | kommt aus der `.env`, du musst nichts tun |
| *hinterlegt* | schon über dieses Formular eingetragen |
| *fehlt* | musst du eintragen |
Steht bei **Client ID** und **Secret** schon *hinterlegt*, brauchst du
nur **Schritt 4** (Webhook) und danach Schritt 6.
> Warum das wichtig ist: Wenn du eine **zweite** App anlegst, obwohl
> schon eine läuft, hast du zwei Sätze Zugangsdaten für dasselbe Konto.
> Das funktioniert zwar, macht aber später jede Fehlersuche doppelt so
> mühsam — und beim Erneuern eines Secrets bricht womöglich das
> DogiCrew-Abo weg.
---
## Bevor du anfängst
Du brauchst ein **PayPal-Geschäftskonto** (kein privates). Falls du noch
keins hast: Auf paypal.com anmelden → *Einstellungen* → *Kontoart
ändern* → auf Geschäftskonto umstellen. Das geht mit demselben Konto und
kostet nichts.
> **Wichtig:** Das Geld landet direkt auf **deinem** PayPal-Konto. Es
> geht zu keinem Zeitpunkt über einen Vermittler oder über mich.
---
## Schritt 1 — Bei den Entwickler-Einstellungen anmelden
1. Gehe auf **https://developer.paypal.com**
2. Oben rechts auf **Log in to Dashboard**
3. Melde dich mit deinen ganz normalen PayPal-Zugangsdaten an
(denselben wie auf paypal.com)
Du landest im *Developer Dashboard*.
---
## Schritt 2 — Auf „Live" umschalten
Oben auf der Seite gibt es einen Schalter mit zwei Stellungen:
```
Sandbox | Live
```
**Stelle ihn auf „Live".**
> Sandbox ist die Spielwiese mit Testgeld. Solange der Schalter dort
> steht, erzeugst du Zugangsdaten, mit denen **kein echtes Geld** fliesst.
> Das ist der häufigste Fehler bei dieser Einrichtung.
---
## Schritt 3 — App anlegen
1. Klicke links auf **Apps & Credentials**
2. Klicke auf **Create App**
3. **App Name:** `Dogfather Webdesign`
4. **Type:** `Merchant` (falls gefragt)
5. Klicke **Create App**
Du siehst jetzt zwei Werte:
| Feld | Was du siehst |
|---|---|
| **Client ID** | eine lange Zeichenkette, sofort sichtbar |
| **Secret** | daneben ein Link **Show** — draufklicken |
**Beide brauchst du gleich.** Kopier sie irgendwohin, wo du sie
wiederfindest — am besten direkt nach Bitwarden.
> **Das Secret ist wie ein Passwort.** Schick es mir **nicht** im Chat —
> dort stuende es dauerhaft im Verlauf, und eintragen koennte ich es
> trotzdem nicht. In Schritt 6 fuegst du es selbst ein.
---
## Schritt 4 — Webhook einrichten
Der Webhook ist die Leitung, über die PayPal uns meldet: *„Die Zahlung
ist durch."* Ohne ihn würde eine Zahlung nicht als bezahlt erscheinen,
wenn der Kunde das Fenster zu früh schliesst.
1. Auf derselben App-Seite nach unten scrollen zu **Webhooks**
2. Klicke **Add Webhook**
3. **Webhook URL** — genau das hier eintragen:
```
https://postfach.dogfather-universe.com/webdesign/paypal-webhook
```
4. Bei **Event types** wähle **diese vier** aus:
- [x] `Payment capture completed`
- [x] `Payment capture denied`
- [x] `Payment capture refunded`
- [x] `Billing subscription cancelled`
> Nicht „Select all" anklicken. Jedes zusätzliche Ereignis erzeugt
> Meldungen, die niemand auswertet — das macht die Suche nach einem
> echten Problem später unnötig schwer.
5. **Save**
6. Danach erscheint in der Liste eine **Webhook ID**. Die brauchst du
für Schritt 6.
> **Achtung, hier stand vorher etwas Falsches:** Die Webhook-ID beginnt
> **nicht** mit `WH-`. Sie sieht aus wie `0NH55953DH663215D` — rund
> 17 Zeichen, Buchstaben und Ziffern gemischt, ohne Vorsilbe.
>
> Das `WH-` steht auf den **Ereignis**-Kennungen, also den einzelnen
> Meldungen, die PayPal später schickt (`WH-3F562076HD293871E-…`). Die
> braucht man hier nicht.
---
## Schritt 5 — Nur falls du die monatliche Betreuung anbieten willst
Für einmalige Zahlungen — Anzahlung, Restbetrag, Zusatzleistungen — bist
du hier fertig. **Überspring diesen Schritt, wenn du kein monatliches
Abo verkaufst.** Du kannst ihn jederzeit nachholen.
> **Wichtig:** Abo-Pläne legst du **nicht** im Entwicklerbereich an,
> sondern in deinem normalen PayPal-Geschäftskonto. Deshalb findest du
> sie unter developer.paypal.com nicht.
1. Gehe auf **https://www.paypal.com/billing/plans**
(oder: paypal.com anmelden → Menü **Zahlungen** bzw.
**Bezahlen & bezahlt werden** → **Abonnements**)
2. **Plan erstellen** / *Create Plan*
3. Produkt anlegen: Name `Dogfather Betreuung`, Art **Dienstleistung**
4. Preis und Rhythmus: **monatlich**, dein Betreuungspreis
5. Speichern, dann **Plan aktivieren** (*Turn on plan*) — ohne das
ist er nicht nutzbar
6. Die **Plan-ID** notieren. Sie beginnt mit `P-`
---
## Schritt 6 — Werte in der Verwaltung eintragen
**Kein SSH, kein Texteditor, kein Neustart.**
1. Verwaltung öffnen: `https://dogfather-universe.com/webdesign/verwaltung.html`
2. Reiter **Zahlungen**
3. Die Felder ausfüllen:
| Feld | Woher |
|---|---|
| **Betriebsart** | *Echtbetrieb* anklicken |
| **Client ID** | aus Schritt 4 |
| **Secret** | aus Schritt 4 |
| **Webhook-Kennung** | aus Schritt 5 |
| **Plan für die Betreuung** | aus Schritt 5b — sonst leer lassen |
4. **Speichern**
> **Ein leeres Feld bedeutet „nicht anfassen", nicht „löschen".** Du
> kannst also einzeln nachtragen, ohne den Rest zu verlieren. Steht bei
> einem Feld schon *„hinterlegt (auf dem Server)"*, kommt der Wert aus
> der `.env` und du brauchst dort nichts einzugeben.
Die Werte werden verschlüsselt gespeichert und wirken sofort. Nach dem
Speichern leeren sich die Felder von selbst — ein Secret soll nicht
stehen bleiben, wenn jemand anders auf den Bildschirm schaut.
**Angezeigt wird ein gespeicherter Wert nie wieder.** Nur: *hinterlegt ·
80 Zeichen*. Wer ihn verliert, erzeugt bei PayPal einen neuen — das ist
sicherer, als ihn dauerhaft abrufbar zu halten.
---
## Schritt 7 — Verbindung testen
Gleich daneben steht der Knopf **Verbindung testen**. Draufklicken.
| Meldung | Bedeutung |
|---|---|
| „Anmeldung bei PayPal erfolgreich — Echtbetrieb." | Alles richtig, weiter zu Schritt 8 |
| „…aber im TESTMODUS" | Betriebsart auf *Echtbetrieb* stellen |
| „PayPal hat die Anmeldung abgelehnt" | Client ID und Secret stammen aus verschiedenen Apps oder aus dem Testbereich — beide aus **derselben** App neu kopieren |
> Der Test meldet sich probeweise bei PayPal an. **Es fließt kein Geld.**
> Aber erst er beweist, dass die Werte stimmen — *„hinterlegt"* heißt
> nur, dass etwas dasteht, nicht dass es richtig ist.
---
## Schritt 8 — Der erste echte Test mit 1 Euro
**Das ist der wichtigste Schritt.** Mach ihn, bevor ein echter Kunde
zahlt.
1. Verwaltung → Reiter **Kunden** → Testkunde anlegen (deine eigene
E-Mail genügt)
2. Dazu ein Projekt mit Preis **1,00 €**
3. Im Projekt eine Zahlung erzeugen
4. Den Einladungslink im eigenen Browser öffnen, Passwort setzen,
ins Portal gehen und bezahlen — mit VanVans PayPal oder per Karte
als Gast
5. Prüfen: Kommt das Geld auf deinem Konto an? Steht die Zahlung in der
Verwaltung auf **„bezahlt"**?
6. In PayPal wieder **erstatten**
7. Testkunde und Testprojekt löschen
> Der Testmodus verhält sich in Kleinigkeiten anders als der Echtbetrieb.
> Ein Durchlauf mit einem echten Euro ist der einzige Nachweis, der
> wirklich zählt — und er kostet dich nichts, weil du ihn zurückholst.
---
## Was passiert, wenn ein Kunde zahlt
1. Kunde klickt im Portal auf **Jetzt bezahlen**
2. Bei der **Anzahlung** muss er zuerst bestätigen, dass sofort begonnen
werden darf und er dadurch sein Widerrufsrecht verliert
(§ 356 Abs. 4 BGB — der genaue Wortlaut wird mit Zeitstempel
gespeichert, weil im Streit du beweisen musst, dass er zugestimmt hat)
3. Er wird zu PayPal geleitet und bezahlt
4. **Das Geld geht sofort auf dein Konto** — es gibt keine Zwischenstufe
5. Die Zahlung erscheint als „bezahlt", das Projekt vermerkt Anzahlung
bzw. Restbetrag als eingegangen
Schliesst der Kunde das Fenster zu früh, meldet der Webhook die Zahlung
trotzdem. Kommt dieselbe Meldung doppelt — was bei PayPal normal ist —
wird sie nur einmal verbucht.
---
## Wenn etwas nicht klappt
| Anzeichen | Ursache | Abhilfe |
|---|---|---|
| „noch nicht freigeschaltet" | Werte fehlen | Verwaltung → Zahlungen, dort steht welcher |
| Zahlung bleibt auf „freigegeben" | Webhook-Adresse falsch | Schritt 4 prüfen — die Adresse muss exakt stimmen |
| Kein echtes Geld | Schalter stand auf Sandbox | Schritt 2, dann neue Zugangsdaten erzeugen |
| PayPal meldet „invalid client" | Client ID und Secret aus verschiedenen Apps | beide aus derselben App neu kopieren |
---
## Zwei Dinge, die dauerhaft gelten
**Das Secret ist ein Passwort.** Es gehört nach Bitwarden, nicht in eine
Notiz, nicht in eine Nachricht, nicht ins Git. Wenn es je irgendwo
auftaucht, wo es nicht hingehört: in der App **Manage Credentials → new
secret** erzeugen und das alte löschen.
**Prüfe nach dem Umstellen auf Live einmal deinen Kontoauszug.** Nicht
weil zu erwarten wäre, dass etwas schiefgeht — sondern weil der Moment,
in dem zum ersten Mal echtes Geld fliessen kann, der richtige ist, um
einmal genau hinzusehen.
+206
View File
@@ -0,0 +1,206 @@
# Wiederherstellung des Creator Workspace
Für den Tag, an dem etwas kaputt ist. Geschrieben, damit sie auch dann
funktioniert, wenn man aufgeregt ist und keine Zeit zum Nachdenken hat.
**Der wichtigste Satz zuerst:** Die Datei `workspace.db` allein ist **nicht** die
Datenbank. Im WAL-Betrieb stehen die neuesten Änderungen in `workspace.db-wal`.
Wer nur die `.db` kopiert, kopiert unter Umständen **nichts** — genau das war am
31.08.2026 der Fall (Datenbank 4 KB, WAL 2,1 MB).
Deshalb: **Nie Dateien kopieren. Immer die fertigen Sicherungen benutzen.**
---
## Wo alles liegt
| Was | Wo |
|---|---|
| Datenbank | `/home/dogiweb/workspace-daten/workspace.db` |
| Hochgeladene Dateien | `/home/dogiweb/workspace-daten/dateien/` |
| Wissens-Bibliothek (PDFs) | `/home/dogiweb/workspace-daten/wissen/` |
| Sicherungen (Server) | `/home/dogiweb/workspace-daten/sicherungen/` |
| Sicherungen (dieser PC) | `~/Documents/Obelix/Sicherungen/workspace/` |
Aufgehoben werden **7 tägliche, 4 wöchentliche, 6 monatliche**. Geschrieben wird
jede Nacht ab 3 Uhr, geprüft direkt danach.
---
## Fall 0: „Ich komme nicht mehr rein"
Der häufigste Notfall — und der einzige, bei dem nichts kaputt ist. Passiert
am 02.09.2026: neuen Code für sich selbst erzeugt, im selben Moment abgemeldet,
Code weg, bevor er abgeschrieben war.
**Der alte Code ist nicht wiederherstellbar.** In der Datenbank steht nur seine
Prüfsumme, nirgends der Klartext — auch nicht in den Sicherungen. Es gibt nur
den Weg nach vorn: einen neuen setzen.
Auf dem Server, angemeldet als `dogi`, **ein Block**:
```
sudo -u dogiweb bash /home/dogiweb/dogfather-universe/tools/notfall-code.sh
```
Für jemand anderen den Namen anhängen, z. B.
`... notfall-code.sh "Cigdem"`. Ohne Namen nimmt es DogFather. Ein falscher
Name schadet nichts — das Skript listet dann auf, wen es gibt.
Das Skript setzt einen neuen Code, zeigt ihn an und löscht die Sperre nach
acht Fehlversuchen gleich mit (die hätte sonst 10 Minuten gekostet).
**Das `-u dogiweb` muss dranbleiben.** Als `root` ausgeführt gehören die
Hilfsdateien der Datenbank danach `root`, und der Dienst kann nicht mehr
schreiben — dann ist wirklich etwas kaputt.
Danach: Code **sofort nach Bitwarden**, nicht in eine Notiz, nicht in einen Chat.
**Warum es keinen fest hinterlegten Notfall-Code in der Anwendung gibt:** Der
wäre eine Hintertür, die dauerhaft offensteht — jeden Tag, für jeden, der sie
findet, ohne dass es auffällt. Dieser Weg verlangt Zugang zum Server,
hinterlässt einen Protokolleintrag, und es gibt nichts zu erraten.
**Seit dem 02.09.2026 sollte er kaum noch nötig sein:** Wer seinen eigenen Code
erneuert, bleibt in dieser Sitzung angemeldet (alle anderen Geräte fliegen
weiterhin hinaus), und der Kasten mit dem Code verschwindet nicht mehr von
selbst. Festgehalten in `server/pruef-code.mjs`.
---
## Fall 1: „Ich habe aus Versehen etwas gelöscht"
Nichts überstürzen. Die Sicherung von heute Nacht hat es noch.
**Erst nachsehen, ob es sich lohnt** — ohne irgendetwas anzufassen:
```
ssh dogfather-server "cd /home/dogiweb/workspace-daten/sicherungen && ls -la && for f in *.db; do echo \"--- \$f\"; sqlite3 \$f 'SELECT COUNT(*) FROM personen; SELECT COUNT(*) FROM aufgaben;'; done"
```
Dann weiter mit Fall 2.
---
## Fall 2: Datenbank zurückspielen
**Ein Block, von oben nach unten.** Er stoppt den Dienst, legt den kaputten Stand
zur Seite (nicht löschen — vielleicht braucht man ihn noch), spielt die Sicherung
ein, prüft sie und startet erst dann wieder.
`DATUM` vorher auf die gewünschte Sicherung ändern.
```
ssh dogfather-server 'DATUM=2026-08-31; S=/home/dogiweb/workspace-daten; sudo systemctl stop dogiweb.service && sleep 2 && mv $S/workspace.db $S/kaputt-$(date +%Y%m%d%H%M).db 2>/dev/null; rm -f $S/workspace.db-wal $S/workspace.db-shm; cp $S/sicherungen/taeglich-$DATUM.db $S/workspace.db && chown dogiweb:dogiweb $S/workspace.db && sqlite3 $S/workspace.db "PRAGMA integrity_check; PRAGMA foreign_key_check; SELECT COUNT(*) || \" Personen\" FROM personen;" && sudo systemctl start dogiweb.service && sleep 3 && systemctl is-active dogiweb.service'
```
Danach **auf der echten Domain nachsehen**, nicht nur auf die Erfolgsmeldung
vertrauen:
```
curl -s -o /dev/null -w "%{http_code}\n" https://dogfather-universe.com/workspace/ && curl -sI https://dogfather-universe.com/ | grep -i via
```
**Wichtig:** `mv` statt `rm`. Die kaputte Datei bleibt als `kaputt-…db` liegen.
Wenn sich herausstellt, dass die Sicherung älter war als gedacht, kann man
daraus noch Einzelnes herausholen. Später von Hand aufräumen.
---
## Fall 3: Der Server ist ganz weg
Dann liegt die letzte Sicherung auf diesem PC. Zuerst holen, was noch geht —
falls der Server noch antwortet:
```
bash ~/Documents/Obelix/DogiHompage/tools/sicherung-holen.sh
```
Wiederaufbau in dieser Reihenfolge:
1. Server neu aufsetzen, Benutzer `dogiweb` anlegen (**macht Filipe**, nicht ich —
siehe `CLAUDE.md`)
2. `git clone` von Gitea nach `/home/dogiweb/dogfather-universe`
3. Ordner `/home/dogiweb/workspace-daten/` anlegen
4. Neueste Sicherung von diesem PC hochladen:
```
scp ~/Documents/Obelix/Sicherungen/workspace/taeglich-*.db dogfather-server:/home/dogiweb/workspace-daten/workspace.db
```
5. `dogiweb.service` und Caddy einrichten, Dienst starten
6. Auf `https://dogfather-universe.com/workspace/` anmelden und prüfen
7. Hochgeladene Dateien und Bibliothek zurückspielen — die stecken **nicht** in
der Datenbanksicherung, sondern liegen als Dateien daneben:
```
scp -r ~/Documents/Obelix/Sicherungen/workspace/dateien/. dogfather-server:/home/dogiweb/workspace-daten/dateien/ && scp -r ~/Documents/Obelix/Sicherungen/workspace/wissen/. dogfather-server:/home/dogiweb/workspace-daten/wissen/
```
---
## Sicherungen auf diesen PC holen
**Das läuft von allein** — täglich um 13:30 über die Windows-Aufgabenplanung
(„Dogfather Workspace - Sicherung holen"). War der Rechner zu der Zeit aus, holt
Windows den Lauf beim nächsten Hochfahren nach.
Eingerichtet wurde das einmalig mit:
```
powershell -ExecutionPolicy Bypass -File ~/Documents/Obelix/DogiHompage/tools/sicherung-einrichten.ps1
```
Von Hand anstoßen geht trotzdem jederzeit:
```
bash ~/Documents/Obelix/DogiHompage/tools/sicherung-holen.sh
```
Holt **drei** Dinge: die Datenbanksicherungen, die hochgeladenen Dateien und die
PDFs der Bibliothek. Prüft danach jede Datenbankdatei sofort auf Unversehrtheit und
darauf, dass wirklich Personen darin stehen — und sagt es, wenn etwas nicht stimmt.
**Warum die Dateien nur hier landen und nicht auch auf dem Server:** Eine zweite
Kopie auf derselben Platte hilft gegen nichts, was die erste nicht schon überlebt
hätte. Und weil `scp` nie löscht, bleibt ein hier einmal geholtes PDF erhalten,
auch wenn es auf dem Server verschwindet.
---
## Von Hand sichern, bevor man etwas Größeres anfasst
Im Workspace unter **Automationen → Sicherung & Zustand → „Jetzt sichern"**.
Dauert Millisekunden. Oder über die Kommandozeile:
```
ssh dogfather-server "sudo systemctl status dogiweb.service --no-pager | head -3"
```
---
## Was noch fehlt
- **Nur ein Ort.** Server und dieser PC stehen beide bei uns. Gegen Feuer oder
Diebstahl hilft das nicht. Eine verschlüsselte Kopie an einem dritten Ort wäre
die Ausbaustufe — kostenlos möglich, aber eine eigene Entscheidung.
---
## Woran man sieht, dass es noch läuft
Im Workspace unter **Automationen → Sicherung & Zustand**. Dort steht in der Zeile
**„Kopie außer Haus"**, wann dieser Rechner zuletzt geholt hat.
Kommt zehn Tage lang nichts, wird der Kasten gelb und nennt den Grund. Das ist der
eigentliche Zweck der Rückmeldung: Eine Sicherung, die still aufhört zu laufen, ist
gefährlicher als gar keine — weil man sich auf sie verlässt.
Prüfen, ob die Aufgabe eingerichtet ist:
```
powershell -Command "Get-ScheduledTaskInfo -TaskName 'Dogfather Workspace - Sicherung holen' | Select-Object LastRunTime, LastTaskResult, NextRunTime"
```
`LastTaskResult` muss **0** sein. Jeder Lauf steht außerdem in
`~/Documents/Obelix/Sicherungen/workspace/lauf-protokoll.txt` — auch die
gescheiterten. Ohne das wäre hinterher nicht zu klären, ob die Aufgabe gar nicht
lief oder ob sie lief und scheiterte. Das sind zwei sehr verschiedene Fehler.
+299 -42
View File
@@ -5,9 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DogiCrew 🩵 — DOGFATHER UNIVERSE</title>
<meta name="description" content="Unterstütze Dogfather dauerhaft mit der DogiCrew 🩵 und sammle exklusive Treueprämien — die Teddy-Kollektion entsteht in Kooperation mit VanVan." />
<link rel="icon" type="image/png" href="assets/img/favicon.png" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/universe-32.png" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/universe-180.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#0b0d10" />
<meta name="theme-color" content="#065f76" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="DOGFATHER UNIVERSE" />
<meta property="og:title" content="DogiCrew 🩵 — DOGFATHER UNIVERSE" />
@@ -19,10 +20,10 @@
<meta name="twitter:description" content="Unterstütze Dogfather dauerhaft mit der DogiCrew 🩵 und sammle exklusive Treueprämien — die Teddy-Kollektion entsteht in Kooperation mit VanVan." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
<link rel="stylesheet" href="assets/css/main.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260828e" />
<style>
/* =====================================================================
Preis-/Anmelde-Kiste — komplett neu gestaltet (Nutzer-Feedback
@@ -216,6 +217,104 @@
.abo-switch a { color: var(--accent); cursor: pointer; font-weight: 600; }
.abo-switch a:hover { text-decoration: underline; }
/* Registrieren/Einloggen-Umschalter (21.08.2026, Nutzer-Wunsch: "die
sollen dann sofort immer den anmelde knopf auswählen können") — zwei
Segmente in einer Pille, aktiver Tab in Gold hervorgehoben, damit die
Wahl sofort ins Auge fällt, noch bevor überhaupt ein Formularfeld zu
sehen ist. Augenschonend: kein grelles Weiß, ruhiger Farbwechsel. */
/* =====================================================================
Aktionsleiste (21.08.2026) — EIN Element für beide Wege, statt vorher
doppeltem "Registrieren" (Umschalter oben + Knopf unten). Links die
eigentliche Aktion in Gold (unverändert im bestehenden Marken-Look),
rechts der Wechsel in Babyblau — der zweiten Hausfarbe der Seite
(theme-dogfather.css). Beide Hälften nutzen dieselbe Technik wie die
bestehenden Gold-Knöpfe (wandernder Verlauf per background-position +
ein durchlaufender Glanzstreifen als ::after) — dadurch wirken sie wie
ein zusammengehöriges Paar statt wie zwei fremde Knöpfe.
Augenschonend (Projektregel): kein Blinken/Flackern, nur ruhig
wandernde Verläufe, und bei prefers-reduced-motion steht alles still.
===================================================================== */
.abo-aktionsleiste {
display: flex; gap: .6rem; margin-top: .4rem;
}
/* flex-basis "auto" statt "0": jede Hälfte startet bei ihrer eigenen
Textbreite und wächst dann anteilig mit. Dadurch bekommt automatisch
die Seite mit dem längeren Text mehr Platz — egal ob das gerade links
("Login-Code anfordern") oder rechts ("Schon registriert? Einloggen")
ist. Mit fester 0-Basis wären beide exakt gleich breit gewesen und der
jeweils längere Text wäre auf zwei Zeilen umgebrochen. */
.abo-aktionsleiste > button {
position: relative; isolation: isolate; overflow: hidden;
flex: 1 1 auto; min-width: 0;
display: flex; align-items: center; justify-content: center; text-align: center;
padding: 1rem .95rem; border-radius: 14px;
font-weight: 800; font-size: .92rem; line-height: 1.25;
white-space: nowrap;
cursor: pointer;
transition: transform .2s ease, box-shadow .2s ease;
}
.abo-aktionsleiste > button::after {
content: ""; position: absolute; inset: 0; z-index: 1;
background: linear-gradient(100deg, transparent 30%, rgba(255,255,255,.85) 48%, transparent 66%);
background-size: 240% 100%;
mix-blend-mode: overlay;
animation: abo-btn-glare 3.2s ease-in-out infinite;
pointer-events: none;
}
.abo-aktionsleiste > button:hover { transform: translateY(-3px) scale(1.015); }
/* Links: Gold — exakt die bestehende Knopf-Optik der Seite. */
.abo-aktion-haupt {
color: #1a1408;
border: 1.5px solid rgba(255,233,168,.65);
background: linear-gradient(120deg, #ffe9a8 0%, #d4af37 30%, #ffe9a8 55%, #fff2c9 78%, #d4af37 100%);
background-size: 280% 100%;
animation: abo-btn-shine 6s ease-in-out infinite;
box-shadow: 0 10px 26px -12px rgba(212,175,55,.55);
}
.abo-aktion-haupt:hover {
box-shadow: 0 16px 34px -10px rgba(212,175,55,.65), 0 0 26px rgba(212,175,55,.4);
}
/* Babyblau — gleiche Machart, kräftigeres Sky-Blau statt des sehr
blassen Standard-Babyblaus, damit es neben dem Gold nicht untergeht
(dieselbe Lektion wie auf der Verwaltungsseite). Dunkler Tiefsee-Text
darauf für klaren Kontrast; zusätzlich ein sanft atmender Ring, der die
Kachel leicht leuchten lässt, ohne zu blenden.
Zwei Namen, eine Optik: ".abo-aktion-neben" ist der Wechsel-Knopf neben
einer Gold-Aktion (Registrierungs-Seite), ".abo-aktion-blau" ist ein
Knopf, der selbst DIE Hauptaktion ist und trotzdem blau sein soll
(Einloggen-Seite, Nutzer-Wunsch 21.08.2026). Bewusst getrennte Namen,
damit später klar bleibt, warum ein Knopf blau ist. */
.abo-aktion-neben,
.abo-aktion-blau {
color: #05202b;
border: 1.5px solid rgba(224,247,254,.7);
background: linear-gradient(120deg, #e0f7fe 0%, #38bdf8 30%, #7dd3fc 55%, #f0fbff 78%, #38bdf8 100%);
background-size: 280% 100%;
animation: abo-btn-shine 6s ease-in-out infinite, abo-blau-atem 4.5s ease-in-out infinite;
box-shadow: 0 10px 26px -12px rgba(56,189,248,.6), 0 0 0 0 rgba(125,211,252,.5);
}
.abo-aktion-neben:hover,
.abo-aktion-blau:hover {
box-shadow: 0 16px 34px -10px rgba(56,189,248,.75), 0 0 30px rgba(125,211,252,.55);
}
@keyframes abo-blau-atem {
0%, 100% { box-shadow: 0 10px 26px -12px rgba(56,189,248,.55), 0 0 14px rgba(125,211,252,.25); }
50% { box-shadow: 0 12px 30px -12px rgba(56,189,248,.75), 0 0 26px rgba(125,211,252,.5); }
}
@media (prefers-reduced-motion: reduce) {
.abo-aktionsleiste > button,
.abo-aktionsleiste > button::after { animation: none; }
}
/* Auf schmalen Handys untereinander statt gequetscht nebeneinander. */
@media (max-width: 430px) {
.abo-aktionsleiste { flex-direction: column; gap: .55rem; }
.abo-aktionsleiste > button { font-size: .92rem; }
}
.abo-oder {
display: flex; align-items: center; gap: .8rem;
color: var(--text-muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .08em;
@@ -265,7 +364,12 @@
<div class="abo-logo-glut" aria-hidden="true"></div>
<div class="abo-logo-wz" aria-hidden="true"></div>
<span class="eyebrow" data-i18n="ab_preis_eyebrow">DogiCrew 🩵</span>
<div class="abo-preis-zahl">4,99 € <small data-i18n="ab_preis_pro_monat">/ Monat</small></div>
<!-- Bugfix/Nutzer-Wunsch 20.08.2026: Preis noch NICHT zeigen, solange die
Registrierung gesperrt ist (siehe .btn-gold-locked weiter unten) — sonst
wäre der Preis schon öffentlich, bevor überhaupt jemand abschließen kann.
Gleicher Chrome-Gold-Schimmer-Text wie beim gesperrten Knopf
(.btn-gold-locked-text), hier freistehend statt in einem Knopf. -->
<div class="abo-preis-zahl"><span class="btn-gold-locked-text" style="font-size:1em;" data-i18n="ab_kollektion_coming_soon">Coming soon…</span></div>
<p class="small muted" style="margin-top:-.6rem;margin-bottom:1rem;">Keine gesondert ausgewiesene MwSt. gemäß Kleinunternehmerregelung (Art. 57 des luxemburgischen TVA-Gesetzes) — keine weiteren Kosten.</p>
<ul class="abo-bedingungen">
<li data-i18n="ab_bed1">Monatliche automatische Zahlung über PayPal</li>
@@ -308,6 +412,12 @@
<!-- Zustand 2: Registrierung -->
<div class="abo-panel" id="panel-register" hidden>
<!-- Bugfix/Nutzer-Wunsch 20.08.2026 (Nachpruefung): Nur der Knopf war
gesperrt, die drei Felder ließen sich trotzdem antippen und
beschreiben ("ich kann immer nur was rein tippen, soll garnicht
möglich sein"). Jetzt wie das E-Mail-Feld im Login-Panel: disabled
statt nur readonly, damit auch Tastatur-/Screenreader-Nutzung
korrekt "nicht bedienbar" meldet. -->
<div class="abo-field">
<label for="reg-name" data-i18n="ab_feld_name">Name oder Anzeigename</label>
<input type="text" id="reg-name" maxlength="60" />
@@ -320,23 +430,68 @@
<label for="reg-email" data-i18n="ab_feld_email">E-Mail-Adresse</label>
<input type="email" id="reg-email" maxlength="120" />
</div>
<button type="button" class="btn btn-primary" style="width:100%;" id="btn-register" data-i18n="ab_btn_register">Registrieren</button>
<!-- Das Zugangscode-Feld stand bis 21.08.2026 hier, direkt neben den
Stammdaten. Es ist bewusst in einen eigenen, SPÄTEREN Schritt
gewandert (Nutzer-Wunsch: "bei der ersten registrierung sollen
die eine email bekommen mit einem einmaligen code damit wir auch
wissen dass email stimmt und danach wenn sie den code eingegeben
haben sollen die erst ihren zugangscode auswählen/eintippen
können"). Ablauf jetzt: hier Stammdaten -> Einmalcode per Mail
(#panel-code) -> Zugangscode festlegen (#panel-neuer-code). -->
<p class="small muted" style="margin:-.2rem 0 .2rem;" data-i18n="ab_reg_hinweis_bestaetigung">Wir schicken dir gleich einen 6-stelligen Code per E-Mail. Danach legst du deinen eigenen Zugangscode fest.</p>
<!-- Nutzer-Wunsch 21.08.2026: "die leute sollen sich auch schon
registrieren können, aber der abo soll noch zu sein, da soll
dann coming soon stehen." Registrierung/Login sind ab jetzt
offen -- Konten können angelegt werden. Gesperrt bleibt NUR
noch der eigentliche Zahlungsschritt danach (siehe
panel-subscribe weiter unten), weil ohne Dogis echte PayPal-
Business-Zugangsdaten niemand wirklich zahlen kann. Wieder
normaler .btn-primary-Stil statt .btn-gold-locked. -->
<!-- Aktionsleiste (21.08.2026, Nutzer-Wunsch: "bringt nichts 2 mal
registrieren da zu haben"). EIN Element statt vorher Umschalter
oben + Knopf unten: links die eigentliche Aktion in Gold,
rechts der Wechsel zum Einloggen in Babyblau. Beide Hälften
sind immer aktiv (kein Tab-Zustand) — links macht etwas,
rechts wechselt die Ansicht. -->
<div class="abo-aktionsleiste">
<button type="button" class="abo-aktion-haupt" id="btn-register" data-i18n="ab_btn_register">Registrieren</button>
<button type="button" class="abo-aktion-neben" id="tab-login" data-i18n="ab_tab_login">Schon registriert? Einloggen</button>
</div>
<p class="abo-fehler" id="register-fehler"></p>
<p class="abo-switch"><a id="switch-to-login" data-i18n="ab_switch_login">Schon Supporter? Hier einloggen</a></p>
</div>
<!-- Zustand 3: Login (E-Mail-Code anfordern) -->
<!-- Zustand 3: Einloggen mit E-Mail + eigenem Zugangscode.
Umgestellt 21.08.2026 (Nutzer-Wunsch: "wieso sollte ich wieder
ein code bekommen, mach die anmeldung einfach"): vorher wurde
auch hier bei JEDEM Login erst ein Einmalcode per E-Mail
verschickt — für längst registrierte Leute ein völlig
unnötiger Umweg. Der E-Mail-Weg existiert weiterhin, aber nur
noch als "Code vergessen?" (siehe Link unten). -->
<div class="abo-panel" id="panel-login" hidden>
<div class="abo-field">
<label for="login-email" data-i18n="ab_feld_email">E-Mail-Adresse</label>
<input type="email" id="login-email" maxlength="120" />
<input type="email" id="login-email" maxlength="120" autocomplete="email" />
</div>
<div class="abo-field">
<label for="login-code" data-i18n="ab_feld_code_eigen">Dein Zugangscode</label>
<input type="password" id="login-code" maxlength="200" autocomplete="current-password" />
</div>
<!-- Nutzer-Wunsch 21.08.2026: "da soll nur noch einloggen sein und
der knopf soll auch blau sein, der neu registriert soll weg."
Hier steht deshalb nur EIN Knopf, in Babyblau statt Gold —
wer auf dieser Ansicht gelandet ist, will einloggen; der Weg
zurück zur Registrierung führt über den Zurück-Knopf des
Browsers bzw. den blauen Knopf auf der Registrierungs-Seite. -->
<div class="abo-aktionsleiste">
<button type="button" class="abo-aktion-blau" id="btn-login" data-i18n="ab_btn_login">Einloggen</button>
</div>
<button type="button" class="btn btn-primary" style="width:100%;" id="btn-login-request" data-i18n="ab_btn_login_code">Login-Code anfordern</button>
<p class="abo-fehler" id="login-fehler"></p>
<p class="abo-switch"><a id="switch-to-register" data-i18n="ab_switch_register">Noch kein Konto? Jetzt registrieren</a></p>
<p class="abo-switch"><a id="code-vergessen" data-i18n="ab_code_vergessen">Code vergessen?</a></p>
</div>
<!-- Zustand 4: Code-Eingabe (nach Registrierung ODER Login-Anfrage) -->
<!-- Zustand 4: Einmalcode aus der E-Mail eingeben. Kommt seit
21.08.2026 NUR noch im "Code vergessen?"-Weg vor — die normale
Registrierung/Anmeldung braucht ihn nicht mehr. -->
<div class="abo-panel" id="panel-code" hidden>
<p class="small" id="code-hinweis" data-i18n="ab_code_hinweis">Wir haben dir einen 6-stelligen Code per E-Mail geschickt.</p>
<div class="abo-field">
@@ -347,6 +502,20 @@
<p class="abo-fehler" id="code-fehler"></p>
</div>
<!-- Zustand 4b: neuen dauerhaften Zugangscode festlegen. Letzter
Schritt des "Code vergessen?"-Weges — an dieser Stelle ist man
durch den Einmalcode bereits eingeloggt, es fehlt nur noch der
neue Code für künftige Anmeldungen. -->
<div class="abo-panel" id="panel-neuer-code" hidden>
<p class="small" data-i18n="ab_neuer_code_hinweis">Fast fertig — leg jetzt deinen neuen Zugangscode fest.</p>
<div class="abo-field">
<label for="neuer-code" data-i18n="ab_feld_code_neu">Dein Zugangscode (mindestens 6 Zeichen)</label>
<input type="password" id="neuer-code" maxlength="200" autocomplete="new-password" />
</div>
<button type="button" class="btn btn-primary" style="width:100%;" id="btn-neuer-code" data-i18n="ab_btn_code_speichern">Zugangscode speichern</button>
<p class="abo-fehler" id="neuer-code-fehler"></p>
</div>
<!-- Zustand 5: eingeloggt, noch kein Abo -> PayPal Smart Buttons.
Zeigt automatisch alle für die Käuferin/den Käufer verfügbaren
Zahlarten (PayPal-Konto, Kreditkarte als Gast, je nach Land) —
@@ -354,15 +523,35 @@
<div class="abo-panel" id="panel-subscribe" hidden>
<p class="abo-erfolg zeige" data-i18n="ab_eingeloggt_als">Eingeloggt.</p>
<!-- Button-Lösung (§ 312j BGB / entspr. Regelung): Preis- und
Zahlungspflicht-Hinweis unmittelbar vor der Zahlung. -->
<p class="small" style="text-align:center;font-weight:700;margin-bottom:.8rem;">4,99 € / Monat · zahlungspflichtiges Abo · monatlich kündbar</p>
<!-- Nutzer-Wunsch 21.08.2026: "die leute sollen sich schon registrieren
können, aber der abo soll noch zu sein, da soll dann coming soon
stehen." Konto ist angelegt/Login hat geklappt -- aber die eigent-
liche Zahlung (PayPal) bleibt gesperrt, bis Dogis echte PayPal-
Business-Zugangsdaten stehen. Die echten Zahlungs-Elemente (Preis-
Hinweis, Widerrufs-Checkbox, PayPal-Container) bleiben unten UNVER-
ÄNDERT im DOM (nur per hidden versteckt), damit das Skript weiter
unten (Checkbox-Listener, ladePaypalButtons() usw.) unangetastet
funktioniert -- einfach das hidden am Wrapper unten entfernen,
sobald PayPal bereitsteht. -->
<div class="btn-gold-locked" style="width:100%;margin-top:.4rem;cursor:default;">
<span class="btn-gold-locked-lock" aria-hidden="true">🔒</span>
<span class="btn-gold-locked-text" data-i18n="ab_kollektion_coming_soon">Coming soon…</span>
</div>
<p class="small muted" style="text-align:center;margin-top:.9rem;" data-i18n="ab_subscribe_locked_hinweis">Dein Konto ist bereit — die Bezahlung startet, sobald das Abo offiziell live geht. Schau einfach bald wieder vorbei.</p>
<!-- Widerrufsrecht bei digitalen Inhalten (§ 356 Abs. 5 BGB /
entspr. Regelung, siehe agb.html Ziffer 6): ohne aktives
Ankreuzen bleibt der Zahlungsbereich gesperrt, es gibt keine
Zahlung ohne diese ausdrückliche, nicht vorausgewählte
Zustimmung. -->
<!-- Nutzer-Wunsch 21.08.2026: "ich will dass jetzt alles verbunden
ist so dass man mit den klicks pro klick weiter kommt" — vorher
war hier eine Sackgasse (kein Link/Knopf mehr nach dem Login).
Jetzt zwei klare Wege weiter: zurück zur Startseite, oder
direkt zum Streamplan (naheliegendster nächster Klick für
jemanden, der sich gerade für Dogi interessiert). -->
<div class="btn-row" style="justify-content:center;margin-top:1.4rem;">
<a class="btn btn-outline" href="index.html" data-i18n="ab_weiter_startseite">Zur Startseite →</a>
<a class="btn btn-outline" href="streamplan.html" data-i18n="ab_weiter_streamplan">Zum Streamplan →</a>
</div>
<div hidden>
<p class="small" style="text-align:center;font-weight:700;margin-bottom:.8rem;">4,99 € / Monat · zahlungspflichtiges Abo · monatlich kündbar</p>
<label class="abo-widerruf-check" for="widerruf-consent">
<input type="checkbox" id="widerruf-consent" />
<span>Ich wünsche ausdrücklich, dass der Zugang zum Supporter-Bereich sofort mit Vertragsschluss beginnt, und weiß, dass mein 14-tägiges Widerrufsrecht dadurch erlischt, sobald der Zugang vollständig bereitgestellt ist (Details in den <a href="agb.html" target="_blank" rel="noopener">AGB</a>, Ziffer 6).</span>
@@ -373,6 +562,7 @@
<p class="small" id="paypal-laedt" data-i18n="ab_paypal_laedt" hidden>Zahlungsoptionen werden geladen …</p>
<p class="abo-fehler" id="subscribe-fehler"></p>
</div>
</div>
<!-- Zustand 6: bereits Supporter -->
<div class="abo-panel" id="panel-already" hidden>
@@ -387,7 +577,10 @@
<div class="container grid grid-2">
<div class="card">
<p data-i18n="ab_text1">Dieses Abonnement ist kein gewöhnlicher Shop und kein einfacher Kauf. Es ist für die Menschen gedacht, die Dogfather dauerhaft unterstützen und ein besonderer Teil dieser gemeinsamen Reise sein möchten.</p>
<p data-i18n="ab_text2">Für 4,99 € im Monat erhältst du Zugang zu einem exklusiven Supporter-Bereich, besonderen Inhalten und einer persönlichen Treuekollektion. Mit jedem erfolgreich bezahlten Meilenstein wächst deine Sammlung: vom kleinen Hasen-Teddy über besondere Jubiläumsprämien bis zum exklusiven Hoodie nach 30 Monaten. Danach beginnt für dich eine neue Kollektion — mit neuen Designs, neuen Erinnerungen und derselben Bedeutung.</p>
<!-- Bugfix/Nutzer-Wunsch 20.08.2026: "4,99 €" hier entfernt (nur die Zahl, Rest
des Satzes unverändert) — sonst hätte der eigentlich gesperrte Preis
weiter oben in der Preis-Box hier im Fließtext trotzdem gestanden. -->
<p data-i18n="ab_text2">Mit deinem monatlichen Beitrag erhältst du Zugang zu einem exklusiven Supporter-Bereich, besonderen Inhalten und einer persönlichen Treuekollektion. Mit jedem erfolgreich bezahlten Meilenstein wächst deine Sammlung: vom kleinen Hasen-Teddy über besondere Jubiläumsprämien bis zum exklusiven Hoodie nach 30 Monaten. Danach beginnt für dich eine neue Kollektion — mit neuen Designs, neuen Erinnerungen und derselben Bedeutung.</p>
<ul class="abo-vorteile">
<li data-i18n="ab_vorteil1">Exklusiver Supporter-Bereich</li>
<li data-i18n="ab_vorteil2">Kleiner Hasen-Teddy nach 6 bezahlten Monaten</li>
@@ -403,7 +596,7 @@
<h2 data-i18n="ab_vanvan_h2">Die Teddy-Kollektion — in Kooperation mit VanVan</h2>
<p data-i18n="ab_vanvan_text">Die DogiCrew 🩵 gehört Dogfather. Die Hasen-Teddys und die dazugehörigen Sammlerkollektionen entstehen jedoch in Zusammenarbeit mit VanVan — der rechten Hand von Dogfather, seiner wichtigsten Vertrauensperson und einem festen Bestandteil der Team-Dogi-Familie. Sie stehen für ihre besondere Rolle, den Zusammenhalt innerhalb der Community und die Verbindung zwischen Dogfather, VanVan und allen Menschen, die diesen gemeinsamen Weg dauerhaft unterstützen.</p>
<div style="display:flex;justify-content:flex-end;margin-top:1.4rem;">
<a class="btn-silver" href="#" aria-disabled="true" onclick="return false;"><span class="btn-silver-spark" aria-hidden="true">✨</span><span class="btn-silver-text" data-i18n="ab_kollektion_coming_soon">Coming soon…</span><span class="btn-silver-spark" aria-hidden="true">✨</span></a>
<a class="btn-silver" aria-disabled="true"><span class="btn-silver-spark" aria-hidden="true">✨</span><span class="btn-silver-text" data-i18n="ab_kollektion_coming_soon">Coming soon…</span><span class="btn-silver-spark" aria-hidden="true">✨</span></a>
</div>
</div>
</div>
@@ -411,13 +604,13 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-abonnieren.js"></script>
<script src="assets/js/supporter.js"></script>
<script src="assets/js/main.js"></script>
<script src="assets/js/i18n-abonnieren.js?v=20260828e"></script>
<script src="assets/js/supporter.js?v=20260828e"></script>
<script src="assets/js/main.js?v=20260828e"></script>
<script>
(function () {
const S = window.DogiSupporter;
const panels = ["panel-lade", "panel-register", "panel-login", "panel-code", "panel-subscribe", "panel-already"];
const panels = ["panel-lade", "panel-register", "panel-login", "panel-code", "panel-neuer-code", "panel-subscribe", "panel-already"];
// panel-social-login (Google-Button) hängt sich an register/login dran,
// ist aber kein "richtiger" exklusiver Zustand — separat gesteuert.
const SOCIAL_LOGIN_PANELS = ["panel-register", "panel-login"];
@@ -455,8 +648,14 @@
el.classList.remove("zeige");
}
let codePurpose = "verify_email"; // oder "login"
// Merkt sich die E-Mail-Adresse zwischen "Code vergessen?" und der
// Eingabe des Einmalcodes (der frühere codePurpose-Schalter entfiel mit
// der Umstellung 21.08.2026 — es gibt nur noch diesen einen Code-Weg).
let codeEmail = "";
// Merkt sich, WARUM gerade ein Einmalcode eingegeben wird (21.08.2026):
// "register" = Erstbestätigung der E-Mail, "login" = "Code vergessen?".
// Steuert in btn-code-verify, welcher Endpunkt aufgerufen wird.
let codeZweck = "login";
async function pruefeStatus() {
if (!S.getToken()) {
@@ -468,11 +667,24 @@
const status = data.subscription?.status || "none";
S.cacheSubStatus(status);
S.cacheDisplayName(data.profile?.displayName || "");
if (["active", "pending", "suspended", "cancelled"].includes(status)) {
zeige("panel-already");
} else {
zeige("panel-subscribe");
}
/* Nutzer-Wunsch 21.08.2026: "anstatt das was du jetzt auf dem screen
siehst, sollen die leute nicht mehr sehen wenn sie angemeldet sind.
sie sollen die richtige seite sehen die wir schon mal vorbereitet
hatten, wo sie ihre persönliche daten sehen und so."
Wer eingeloggt ist, gehört nicht mehr auf diese Anmeldeseite —
egal ob mit oder ohne bezahltes Abo. supporter.html zeigt in BEIDEN
Fällen das Richtige: persönliche Daten, Fortschritt mit allen
Prämien-Meilensteinen, Sammlungsarchiv, Profil bearbeiten,
Ausloggen — bei "kein Abo" eben mit entsprechendem Hinweis statt
echter Laufzeit. Vorher landete man hier stattdessen in einer
Sackgasse ("Coming soon…"), ohne je die eigentliche Seite zu sehen.
replace() statt href: die Anmeldeseite verschwindet damit aus dem
Verlauf — ein "Zurück" führt sonst sofort wieder hierher und von
hier wieder weiter, was sich wie eine Schleife anfühlt. */
location.replace("supporter.html");
return;
} catch (err) {
// Token ungültig/abgelaufen -> zurück zur Registrierung/Login
S.clearToken();
@@ -585,8 +797,10 @@
ladeHinweis.hidden = true;
}
document.getElementById("switch-to-login").addEventListener("click", () => zeige("panel-login"));
document.getElementById("switch-to-register").addEventListener("click", () => zeige("panel-register"));
// Der frühere Gegenstück-Knopf "Neu registrieren" auf der Einloggen-Seite
// wurde am 21.08.2026 auf Wunsch entfernt — deshalb hier nur noch der eine
// Wechsel von der Registrierung zum Einloggen.
document.getElementById("tab-login").addEventListener("click", () => zeige("panel-login"));
// Widerrufsrecht-Checkbox: erst nach aktivem Ankreuzen werden die
// PayPal-Zahlungsoptionen überhaupt geladen/klickbar (siehe agb.html
@@ -615,24 +829,44 @@
const email = document.getElementById("reg-email").value.trim();
if (!displayName || !email) return fehlerZeigen(el, "Bitte Name und E-Mail-Adresse angeben.");
try {
// Schritt 1 von 3 (21.08.2026): /supporter/register legt nur das noch
// unbestätigte Konto an und verschickt einen Einmalcode -- es kommt
// bewusst KEIN Token zurück, eingeloggt ist man erst nach Schritt 2.
await S.apiFetch("/supporter/register", { displayName, tiktokUsername, email });
codePurpose = "verify_email";
codeEmail = email;
codeZweck = "register";
zeige("panel-code");
} catch (err) {
fehlerZeigen(el, err.message);
}
});
document.getElementById("btn-login-request").addEventListener("click", async () => {
// Normale Anmeldung: E-Mail + eigener Zugangscode, ohne Umweg über E-Mail.
document.getElementById("btn-login").addEventListener("click", async () => {
const el = document.getElementById("login-fehler");
fehlerVerstecken(el);
const email = document.getElementById("login-email").value.trim();
if (!email) return fehlerZeigen(el, "Bitte E-Mail-Adresse angeben.");
const accessCode = document.getElementById("login-code").value;
if (!email || !accessCode) return fehlerZeigen(el, "Bitte E-Mail-Adresse und Zugangscode eingeben.");
try {
const data = await S.apiFetch("/supporter/login-code", { email, accessCode });
S.setToken(data.token);
await pruefeStatus();
} catch (err) {
fehlerZeigen(el, err.message);
}
});
// "Code vergessen?" — hier lebt der alte E-Mail-Einmalcode-Weg weiter.
document.getElementById("code-vergessen").addEventListener("click", async () => {
const el = document.getElementById("login-fehler");
fehlerVerstecken(el);
const email = document.getElementById("login-email").value.trim();
if (!email) return fehlerZeigen(el, "Bitte zuerst deine E-Mail-Adresse eingeben.");
try {
await S.apiFetch("/supporter/login-request", { email });
codePurpose = "login";
codeEmail = email;
codeZweck = "login";
zeige("panel-code");
} catch (err) {
fehlerZeigen(el, err.message);
@@ -645,9 +879,26 @@
const code = document.getElementById("code-input").value.trim();
if (!code) return fehlerZeigen(el, "Bitte Code eingeben.");
try {
const path = codePurpose === "login" ? "/supporter/verify-login" : "/supporter/verify-email";
const data = await S.apiFetch(path, { email: codeEmail, code });
// Dieselbe Code-Eingabe bedient zwei Wege (21.08.2026):
// "register" -> Erstbestätigung der E-Mail bei der Registrierung
// "login" -> "Code vergessen?"-Weg für bestehende Konten
// Beide enden gleich: eingeloggt, danach Zugangscode festlegen.
const endpunkt = codeZweck === "register" ? "/supporter/verify-email" : "/supporter/verify-login";
const data = await S.apiFetch(endpunkt, { email: codeEmail, code });
S.setToken(data.token);
zeige("panel-neuer-code");
} catch (err) {
fehlerZeigen(el, err.message);
}
});
document.getElementById("btn-neuer-code").addEventListener("click", async () => {
const el = document.getElementById("neuer-code-fehler");
fehlerVerstecken(el);
const accessCode = document.getElementById("neuer-code").value;
if (!accessCode) return fehlerZeigen(el, "Bitte leg einen Zugangscode fest.");
try {
await S.apiFetch("/supporter/set-access-code", { accessCode }, true);
await pruefeStatus();
} catch (err) {
fehlerZeigen(el, err.message);
@@ -662,6 +913,12 @@
} catch {
/* Konfiguration konnte nicht geladen werden — Google-Button/PayPal-Buttons bleiben dann aus. */
}
/* Bugfix 20.08.2026, wieder aktiviert 21.08.2026: "Mit Google anmelden" war ein
dritter, bisher übersehener Weg, trotz gesperrtem Registrieren-/Login-Knopf
doch ein echtes Konto anzulegen bzw. sich einzuloggen. War deshalb deaktiviert,
solange die Registrierung selbst gesperrt war. Registrierung ist jetzt (Nutzer-
Wunsch 21.08.2026) bewusst offen -- Google-Login ist nur ein weiterer,
gleichwertiger Weg zum selben jetzt erlaubten Ziel, kein Schlupfloch mehr. */
initGoogleConsent();
await pruefeStatus();
}
+8 -7
View File
@@ -5,9 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AGB — DogiCrew-Supporter-Abo — DOGFATHER UNIVERSE</title>
<meta name="description" content="Allgemeine Geschäftsbedingungen für das DogiCrew-Supporter-Abo von DOGFATHER UNIVERSE." />
<link rel="icon" type="image/png" href="assets/img/favicon.png" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/universe-32.png" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/universe-180.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#0b0d10" />
<meta name="theme-color" content="#065f76" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="DOGFATHER UNIVERSE" />
<meta property="og:title" content="AGB — DogiCrew-Supporter-Abo — DOGFATHER UNIVERSE" />
@@ -20,10 +21,10 @@
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<meta name="robots" content="noindex" />
<link rel="stylesheet" href="assets/css/main.css" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
<link rel="stylesheet" href="assets/css/main.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260828e" />
</head>
<body>
<div id="site-header"></div>
@@ -134,6 +135,6 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/main.js"></script>
<script src="assets/js/main.js?v=20260828e"></script>
</body>
</html>
+10 -9
View File
@@ -5,9 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Aktion — DOGFATHER UNIVERSE</title>
<meta name="description" content="Detailansicht einer Aktion oder eines Projekts von Team Dogi." />
<link rel="icon" type="image/png" href="assets/img/favicon.png" />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/universe-32.png" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/universe-180.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#0b0d10" />
<meta name="theme-color" content="#065f76" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="DOGFATHER UNIVERSE" />
<meta property="og:title" content="Aktion — DOGFATHER UNIVERSE" />
@@ -19,10 +20,10 @@
<meta name="twitter:description" content="Detailansicht einer Aktion oder eines Projekts von Team Dogi." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
<link rel="stylesheet" href="assets/css/main.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260828e" />
</head>
<body>
<div id="site-header"></div>
@@ -51,9 +52,9 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/data-aktionen.js"></script>
<script src="assets/js/i18n-aktion-detail.js"></script>
<script src="assets/js/main.js"></script>
<script src="assets/js/data-aktionen.js?v=20260828e"></script>
<script src="assets/js/i18n-aktion-detail.js?v=20260828e"></script>
<script src="assets/js/main.js?v=20260828e"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const params = new URLSearchParams(location.search);
+1609 -55
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 KiB

After

Width:  |  Height:  |  Size: 482 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 KiB

After

Width:  |  Height:  |  Size: 482 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 359 KiB

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 KiB

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 KiB

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 KiB

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

After

Width:  |  Height:  |  Size: 576 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

After

Width:  |  Height:  |  Size: 576 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

After

Width:  |  Height:  |  Size: 396 KiB

Some files were not shown because too many files have changed in this diff Show More