Files
dogfather-universe/workspace/assets/js/bereiche.js
T
DogFatherGitandClaude Opus 5 b45de940e8 Rund um das Team -- die Arbeitslage von Managern und Scouts
Filipe: "so eine kategorie wie ueber die creator will ich dass nur fuer
die spicy und dogfather rolle auch ueber manager und scouts gibt ...
ich will dass es so ultra krass gut ist dass die spicy und dogfather
rolle einen kompletten teil haben mit daten ueber die arbeit von den
manager und scout. keine geheimen sachen also termine, chats und
geheime dateien soll auch so bleiben dass keiner."

--- ZUERST DIE KOPFLEISTE ---

Filipe meldete, die Kopfleiste sei bei DogFather "nicht gemacht".
Nachgemessen auf ALLEN 18 Seiten, in allen fuenf Rollen, bei drei
Breiten: einzeilig, Spanne 4 px. Und die neuen Dateien liegen
nachweislich auf dem Server (a70bc4f, `abmelden__zeichen` in der
ausgelieferten kopf.js). Das Bild war vor dem Ausliefern entstanden.

Die Messung hat aber zwei echte Sachen gefunden, die vorher niemand
gesehen hatte -- beide bei 320 px auf UNTERseiten, wo links der
Zurueck-Knopf und rechts zusaetzlich die Glocke steht: 305 px
gebraucht, 294 verfuegbar. Eine Stufe kleiner (34 px je Knopf, 4 px
Abstand) macht 283 und passt; 34 px bleiben weit ueber den 24 px
Mindestmass fuer ein Beruehrziel.

Ausserdem die Auslieferung geordnet: HTML wird immer nachgefragt,
Dateien mit Versionsstempel duerfen ein Jahr liegenbleiben (vorher
bekam ALLES `no-cache`, also auch jede Stilvorlage bei jedem Aufruf).
sw.js ausgenommen -- er wird ohne Stempel geladen, ein Fehler darin
bliebe sonst ein Jahr stehen.

--- DIE NEUE SEITE ---

workspace/team.html, nur fuer `spicy` und `admin`. Aufbau:

  DIE LUECKEN ZUERST. Creator ohne Betreuung, Scouts ohne Manager,
  Leute ohne einen einzigen Creator -- mit NAMEN, nicht nur als Zahl.
  Eine Kennzahl sagt, wie es laeuft; eine Luecke sagt, wo etwas fehlt,
  und nur das Zweite kann man heute abstellen.

  DANN DIE LAGE in fuenf Zahlen, dann JEDE PERSON EINZELN: betreute
  Creator, laufende und ueberfaellige Aufgaben, in 30 Tagen erledigte,
  Durchlaufzeit, Startcheck-Fortschritt der betreuten Creator,
  LIVE-Tage und Diamanten. Bei Scouts zusaetzlich die Pipeline mit
  Uebernahmequote und Zeit bis zur Uebergabe.

  EIN MANAGER TRAEGT DIE CREATOR SEINER SCOUTS MIT. Ohne das saehe
  einer mit fuenf Scouts aus wie jemand ohne Arbeit.

--- DREI ENTSCHEIDUNGEN, DIE ALLES TRAGEN ---

1. TERMINE, CHATS UND DATEIEN KOMMEN NICHT VOR -- weder Inhalte noch
   Zaehlungen. Ausdruecklicher Wunsch, und der richtige: Ein Kalender
   verraet, wann jemand nicht da war; ein Chatzaehler, mit wem jemand
   oft spricht.

   Das ist keine Zusicherung im Kommentar. pruef-team liest den
   Quelltext von workspace-team.js und schlaegt an, wenn eine dieser
   Tabellen darin auftaucht -- mit Gegenprobe, dass die Suche `aufgaben`
   und `leads` auch wirklich findet. Der Weg ueber die Antwort allein
   waere schwaecher: Ein leerer Testbestand kann ein Feld verstecken.

2. SEGMENTIEREN, NICHT MITTELN. Aus der Recherche zu
   Arbeitslast-Dashboards: Ein Durchschnitt versteckt genau die Person,
   bei der es klemmt. Markiert wird gegen den MEDIAN der eigenen Rolle
   -- ein Manager traegt naturgemaess mehr als ein Scout, und ihn daran
   zu messen waere unfair und nutzlos.

3. ES IST EINE ARBEITSLAGE, KEINE UEBERWACHUNG. Das steht so auf der
   Seite, im Kopf, in einem eigenen Kasten. Wer das nicht dazuschreibt,
   baut ein Kontrollwerkzeug, auch wenn er es nicht wollte. Deshalb
   zeigen die Kennzahlen auf ZUSTAENDE (unbetreute Creator,
   liegengebliebene Kontakte) und nicht auf Anwesenheit oder Fleiss.

--- WAS DIE MESSUNG UNTERWEGS GEFUNDEN HAT ---

* Die Lead-Status hiessen anders, als ich angenommen hatte: "kontakt"
  gibt es nicht. Die CHECK-Bedingung der Datenbank hat es sofort
  abgelehnt -- ohne sie waere "offen" still zu klein gewesen.

* Die Pruefung fand ihr eigenes Hinweisschild: Die Antwort traegt ein
  Feld `ausgenommen: ["Termine","Chats","Dateien"]`, aus dem die Seite
  den Satz baut. Es wird jetzt herausgenommen UND eigens geprueft --
  ignorieren waere bequem gewesen und haette kuenftig jedes Feld unter
  diesem Namen durchgelassen.

* Die Lektion vom Vorlagenbrett gleich mitgenommen: alle Karten haben
  einen DECKENDEN Grund. Eine Karte mit sieben Prozent Farbe auf
  durchsichtigem Grund laesst das Buehnenfoto durch -- dort waren es
  3,61:1. Gemessen jetzt: 6,61 bis 14,80:1. Eine der Regeln hatte den
  deckenden Grund selbst wieder aufgehoben (zwei Regeln, die spaetere
  gewinnt) -- gefunden, bevor es jemand sehen musste.

* "1 Scouts" statt "1 Scout". Eine Kleinigkeit, und das Erste, was
  auffaellt: Eine Seite, die ihre eigene Sprache nicht beherrscht, wird
  auch bei den Zahlen nicht geglaubt.

--- Pruefung ---

server/pruef-team.mjs, neu, 51 Pruefungen, alle gruen. Darunter: alle
fuenf Rollen an Schnittstelle UND Seite (Manager, Scout und Creator
bekommen 404 bzw. eine Umleitung), Spicy sieht VanVan nicht (mit
Gegenprobe an der Personenliste), die Zahlen an einem gebauten
Bestand, acht Kontrastmessungen an der wirklichen Flaeche, Handy.

server/pruef-zwischenspeicher.mjs, neu, 15 Pruefungen: was liegenbleiben
darf und was nicht -- an echten Kopfzeilen gemessen, nicht am
Quelltext. Sie meldete zuerst drei Fehler, und das war sie selbst: Sie
fragte unangemeldet und bekam Umleitungen. Eine Pruefung braucht ihre
Voraussetzung, bevor sie misst.

Ausserdem gruen: pruef-handy (das Handy fand die 320-px-Sache),
pruef-workspace-seiten, pruef-alle-wege, pruef-sicht, pruef-css-klassen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 15:42:14 +02:00

997 lines
47 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ===================================================================
Die Bereiche -- EINE Quelle für die ganze Anwendung.
Zeichen, Farbton, Rolle und Ziel jedes Bereichs standen bis zum
01.09.2026 nur in start.js. Seit jede Unterseite Zeichen und Farbe
ihrer Kachel trägt, brauchen sie auch die Kopfleisten -- und eine
zweite, abgeschriebene Liste wäre der sichere Weg, dass "Aufgaben"
irgendwann auf der Startseite gelb und auf der Aufgabenseite grün ist.
Deshalb liegt beides jetzt hier und wird von start.js UND kopf.js
benutzt. Wer eine Kachel ändert, ändert damit automatisch auch den
Kopf der zugehörigen Seite.
=================================================================== */
(() => {
'use strict';
/* ---------- Zeichen ------------------------------------------------------
Alle im selben Raster (24×24). Seit dem 03.09.2026 besteht jedes
Zeichen aus ZWEI Teilen:
fuellung eine gefüllte Grundform -- der Gegenstand selbst,
gedeckt hinterlegt
linien die scharfe Zeichnung darüber
Das ist der Unterschied zwischen einem Symbol und einem Piktogramm.
Eine reine Kontur ist neutral; sobald ein Teil FLÄCHE hat, bekommt
das Zeichen ein Vorn und ein Hinten, und das Auge erkennt es, ohne
es zu lesen. Gefüllt wird immer das, worum es geht: beim Kalender
der Kopf, beim Ordner der Korpus, bei einer Person der Kopf, beim
Regler die Griffe.
WARUM NICHT EINFACH DICKER: Ein früherer Versuch legte eine dicke,
weichgezeichnete Kopie der LINIE darunter. Das hat die feinen
Lücken zugeschmiert -- aus dem Kalender wurde ein leerer Kasten
(siehe start.css). Eine Fläche ist etwas anderes als ein
aufgeblasener Strich: Sie liegt INNERHALB der Kontur und lässt die
Zwischenräume unberührt.
Eigene Pfade statt einer Fremdbibliothek: siebzehn Zeichen sind
kleiner als jede Schriftart und kosten keine Lieferkette. */
/* ---------- Die Silhouetten -------------------------------------------
Der UMRISS jedes Gegenstands als geschlossene Form. Aus ihm entsteht
die Tiefe: Er wird dreimal übereinander gezeichnet, jedes Mal ein
Stück versetzt und dunkler -- unten die Grundfläche, darüber die
Seitenwand, oben die Deckfläche. Das ist dasselbe Verfahren, mit dem
man einen Körper von Hand zeichnet, und es ist der Grund, warum die
Zeichen jetzt eine Höhe haben statt nur einen Umriss.
Getrennt von den Linien gehalten: Die Linien sind die DETAILS auf der
Oberfläche (Querstriche, Haken, Wellen). Sie bekommen keine Tiefe --
ein aufgedruckter Strich steht nicht hervor. */
const KOERPER = {
dashboard: [
'M4 4.5h6.4v6.4H4z',
'M13.6 4.5h6.4v4.2h-6.4z',
'M13.6 11.6h6.4v7.9h-6.4z',
'M4 13.7h6.4v5.8H4z'
],
aufgaben: [
'M4.5 5h4v14h-4z',
'M10 5h4v9h-4z',
'M15.5 5h4v11.5h-4z'
],
kalender: [
'M3.5 7h17v12.5h-17z'
],
calls: [
'M3.5 7h11v10h-11z',
'M14.5 11l6-3.5v9l-6-3.5z'
],
dateien: [
'M3.5 6.5h5.5l2 2h9.5v11h-17z'
],
/* Chat (06.09.2026): eine Sprechblase mit Schweif nach links
unten. Bewusst OHNE die drei Punkte darin -- bei 20 Pixeln
werden die zu Matsch, und die Blase allein ist eindeutig. */
chat: [
'M4 5.5h16v11H9.5L5 20.5V16.5H4z'
],
steckbrief: [
'M3.5 5.5h17v13h-17z'
],
profil: [
'M12 7a3.2 3.2 0 1 0 0 6.4A3.2 3.2 0 0 0 12 7z',
'M5.5 20c.7-3.3 3.3-5.3 6.5-5.3s5.8 2 6.5 5.3z'
],
startcheck: [
'M6 4.5h12v15H6z'
],
live: [
'M12 10.2a1.8 1.8 0 1 0 0 3.6 1.8 1.8 0 0 0 0-3.6z'
],
content: [
'M3.5 6h17v13h-17z'
],
technik: [
'M9 5.9a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2z',
'M15 10.4a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2z',
'M7.5 14.9a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2z'
],
community: [
'M9 8.6a2.7 2.7 0 1 0 0 5.4 2.7 2.7 0 0 0 0-5.4z',
'M3.5 19.5c.6-2.8 2.8-4.5 5.5-4.5s4.9 1.7 5.5 4.5z'
],
schutz: [
'M12 3.6 5.6 6.3v5.1c0 3.8 2.6 7.2 6.4 8.4 3.8-1.2 6.4-4.6 6.4-8.4V6.3z'
],
reports: [
'M6.6 9.5h1.8v7h-1.8z',
'M11.1 5.5h1.8v11h-1.8z',
'M15.6 11.5h1.8v5h-1.8z'
],
personen: [
'M14.4 6.2a4 4 0 1 0 0 8 4 4 0 0 0 0-8z'
],
scouting: [
'M3.5 5.5h17l-6.6 7.6v6.4l-3.8-2.1v-4.3z'
],
wissen: [
'M4 5h6.4c.9 0 1.6.7 1.6 1.6V19c0-.9-.7-1.6-1.6-1.6H4z',
'M20 5h-6.4c-.9 0-1.6.7-1.6 1.6V19c0-.9.7-1.6 1.6-1.6H20z'
],
automation: [
'M13.2 3.2 6 13.4h5.2l-.8 7.4 7.6-10.6h-5.2z'
],
/* Zahlen (06.09.2026): die Flaeche UNTER einem steigenden
Linienzug. Bewusst kein zweites Balkendiagramm -- "Reports &
Review" hat schon eins, und zwei Kacheln mit demselben Zeichen
sind schlimmer als eine ohne: Man klickt zweimal falsch, bevor
man merkt, dass es zwei verschiedene Sachen sind. Eine Linie
ist auch die richtige Aussage: Es geht hier nicht um einen
Bericht, sondern um einen VERLAUF. */
zahlen: [
'M4 16 9.5 11.5 13 14 20 6.5 20 19.5 4 19.5z'
],
/* Agentur (06.09.2026): ein Aktenkoffer. Bewusst kein
Haendedruck und kein Gebaeude -- beides wird bei 20 Pixeln zu
Matsch. Der Koffer ist die einzige Form, die man bei dieser
Groesse noch als "offizielle Seite" liest. */
agentur: [
'M3.5 8h17v11h-17z'
]
};
const ZEICHEN = {
dashboard: {
/* Zwei der vier Felder gefüllt, über Eck -- das gibt dem Zeichen
einen Rhythmus. Alle vier zu füllen wäre ein Klotz. */
fuellung: ['M4 4.5h6.4v6.4H4z', 'M13.6 11.6h6.4v7.9h-6.4z'],
linien: ['M4 4.5h6.4v6.4H4z', 'M13.6 4.5h6.4v4.2h-6.4z',
'M13.6 11.6h6.4v7.9h-6.4z', 'M4 13.7h6.4v5.8H4z'],
},
aufgaben: {
/* Die mittlere Spalte -- "in Arbeit". Das Brett hat drei Spalten,
und die mittlere ist die, in der gerade etwas passiert. */
fuellung: ['M10 5h4v9h-4z'],
linien: ['M4.5 5h4v14h-4z', 'M10 5h4v9h-4z', 'M15.5 5h4v11.5h-4z'],
},
kalender: {
/* Der Kopf des Blattes. Genau der Streifen, an dem man einen
Kalender auch aus drei Metern erkennt. */
fuellung: ['M3.5 7h17v4.5h-17z'],
linien: ['M3.5 7h17v12.5h-17z', 'M3.5 11.5h17', 'M7.5 4v4.6', 'M16.5 4v4.6'],
},
calls: {
/* Das Objektiv. Der Kasten bleibt offen, sonst wird das Zeichen
schwer. */
fuellung: ['M14.5 11l6-3.5v9l-6-3.5z'],
linien: ['M3.5 7h11v10h-11z', 'M14.5 11l6-3.5v9l-6-3.5z'],
},
dateien: {
/* Der Korpus ohne die Lasche -- so bleibt die Stufe oben sichtbar,
die den Ordner ausmacht. */
fuellung: ['M3.5 10h17v9.5h-17z'],
linien: ['M3.5 6.5h5.5l2 2h9.5v11h-17z', 'M3.5 10h17'],
},
steckbrief: {
/* Kopf und Schultern der Person IN der Karte -- unterscheidbar vom
Profil-Zeichen (nur die Person), damit die beiden Kacheln nicht
gleich aussehen. */
fuellung: ['M8.5 11.2a1.9 1.9 0 1 0 0-3.8 1.9 1.9 0 0 0 0 3.8z'],
linien: ['M3.5 5.5h17v13h-17z', 'M8.5 11.2a1.9 1.9 0 1 0 0-3.8 1.9 1.9 0 0 0 0 3.8z',
'M5.6 16.2c0-1.7 1.3-3 2.9-3s2.9 1.3 2.9 3', 'M14.5 9.5h4', 'M14.5 13h4'],
},
profil: {
fuellung: ['M12 7a3.2 3.2 0 1 0 0 6.4A3.2 3.2 0 0 0 12 7z'],
linien: ['M12 7a3.2 3.2 0 1 0 0 6.4A3.2 3.2 0 0 0 12 7z',
'M5.5 20c.7-3.3 3.3-5.3 6.5-5.3s5.8 2 6.5 5.3'],
},
startcheck: {
/* Nur der Haken ist gefüllt -- er ist die Aussage des Zeichens.
Ein gefülltes Klemmbrett wäre ein Kasten. */
fuellung: ['M9.2 9.6l1.7 1.7 3.6-3.7 1 1-4.6 4.7-2.7-2.7z'],
linien: ['M6 4.5h12v15H6z', 'M9.2 9.6l1.7 1.7 3.6-3.7', 'M9.2 15.2h5.6'],
},
live: {
/* Der Sender in der Mitte. Die Wellen bleiben Linien -- gefüllte
Wellen sähen aus wie ein Auge. */
fuellung: ['M12 10.2a1.8 1.8 0 1 0 0 3.6 1.8 1.8 0 0 0 0-3.6z'],
linien: ['M12 10.2a1.8 1.8 0 1 0 0 3.6 1.8 1.8 0 0 0 0-3.6z',
'M8.2 8.2a5.4 5.4 0 0 0 0 7.6', 'M15.8 15.8a5.4 5.4 0 0 0 0-7.6',
'M5.4 5.4a9.4 9.4 0 0 0 0 13.2', 'M18.6 18.6a9.4 9.4 0 0 0 0-13.2'],
},
content: {
/* Die Klappe der Filmklappe. */
fuellung: ['M3.5 6h17v4h-17z'],
linien: ['M3.5 6h17v13h-17z', 'M3.5 10h17', 'M8 6l-1.4 4', 'M13 6l-1.4 4', 'M18 6l-1.4 4'],
},
technik: {
/* Die drei Griffe. Sie waren bis zum 03.09.2026 Bögen der Länge
null und wurden nie gezeichnet -- übrig blieben drei nackte
Striche, die aussahen wie ein Menü-Symbol. */
fuellung: ['M9 5.9a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2z',
'M15 10.4a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2z',
'M7.5 14.9a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2z'],
linien: ['M4 7.5h16', 'M4 12h16', 'M4 16.5h16',
'M9 5.9a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2z',
'M15 10.4a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2z',
'M7.5 14.9a1.6 1.6 0 1 0 0 3.2 1.6 1.6 0 0 0 0-3.2z'],
},
community: {
/* Der vordere Kopf. Der hintere bleibt Kontur -- dadurch entsteht
die Tiefe, um die es bei zwei Personen geht. */
fuellung: ['M9 8.6a2.7 2.7 0 1 0 0 5.4 2.7 2.7 0 0 0 0-5.4z'],
linien: ['M9 8.6a2.7 2.7 0 1 0 0 5.4 2.7 2.7 0 0 0 0-5.4z',
'M3.5 19.5c.6-2.8 2.8-4.5 5.5-4.5s4.9 1.7 5.5 4.5',
'M16 8.8a2.4 2.4 0 1 1 1.6 4.3', 'M17 15.2c2 .5 3.3 2 3.7 4.3'],
},
schutz: {
fuellung: ['M12 3.6 5.6 6.3v5.1c0 3.8 2.6 7.2 6.4 8.4 3.8-1.2 6.4-4.6 6.4-8.4V6.3z'],
linien: ['M12 3.6 5.6 6.3v5.1c0 3.8 2.6 7.2 6.4 8.4 3.8-1.2 6.4-4.6 6.4-8.4V6.3z',
'M9.4 11.8l1.9 1.9 3.4-3.6'],
},
reports: {
/* Aus drei Strichen werden drei Säulen -- ein Balkendiagramm hat
Balken, keine Striche. */
fuellung: ['M6.6 9.5h1.8v7h-1.8z', 'M11.1 5.5h1.8v11h-1.8z', 'M15.6 11.5h1.8v5h-1.8z'],
linien: ['M4 19.5h16', 'M6.6 9.5h1.8v7h-1.8z', 'M11.1 5.5h1.8v11h-1.8z',
'M15.6 11.5h1.8v5h-1.8z'],
},
teamlage: {
/* Zwei Koepfe und eine steigende Linie darunter: Es geht um
Menschen UND um ihren Verlauf. Gefuellt ist nur der vordere
Kopf -- zwei gefuellte Kreise nebeneinander lesen sich als
Brille, nicht als Team. */
fuellung: ['M8.6 4.6a2.9 2.9 0 1 0 0 5.8 2.9 2.9 0 0 0 0-5.8z'],
linien: ['M8.6 4.6a2.9 2.9 0 1 0 0 5.8 2.9 2.9 0 0 0 0-5.8z',
'M15.6 5.4a2.4 2.4 0 1 0 0 4.8 2.4 2.4 0 0 0 0-4.8z',
'M4 20v-2.2', 'M9 20v-4.6', 'M14 20v-3.2', 'M19 20v-6.4'],
},
personen: {
/* Der Bart des Schlüssels bleibt Linie, der Kopf wird Fläche. */
fuellung: ['M14.4 6.2a4 4 0 1 0 0 8 4 4 0 0 0 0-8z'],
linien: ['M14.4 6.2a4 4 0 1 0 0 8 4 4 0 0 0 0-8z', 'M11.6 12.2 4 19.8',
'M6.4 17.4l2 2', 'M8.6 15.2l2 2'],
},
scouting: {
/* Der Trichter, aber nur der obere Teil -- der Stiel bleibt offen,
sonst kippt das Zeichen optisch nach unten. */
fuellung: ['M3.5 5.5h17l-6.6 7.6h-3.8z'],
linien: ['M3.5 5.5h17l-6.6 7.6v6.4l-3.8-2.1v-4.3z'],
},
wissen: {
/* Die linke Seite gefüllt: ein aufgeschlagenes Buch hat eine
Seite im Licht und eine im Schatten. */
fuellung: ['M4 5h6.4c.9 0 1.6.7 1.6 1.6V19c0-.9-.7-1.6-1.6-1.6H4z'],
linien: ['M4 5h6.4c.9 0 1.6.7 1.6 1.6V19c0-.9-.7-1.6-1.6-1.6H4z',
'M20 5h-6.4c-.9 0-1.6.7-1.6 1.6V19c0-.9.7-1.6 1.6-1.6H20z'],
},
automation: {
fuellung: ['M13.2 3.2 6 13.4h5.2l-.8 7.4 7.6-10.6h-5.2z'],
linien: ['M13.2 3.2 6 13.4h5.2l-.8 7.4 7.6-10.6h-5.2z'],
},
agentur: {
/* Der Deckel als Flaeche -- daran erkennt man einen Koffer, so wie
man einen Kalender an seinem Kopf erkennt. */
fuellung: ['M3.5 8h17v4.6h-17z'],
linien: ['M3.5 8h17v11h-17z',
'M9 8V6.4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1V8',
'M3.5 12.6h17',
'M10.9 11.6h2.2v2h-2.2z'],
},
zahlen: {
fuellung: ['M4 16 9.5 11.5 13 14 20 6.5 20 19.5 4 19.5z'],
/* Der Linienzug selbst, die Grundlinie darunter und ein Punkt am
oberen Ende -- der letzte Messwert, das Jetzt. Genau dieselbe
Bildsprache wie die kleine Linie in jeder Zahlkarte: Wer die
Kachel gesehen hat, erkennt die Karten wieder. */
linien: ['M4 16 9.5 11.5 13 14 20 6.5', 'M3.6 19.8h16.8',
'M20 5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z'],
},
};
/* ---------- Die Bereiche -------------------------------------------------
`rollen` steuert, wem ein Bereich überhaupt erscheint -- serverseitig
wird es ohnehin noch einmal geprüft, das hier ist nur Höflichkeit.
`ton` ist der Farbplatz 1..17 -- JEDE Kachel hat ihre eigene Farbe.
Ein erster Versuch mit frei gewählten Farben ergab Paare mit einem
Abstand von 1,4: praktisch dieselbe Farbe, das sieht nach Zufall aus
statt nach Absicht. Deshalb sind Farben UND Zuordnung gerechnet, von
tools/kachel-farben.mjs. Die Überlegung dahinter: Das Auge vergleicht
nur, was nebeneinander liegt. Siebzehn Farben müssen sich also nicht
alle voneinander unterscheiden, sondern nur von ihren Nachbarn im
Raster -- und da sind es jetzt mindestens 105,9 Grad.
Wer hier Kacheln umsortiert oder eine hinzufügt, ändert damit die
Nachbarschaften. Dann gehört tools/kachel-farben.mjs neu gelaufen
(dort die Liste NACHBARN anpassen), sonst stimmt der Abstand nicht
mehr und niemand merkt es. */
const GRUPPEN = [
{
name: 'Täglich',
unter: 'Was du sowieso jeden Tag aufmachst',
bereiche: [
{ name: 'Dashboard', unter: 'Alle Creator auf einen Blick', zeichen: 'dashboard', ton: 1,
gross: true, rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'uebersicht.html', szene: 'showbuehne' },
{ name: 'Aufgaben', unter: 'Offen, in Arbeit, Review', zeichen: 'aufgaben', ton: 13,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'aufgaben.html', szene: 'garage' },
/* REIHENFOLGE: CHAT, KALENDER, CALLS (08.09.2026).
Filipe, screen31: "die reihenfolge von denen, links der chat,
mitte den kalender und rechts calls & protokolle".
Vorher stand der Chat hinter "Calls & Protokolle", mit der
Begruendung, dass beides Gespraech ist. Die neue Ordnung ist
die nach VERBINDLICHKEIT, und sie liest sich von links nach
rechts: Der Chat laeuft nebenher, der Kalender bindet an eine
Uhrzeit, das Protokoll haelt fest, was verabredet wurde.
Die Toene bleiben an ihren Kacheln (Chat 4, Kalender 8, Calls
15) -- sie kennzeichnen die Kachel, nicht ihren Platz. Wer sie
beim Umsortieren mitwandern liesse, haette am naechsten Tag
zwei Kacheln in derselben Farbe. */
{ name: 'Chat', unter: 'Nachrichten mit deinem Team', zeichen: 'chat', ton: 4,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'chat.html', szene: 'lounge' },
{ name: 'Kalender', unter: 'Termine und Calls', zeichen: 'kalender', ton: 8,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'kalender.html', szene: 'skyline' },
{ name: 'Calls & Protokolle', unter: 'Gespräche mit To-dos', zeichen: 'calls', ton: 15,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'calls.html', szene: 'lounge' },
{ name: 'Dateien', unter: 'Ablage und Freigaben', zeichen: 'dateien', ton: 19,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'dateien.html', szene: 'arena' },
],
},
{
name: 'Rund um den Creator',
unter: 'Die Betreuungsakte – Stand, Zahlen, Verlauf',
bereiche: [
/* ZWEI VERSCHIEDENE DINGE, zwei Kacheln (01.09.2026).
Vorher war beides dieselbe Seite, und das war verwirrend:
Oben stand "Profil: SpongBobSchwammKopf" und mitten darin
"MEIN PROFIL - Dogfather". Zwei Personen auf einem Bildschirm,
ohne dass klar war, welche gerade gemeint ist.
Es sind auch wirklich zwei Sachen:
MEIN STECKBRIEF gehoert MIR -- Bild, ein Satz ueber mich,
meine Kanaele. Fuehre ich selbst, hat
jeder (auch Scouts und Manager).
CREATOR-PROFILE die BETREUUNGSAKTE eines Creators --
Ziele, 90-Tage-Plan, interne Notizen.
Fuehren die Betreuer.
Ein Creator sieht nur seine eigene Akte (dort steht auch sein
Steckbrief mit drin, er hat ja nur eine Seite). Alle anderen
bekommen beide Kacheln getrennt. */
/* ZAHLEN STEHT VORN (06.09.2026).
Der Workspace organisierte bis dahin ARBEIT hervorragend und
wusste über das GESCHÄFT nichts -- keine Diamanten, keine
LIVE-Tage, keine Verweildauer. Damit hing die ganze Gruppe
hier in der Luft: Der Start-Check bewertet, ohne zu messen,
der Report fragt "was hat funktioniert?" ohne Beleg, und das
90-Tage-Ziel war ein Satz statt eines Fortschritts. Deshalb
die erste Kachel der Gruppe, nicht die letzte.
Ton 4 (Violett) ist von Hand gewählt und nicht gerechnet: Er
liegt weit von seinem Nachbarn 15 (Blaugrün) und von 8
(Orangerot) darunter. Wer diese Gruppe umsortiert, lässt
tools/kachel-farben.mjs neu laufen -- die Nachbarschaften
haben sich durch diese Kachel geändert. */
/* REIHENFOLGE: STECKBRIEF, PROFILE, ZAHLEN (Wunsch Filipe,
07.09.2026: "zuerst steckbrief, dan, profile, und dan erst
zahlen"). Das ist auch die Reihenfolge, in der man arbeitet:
erst wer jemand IST, dann was er vorhat, dann was dabei
herauskommt. Die Zahlen zuerst zu zeigen dreht das um. */
{ name: 'Mein Steckbrief', unter: 'Dein Bild und deine Kanäle', zeichen: 'steckbrief', ton: 10,
rollen: ['spicy', 'admin', 'manager', 'scout'], ziel: 'steckbrief.html', szene: 'halle' },
{ name: 'Creator-Profile', unter: 'Ziele und 90-Tage-Plan', zeichen: 'profil', ton: 20,
rollen: ['spicy', 'admin', 'manager', 'scout'], ziel: 'profil.html', szene: 'halle' },
/* KEINE eigene Steckbrief-Kachel fuer Creator, und das ist
Absicht: Ihr Steckbrief samt Profilbild steht bereits auf
`profil.html` (der Block mit `data-nur-creator="ja"`). Eine
zweite Kachel fuehrte auf dieselbe Sache unter anderem Namen
-- und wer zwei Wege zu einer Einstellung anbietet, bekommt
die Frage, welcher der richtige ist. */
{ name: 'Mein Profil', unter: 'Dein Bild, deine Ziele und Pläne', zeichen: 'profil', ton: 20,
rollen: ['creator'], ziel: 'profil.html', szene: 'halle' },
/* NUR DOGFATHER (07.09.2026, Wunsch Filipe: "nimm die kategorie
zahlen bei jedem weg, nur die rolle dogfather soll die
sehen"). Die Kachel verschwindet damit fuer Manager, Scouts
und Creator -- die SEITE bleibt fuer sie gesperrt, das regelt
weiterhin die Rechteliste in workspace.js. Eine Kachel
wegzunehmen, ohne die Seite zu schliessen, waere nur ein
versteckter Weg und keine Regel. */
{ name: 'Zahlen', unter: 'Diamanten, LIVE-Tage, Verweildauer', zeichen: 'zahlen', ton: 2,
rollen: ['admin'], ziel: 'leistung.html', szene: 'arena' },
{ name: 'Start-Check', unter: 'Erstanalyse, 16 Punkte', zeichen: 'startcheck', ton: 16,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'startcheck.html', szene: 'wald' },
{ name: 'LIVE-Analyse', unter: 'Vorher, während, danach', zeichen: 'live', ton: 3,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=live', szene: 'portal' },
{ name: 'Content-Ideen', unter: 'Idee bis Upload', zeichen: 'content', ton: 12,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'content.html', szene: 'portal' },
{ name: 'Community', unter: 'Moderation und Konflikte', zeichen: 'community', ton: 6,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=community', szene: 'lounge' },
{ name: 'Technik', unter: 'Setup, Fehler, Lösungen', zeichen: 'technik', ton: 14,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=technik', szene: 'garage' },
{ name: 'Schutz & Regeln', unter: 'Richtlinien und Vorfälle', zeichen: 'schutz', ton: 5,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=schutz', szene: 'halle' },
/* DER SECHSTE BEREICH (06.09.2026). Kernprinzip 04 des Konzepts
lautet "Die Agentur bleibt angebunden" -- und dafuer gab es
bis heute nichts. Fuenf Bereiche standen, dieser fehlte.
Steht bei den anderen fuenf und nicht unter "Team & System":
Es ist ein Bereich wie LIVE, Technik oder Schutz, nur dass
die andere Seite des Tisches die Agentur ist.
Ton 9 (Orange) -- deutlich von seinen Nachbarn 1 (Blau) und
14 (Gruen) entfernt. */
/* Untertitel gekuerzt (06.09.2026): "Kampagnen, Schulungen,
offizieller Weg" wurde auf dem Handy abgeschnitten -- gemessen
von pruef-start-ansicht. Ein Untertitel, der mitten im Wort
endet, ist schlechter als ein kuerzerer. */
{ name: 'Agentur', unter: 'Kampagnen und offizielle Wege', zeichen: 'agentur', ton: 17,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'bereich.html?b=agentur', szene: 'halle' },
{ name: 'Reports & Review', unter: 'Woche und Entscheidung', zeichen: 'reports', ton: 7,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'report.html', szene: 'showbuehne' },
],
},
/* =============================================================
RUND UM DAS TEAM (09.09.2026)
Filipe: "so eine kategorie wie ueber die creator will ich dass
nur fuer die spicy und dogfather rolle auch ueber manager und
scouts gibt ... ich will dass die spicy und dogfather rolle
einen kompletten teil haben mit daten ueber die arbeit von den
manager und scout."
WARUM SIE HIER STEHT, direkt hinter "Rund um den Creator": Es
ist dieselbe Frage, eine Ebene hoeher. Dort geht es um die
Betreuung EINES Creators, hier um die, die betreuen.
NUR ZWEI ROLLEN. Ein Manager sieht die Gruppe nicht -- eine
Uebersicht ueber die Arbeit der Kollegen gehoert der Leitung.
Die Kachel wegzunehmen reicht dafuer nicht: Die Seite selbst
ist in workspace.js auf ['spicy','admin'] gesetzt, und die
Schnittstelle antwortet allen anderen mit 404. Eine
ausgeblendete Kachel hat noch nie etwas geschuetzt. */
{
name: 'Rund um das Team',
unter: 'Manager und Scouts – Auslastung, Lücken, Wirkung',
bereiche: [
{ name: 'Team-Lage', unter: 'Wer betreut wen, wo staut sich etwas',
zeichen: 'teamlage', ton: 22,
rollen: ['spicy', 'admin'], ziel: 'team.html', szene: 'halle' },
],
},
{
name: 'Team & System',
unter: 'Was den Laden am Laufen hält',
/* Für einen Creator bleibt von dieser Gruppe genau eine Kachel übrig:
die Wissens-Bibliothek. "Team & System" wäre dann eine Überschrift,
die etwas verspricht, was gar nicht da ist -- und die klingt, als
gäbe es dahinter noch einen Bereich, den er nicht sehen darf. Er
liest stattdessen "Wissen", und das beschreibt genau, was dort
steht. Der Untertitel wechselt mit. */
nameCreator: 'Wissen',
unterCreator: 'Nachschlagen, wenn du etwas brauchst',
bereiche: [
/* NUR DogFather (02.09.2026, Wunsch: "die manager sollen diese
kategorien garnicht sehen").
Es war ohnehin ein Widerspruch: In der Rollenauswahl steht seit
jeher "Manager -- dieselben Rechte wie DogFather, AUSSER der
Personenverwaltung". Die Kachel stand trotzdem bei ihm, und die
Seite liess ihn auch hinein. 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. */
{ name: 'Personen & Zugänge', unter: 'Rollen und Codes', zeichen: 'personen', ton: 11,
/* MANAGER SEHEN DIESE KACHEL JETZT AUCH (07.09.2026).
Filipe: "die manager sehen immer noch nicht die kachel
personen & zugangscode, wo sie dan die neuen creator
hinzufuegen koennen." Die SEITE zeigt ihnen nur das
Anlegen -- Codes, Sperren, Loeschen und das Protokoll
haengen weiterhin an DogFather (siehe personen.js). */
rollen: ['spicy', 'admin', 'manager'], ziel: 'personen.html', szene: 'halle' },
{ name: 'Scout-Pipeline', unter: 'Entdeckt bis übergeben', zeichen: 'scouting', ton: 21,
rollen: ['spicy', 'admin', 'manager', 'scout'], ziel: 'scouting.html', szene: 'wald' },
{ name: 'Wissens-Bibliothek', unter: 'Anleitungen und PDFs', zeichen: 'wissen', ton: 9,
rollen: ['spicy', 'admin', 'manager', 'creator', 'scout'], ziel: 'wissen.html', szene: 'arena' },
/* Ebenfalls nur DogFather (02.09.2026). Was von allein passiert,
legt fest, was ALLEN anderen automatisch zugeschoben wird --
das gehört in dieselbe Hand wie die Rollenvergabe. */
{ name: 'Automationen', unter: 'Was von allein passiert', zeichen: 'automation', ton: 18,
rollen: ['admin'], ziel: 'automation.html', szene: 'garage' },
],
},
];
/* Ein Bereich anhand des Dateinamens finden. Die Bereichsseite
(bereich.html?b=live) trägt ihren Bereich in der Adresse -- deshalb
wird auch die Abfrage verglichen, nicht nur der Dateiname. */
function zuSeite(pfad, abfrage) {
const datei = String(pfad).split('/').pop() || 'start.html';
const gesucht = datei + (abfrage || '');
let treffer = null;
for (const g of GRUPPEN) {
for (const b of g.bereiche) {
if (b.ziel === gesucht) return b;
/* Ohne Abfrage: der erste Bereich mit passendem Dateinamen.
"Mein Profil" und "Creator-Profile" zeigen auf dieselbe Datei
und teilen sich Zeichen und Ton -- da ist beides richtig. */
if (!treffer && b.ziel.split('?')[0] === datei) treffer = b;
}
}
return treffer;
}
/* Ein SVG mit dem Zeichen eines Bereichs. Klasse frei wählbar, damit
Kachel, Hinweiszeile und Seitenkopf dieselbe Form in
unterschiedlicher Größe benutzen können. */
/* ---------- Die Zeichen (überarbeitet 03.09.2026) ------------------------
Wunsch: "ich will dass du die symbole perfektionnierst, ich will
wirklich dass jeder sich denkt woooow."
Vorher war jedes Zeichen eine flache Linie in einer Farbe. Sauber,
aber leblos -- siebzehn gleich starke Striche nebeneinander.
Jetzt besteht jedes Zeichen aus ZWEI übereinanderliegenden Lagen
derselben Form:
SCHEIN dick, weich, halbdurchsichtig -- das Licht, das die
Linie wirft. Sie liegt darunter und wird nie gelesen,
aber ohne sie sitzt das Zeichen flach auf der Fläche
statt darüber zu schweben.
LINIE die eigentliche Zeichnung, mit einem VERLAUF von oben
hell nach unten gedämpft.
Der Verlauf ist der eigentliche Kniff. Er ist EINMAL im Dokument
definiert und benutzt `currentColor` -- dadurch passt er sich jeder
der siebzehn Kachelfarben von selbst an, ohne siebzehn Verläufe.
Eine Lichtquelle oben, wie in der echten Welt: Genau das lässt eine
Linie geprägt statt gemalt wirken.
Kein zusätzliches Bild, keine Schriftart, kein Nachladen -- alles
entsteht aus den Pfaden, die ohnehin schon da waren. */
const NS = 'http://www.w3.org/2000/svg';
const VERLAUF_ID = 'zeichen-verlaeufe';
/* ---------- Die drei Verläufe ------------------------------------------
Ein Zeichen wird aus drei Lagen aufgebaut, und jede hat ihren
eigenen Verlauf. Alle drei stehen EINMAL im Dokument und arbeiten
mit currentColor -- dadurch nehmen sie den Ton jeder der siebzehn
Kacheln an, ohne dass es einundfünfzig Verläufe braucht.
flaeche die gefüllte Grundform: oben kräftig, unten fast weg.
Das ist der Körper des Gegenstands.
linie die Kontur: oben fast WEISS, unten im Farbton. Genau so
sieht Metall aus, auf das Licht von oben fällt -- und
genau das lässt ein Zeichen plastisch wirken statt
gezeichnet.
glanz ein schmaler heller Streifen quer über die obere Hälfte.
Die Spiegelung. Sie ist der Unterschied zwischen "gut
gemacht" und "das will ich anfassen".
ALLE in Benutzerkoordinaten (userSpaceOnUse) über die festen 24
Einheiten des Rasters. In Objektkoordinaten -- der Vorgabe -- würde
der Verlauf auf den Umriss JEDES einzelnen Pfades gerechnet, und
eine waagerechte Linie hat die Höhe null: Der Verlauf wäre entartet
und der Browser zeichnete den Pfad gar nicht. Genau daran sind am
03.09.2026 die Querlinie im Kalender, die Regler in Technik und die
Grundlinie der Berichte verschwunden. */
/* =====================================================================
WARUM JEDES ZEICHEN SEINE VERLAEUFE SELBST TRAEGT (07.09.2026)
Sie lagen bis heute in EINEM versteckten SVG am Ende der Seite, und
alle Zeichen verwiesen darauf. Das ist die uebliche Bauart, spart
Platz -- und sie hat hier ein Jahr lang das Gegenteil von dem
bewirkt, was danebenstand.
Die Verlaeufe arbeiten mit `currentColor`, damit jedes Zeichen die
Farbe SEINER Kachel annimmt. `currentColor` in einem Verlaufsstopp
wird aber an dem Element aufgeloest, das den STOPP enthaelt -- also
im versteckten SVG am Seitenende, nicht an der Kachel. Dort ist die
Textfarbe das helle Grau der Seite. Jedes Zeichen im ganzen Haus war
deshalb grau, egal welche Farbe seine Kachel hatte.
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 klar, wo die Farbe
wirklich herkam.
Das Tueckische: Es sah nie kaputt aus. Graue Zeichen auf dunklem
Grund wirken sauber und zurueckhaltend -- man haelt es fuer die
Gestaltung. Filipe hat es als Erster benannt: "ich will dass die
symbole viel krasser, realistischer, farbiger und spezieller sind."
Jetzt bekommt jedes Zeichen seine eigenen Verlaeufe MIT, in seinem
eigenen SVG und mit eigener Kennung. Damit steht `currentColor` dort,
wo es hingehoert -- im Zeichen selbst, das die Farbe seiner Kachel
erbt. Zwanzig Zeichen je Seite ergeben zwanzig kleine
Verlaufsbloecke; das ist nichts gegen ein Farbsystem, das nicht
wirkt. */
let zeichenZaehler = 0;
function verlaufSicherstellen(ziel, endung) {
const defs = document.createElementNS(NS, 'defs');
/* AN DER FORM AUSGERICHTET, NICHT AM RASTER (07.09.2026).
Die Verlaeufe spannten ueber das ganze 24er-Raster (y von 2 bis
22). Ein Zeichen fuellt das aber nie aus: Die Sprechblase des
Chats reicht von y 5,5 bis 20,5. Das warme Streulicht sass bei
y 22 -- also UNTER der Form, und war nie zu sehen. Bei hoher
Vergroesserung nachgemessen; von den drei Lichtern kam nur eines
an.
`objectBoundingBox` spannt den Verlauf ueber JEDES Teil einzeln:
Der Kalenderkorpus bekommt sein volles Licht, seine Fuesse
ebenfalls. Genau so verhaelt sich ein echter Aufbau -- jedes Teil
liegt im selben Licht, nicht im selben Ausschnitt. */
const bauen = (id, stops, x2 = '0', y1 = '0', y2 = '1', raum = 'objectBoundingBox') => {
const lg = document.createElementNS(NS, 'linearGradient');
lg.setAttribute('id', id + endung);
lg.setAttribute('gradientUnits', raum);
lg.setAttribute('x1', '0'); lg.setAttribute('y1', y1);
lg.setAttribute('x2', x2); lg.setAttribute('y2', y2);
for (const [pos, farbe, deck] of stops) {
const st = document.createElementNS(NS, 'stop');
st.setAttribute('offset', pos);
st.setAttribute('stop-color', farbe);
st.setAttribute('stop-opacity', deck);
lg.append(st);
}
defs.append(lg);
};
/* Die Deckfläche: oben fast weiss, unten im Farbton. Das ist die
Seite, auf die das Licht fällt. */
/* AM 07.09.2026 AUF FARBE UMGESTELLT.
Filipe: "ich will dass die symbole viel krasser, realistischer,
farbiger und spezieller sind."
Der Farbton kam die ganze Zeit richtig an -- gemessen, das Zeichen
der Dashboard-Kachel bekam sauber rgb(62,149,231). Er war nur
nicht zu SEHEN, weil die Verlaeufe fast ueberall Weiss zeigten:
Die Deckflaeche begann mit 92 % Weiss, die Kontur war bis 38 %
weiss und bei 100 % wieder. Uebrig blieb ein graues Zeichen mit
einem Hauch Farbe in der Mitte.
Weiss ist jetzt 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. */
/* =====================================================================
DREI LICHTER, DREI FARBEN (07.09.2026)
Filipe: "die symbole sollen auch 2-3 farben haben ... sollen mehr
leben haben, auch viel realistischere effekte."
Nicht drei ausgedachte Farben, sondern die drei, die ein
Gegenstand in einem echten Aufbau bekommt -- 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.
Hier ein Hauch Babyblau darin.
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 unten ist der Grund, warum ein Gegenstand im Bild
"steht" statt zu schweben.
Und die Schatten sind KUEHL: Warmes Licht, kuehle Schatten -- das
ist die aelteste Regel der Malerei und der schnellste Weg von
"Farbflaeche" zu "Koerper". Sie steckt unten in `zeichen-wand`. */
bauen('zeichen-deck', [
['0', '#eaf4ff', '0.95'],
['0.12', 'currentColor', '1'],
['0.55', 'currentColor', '0.97'],
['0.82', '#ff9c5a', '0.42'],
['1', '#ffb46a', '0.66'],
]);
/* Die Seitenwand: derselbe Ton, deutlich dunkler -- eine Wand, die
vom Licht abgewandt ist. */
/* Die Seitenwand -- kuehl und dunkel. Ein Schatten ist nicht
"dieselbe Farbe, nur dunkler": Ihm fehlt das warme Hauptlicht,
also kippt er ins Blaue. */
bauen('zeichen-wand', [
['0', 'currentColor', '0.52'],
['0.6', '#1b2b45', '0.62'],
['1', '#0d1626', '0.72'],
]);
/* Der Körper: oben satt, unten fast durchsichtig. */
bauen('zeichen-flaeche', [
['0', 'currentColor', '0.68'],
['0.45', 'currentColor', '0.38'],
['1', 'currentColor', '0.12'],
]);
/* Der Eigenschatten: eine zweite Fuellung, die von unten dunkel
hereinkriecht. Ein Koerper ist unten dunkler als oben -- ohne das
bleibt jede Flaeche eine Farbflaeche. */
bauen('zeichen-schatten', [
['0', '#000000', '0'],
['0.55', '#000000', '0'],
['1', '#000000', '0.45'],
]);
/* Die Kontur: Licht von oben. Oben fast weiss, unten im Farbton --
das ist der Trick, der die Zeichen aus der Fläche hebt. */
/* Die Kontur bekommt ZWEI Lichtquellen -- so wie ein echter
Gegenstand auf einem Tisch:
oben das Hauptlicht, fast weiss
Mitte der Eigenton des Gegenstands
unten STREULICHT, das vom Untergrund zurueckgeworfen wird
Der letzte Stopp ist der Unterschied zwischen "Zeichnung" und
"Ding". Ohne ihn laeuft jede Form nach unten ins Dunkle aus und
sieht aus wie gemalt; mit ihm bekommt sie unten eine Kante, und
das Auge liest sie als Koerper im Raum. */
bauen('zeichen-linie', [
['0', '#f2f9ff', '0.99'],
['0.1', '#bfe0ff', '0.78'],
['0.3', 'currentColor', '1'],
['0.74', 'currentColor', '1'],
['1', '#ffc389', '0.85'],
]);
/* DAS RANDLICHT. Eine kuehle Kante auf der Lichtseite -- der
schmale Streifen, in dem das Fuehrungslicht die Kante des
Gegenstands streift. Er wird als zweite Lage der Kontur
gezeichnet, ein Drittel Rasterpunkt nach oben links versetzt.
Ein Gegenstand ohne diese Kante sieht immer ein wenig flach aus,
und man kann meist nicht sagen, warum. */
bauen('zeichen-rand', [
['0', '#dff0ff', '0.85'],
['0.5', '#9fd4ff', '0.45'],
['1', '#9fd4ff', '0'],
]);
/* Die Spiegelung, diagonal von links oben nach rechts unten. */
/* DAS SPITZLICHT WIRD SCHMAL UND HART.
Vorher lief es ueber zwei Drittel der Flaeche aus -- das ist kein
Spitzlicht, sondern ein Schleier, und Schleier machen jede Farbe
blass. Ein Spitzlicht auf einer gewoelbten Flaeche ist KLEIN und
hat eine Kante; genau daran erkennt das Auge die Woelbung. */
bauen('zeichen-glanz', [
['0', '#ffffff', '0.72'],
['0.16', '#ffffff', '0.3'],
['0.3', '#ffffff', '0'],
], '18', '0', '20', 'userSpaceOnUse');
ziel.append(defs);
}
function zeichenBauen(name, klasse = 'kachel__svg') {
const svg = document.createElementNS(NS, 'svg');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('class', klasse);
/* Eine eigene Kennung je Zeichen -- zwei gleiche Kennungen in einem
Dokument sind ein Verweis ins Nichts fuer das zweite. */
const endung = '-' + (++zeichenZaehler);
verlaufSicherstellen(svg, endung);
/* Erst die Fläche, dann die Linie -- in dieser Reihenfolge liegt
die Zeichnung oben. Ein Zeichen ohne Füllung (ältere Einträge,
die noch ein reines Feld sind) bekommt einfach keine. */
const eintrag = ZEICHEN[name] || {};
const umriss = KOERPER[name] || eintrag.fuellung || [];
const linien = Array.isArray(eintrag) ? eintrag : (eintrag.linien || []);
/* ---- Die Tiefe ----------------------------------------------------
Dieselbe Silhouette, dreimal, jedes Mal um einen halben
Rasterpunkt nach rechts unten versetzt. Von hinten nach vorn wird
sie heller. Das Auge liest die drei versetzten Kanten als EINE
schräge Seitenwand -- genau so, wie man einen Quader zeichnet.
Drei Lagen sind gemessen: bei zwei wirkt es wie ein
Druckfehler, ab fünf wie ein Schlagschatten. */
const tiefen = [
['zeichen__tiefe3', 'translate(1.5 1.5)'],
['zeichen__tiefe2', 'translate(1 1)'],
['zeichen__tiefe1', 'translate(0.5 0.5)'],
];
/* WOHIN DIE VERWEISE GEHOEREN: in den Inline-Stil.
Eine Klassenregel in start.css (`.zeichen__deck { fill: url(...) }`)
wuerde einen Praesentations-Attributwert schlagen, aber nicht den
Inline-Stil. Und sie kann die Kennung nicht kennen, weil die je
Zeichen anders ist. Die Farbe des Wasserzeichens setzt weiterhin
das CSS -- dort steht ausdruecklich `stroke: var(--ton)`, und
darum bekommt es hier keinen Verweis. */
const wasserzeichen = /wasserzeichen/.test(klasse);
const verweis = (el, eigenschaft, id) => {
if (!wasserzeichen) el.style.setProperty(eigenschaft, `url(#${id}${endung})`);
};
for (const [klasse, versatz] of tiefen) {
if (!umriss.length) break;
const g = document.createElementNS(NS, 'g');
g.setAttribute('class', klasse);
g.setAttribute('transform', versatz);
if (klasse !== 'zeichen__tiefe3') verweis(g, 'fill', 'zeichen-wand');
for (const d of umriss) {
const pfad = document.createElementNS(NS, 'path');
pfad.setAttribute('d', d);
g.append(pfad);
}
svg.append(g);
}
/* ---- Die Deckfläche und was darauf liegt --------------------------- */
const lagen = [
['zeichen__deck', umriss], // die Oberseite, hell
['zeichen__glanz', umriss], // die Spiegelung darauf
/* Die Details ZWEIMAL: erst ein dunkler Saum, dann die helle
Linie darauf.
Der Grund ist, dass dieselbe Linie über ZWEI Untergründen
liegt. Ein Querstrich im Kalender liegt auf der hellen
Deckfläche, die Wellen der LIVE-Analyse dagegen frei auf der
dunklen Plakette. Eine dunkle Linie verschwindet dort, eine
helle auf dem Deck -- was immer man wählt, die Hälfte ist weg.
Dunkler Saum plus helle Linie löst beides auf einmal: Auf dem
hellen Deck sieht man den Saum und liest eine eingravierte
Rille, auf der dunklen Plakette verschwindet er und die helle
Linie trägt. Ein Zeichen, zwei Untergründe, eine Lösung. */
['zeichen__saum', linien],
['zeichen__rand', linien], // die kuehle Lichtkante darunter
['zeichen__linie', linien],
];
const verlaufZu = {
zeichen__deck: ['fill', 'zeichen-deck'],
zeichen__glanz: ['fill', 'zeichen-glanz'],
zeichen__rand: ['stroke', 'zeichen-rand'],
zeichen__linie: ['stroke', 'zeichen-linie'],
};
for (const [klasse, pfade] of lagen) {
if (!pfade.length) continue;
const g = document.createElementNS(NS, 'g');
g.setAttribute('class', klasse);
if (klasse === 'zeichen__rand') g.setAttribute('transform', 'translate(-0.34 -0.34)');
if (verlaufZu[klasse]) verweis(g, verlaufZu[klasse][0], verlaufZu[klasse][1]);
for (const d of pfade) {
const pfad = document.createElementNS(NS, 'path');
pfad.setAttribute('d', d);
g.append(pfad);
}
svg.append(g);
}
return svg;
}
/* ---------- Das Merkzeichen ----------------------------------------------
Wunsch vom 01.09.2026: "wenn die Ansprechpartner was markieren, sollen
die da so ein Zeichen/Symbol haben, so dass die sehen, da ist was --
und es soll auch oben in der Kachel angezeigt werden."
Es ist bewusst das EINZIGE Zeichen der ganzen Anwendung, das eine
eigene Form hat statt der 24×24-Linienzeichen oben: eine Raute mit
Ausrufestrich. Der Grund ist nicht Geschmack, sondern Erkennbarkeit --
alle anderen Zeichen sind Rechtecke und Kreise; eine Raute ist die
einzige Form auf dem Bildschirm, die spitz nach oben zeigt, und
genau deshalb findet das Auge sie zwischen zwanzig Kacheln sofort.
Und es ist immer DIESELBE Farbe (Bernstein), egal auf welcher Kachel
es sitzt. Ein Zeichen, das seine Farbe wechselt, muss gelesen werden;
eines, das immer gleich aussieht, wird erkannt. Rot wäre falsch --
"verbessern" ist kein Fehler, sondern ein Auftrag.
anzahl > 1 schreibt die Zahl daneben, sonst steht nur das Zeichen.
Der Satz für Vorleseprogramme wird immer gesetzt: Ein Symbol ohne
Text ist für sie nicht vorhanden. */
function merkzeichenBauen(anzahl = 0, klasse = 'merk') {
const s = document.createElement('span');
s.className = klasse;
s.setAttribute('role', 'img');
s.setAttribute('aria-label', anzahl > 0
? `${anzahl} ${anzahl === 1 ? 'Punkt ist' : 'Punkte sind'} zum Verbessern markiert`
: 'Zum Verbessern markiert');
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('class', klasse + '__svg');
/* Drei Pfade: die gefüllte Raute, der Strich, der Punkt. Getrennt,
damit die Fläche eine andere Deckkraft bekommen kann als die
Linie -- eine durchgehend volle Raute wäre auf dunklem Grund ein
Klecks. */
const teile = [
['M12 1.9 22.1 12 12 22.1 1.9 12z', 'flaeche'],
['M12 7.5v5.6', 'strich'],
['M12 16.9h.01', 'punkt'],
];
for (const [d, art] of teile) {
const p = document.createElementNS('http://www.w3.org/2000/svg', 'path');
p.setAttribute('d', d);
p.setAttribute('class', `${klasse}__${art}`);
svg.append(p);
}
s.append(svg);
if (anzahl > 1) {
const n = document.createElement('span');
n.className = klasse + '__n';
n.textContent = String(anzahl);
n.setAttribute('aria-hidden', 'true');
s.append(n);
}
return s;
}
/* ===================================================================
WER GEHÖRT ZUR LEITUNG? EINE ANTWORT, NICHT NEUN (07.09.2026)
Filipe hat gemeldet, dass die Seite „Creator-Profile" bei Spicy
Media mit „Profil konnte nicht geladen werden" abbricht. Die
Ursache war nicht diese eine Seite: In NEUN Skripten stand
dieselbe Zeile
const LEITUNG = new Set(['admin', 'manager']); // <- ohne spicy
und in keinem davon stand `spicy`. profil.js hat deshalb den
Zweig für Creator genommen und das EIGENE Profil geladen -- und
ein Spicy-Media-Zugang ist kein Creator, also 404.
Das ist die Bauart, die von selbst auseinanderläuft: Wer eine
Rolle hinzufügt, müsste an neun Stellen daran denken, und die
acht, an die er nicht gedacht hat, gehen nicht kaputt -- sie
verhalten sich nur still falsch. Deshalb steht die Antwort jetzt
hier, an EINER Stelle, in einer Datei, die jede dieser Seiten vor
ihrem eigenen Skript lädt.
`pruef-css-klassen.mjs` verlangt, dass kein Workspace-Skript sich
wieder eine eigene Leitungsliste baut. */
const LEITUNG = new Set(['spicy', 'admin', 'manager']);
/* Die Reihenfolge, in der Rollen überall erscheinen -- gleiche
Begründung, dieselbe Falle: kalender.js hatte seine eigene, ohne
Spicy Media, und sortierte sie damit ans Ende. */
const ROLLENFOLGE = ['spicy', 'admin', 'manager', 'scout', 'creator'];
/* DIE NAMEN DER ROLLEN -- ebenfalls an EINER Stelle (07.09.2026).
Sie standen als Objekt in fuenf Skripten: chat.js zweimal,
dateien.js, kalender.js zweimal. In keinem davon 'spicy'. Im Chat
stand ueber Cigdems Namen deshalb woertlich "UNDEFINED" -- Filipe
hat es im Bildschirmfoto geschickt.
Es ist derselbe Fehler wie bei der Leitungsliste heute Frueh, nur
in anderer Form: damals ein Set, hier ein Objekt. Deshalb sucht
`pruef-css-klassen.mjs` jetzt auch nach Rollen-OBJEKTEN.
Zwei Schreibweisen, weil beide gebraucht werden: die kurze fuer
Namensschilder ("Scout"), die Gruppenform fuer Ueberschriften
ueber Listen ("Scouts"). */
const ROLLEN_NAME = {
spicy: 'Spicy Media', admin: 'DogFather', manager: 'Manager',
scout: 'Scout', creator: 'Creator',
};
const ROLLEN_GRUPPE = {
spicy: 'Spicy Media', admin: 'DogFather', manager: 'Manager',
scout: 'Scouts', creator: 'Creator',
};
window.Bereiche = {
ZEICHEN, GRUPPEN, zuSeite, zeichenBauen, merkzeichenBauen,
LEITUNG, ROLLENFOLGE, ROLLEN_NAME, ROLLEN_GRUPPE,
};
})();